From eebf9ae90934e8f4131b92a348db95b321852324 Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Wed, 7 Aug 2024 09:06:51 -0700 Subject: [PATCH] Make generator compatible with older version of projections (#1710) * Make generator compatible with older version of projections * Increment version --- .../CsWinRT-Variables.yml | 2 +- .../AnalyzerReleases.Shipped.md | 10 ++++- .../AnalyzerReleases.Unshipped.md | 1 - .../WinRT.SourceGenerator/AotOptimizer.cs | 11 +++++ .../CsWinRTDiagnosticStrings.Designer.cs | 18 ++++++++ .../CsWinRTDiagnosticStrings.resx | 6 +++ src/Authoring/WinRT.SourceGenerator/Helper.cs | 27 +++++++++++ .../RcwReflectionFallbackGenerator.cs | 42 ++++++----------- .../WinRTAotCodeFixer.cs | 45 +++++++++++++++---- .../WinRT.SourceGenerator/WinRTRules.cs | 12 +++++ 10 files changed, 133 insertions(+), 41 deletions(-) diff --git a/build/AzurePipelineTemplates/CsWinRT-Variables.yml b/build/AzurePipelineTemplates/CsWinRT-Variables.yml index 271d13a46..2eda71cc1 100644 --- a/build/AzurePipelineTemplates/CsWinRT-Variables.yml +++ b/build/AzurePipelineTemplates/CsWinRT-Variables.yml @@ -5,7 +5,7 @@ variables: - name: MinorVersion value: 1 - name: PatchVersion - value: 0 + value: 1 - name: WinRT.Runtime.AssemblyVersion value: '2.1.0.0' - name: Net5.SDK.Feed diff --git a/src/Authoring/WinRT.SourceGenerator/AnalyzerReleases.Shipped.md b/src/Authoring/WinRT.SourceGenerator/AnalyzerReleases.Shipped.md index 2c40a2ed2..a07fdeceb 100644 --- a/src/Authoring/WinRT.SourceGenerator/AnalyzerReleases.Shipped.md +++ b/src/Authoring/WinRT.SourceGenerator/AnalyzerReleases.Shipped.md @@ -32,4 +32,12 @@ CsWinRT1023 | Usage | Error | Array parameters should not be marked both `ReadOn CsWinRT1024 | Usage | Error | Array parameter marked `out` should not be declared `ReadOnlyArrayAttribute` CsWinRT1025 | Usage | Error | Array parameter should be marked either `ReadOnlyArrayAttribute` or `WriteOnlyArrayAttribute` CsWinRT1026 | Usage | Error | Non-array parameter should not be marked `ReadOnlyArrayAttribute` or `WriteOnlyArrayAttribute` -CsWinRT1027 | Usage | Error | Class incorrectly implements an interface \ No newline at end of file +CsWinRT1027 | Usage | Error | Class incorrectly implements an interface + +## Release 2.1.0 + +### New Rules +Rule ID | Category | Severity | Notes +--------|----------|----------|------- +CsWinRT1028 | Usage | Warning | Class should be marked partial +CsWinRT1029 | Usage | Warning | Class implements WinRT interfaces generated using an older version of CsWinRT. \ No newline at end of file diff --git a/src/Authoring/WinRT.SourceGenerator/AnalyzerReleases.Unshipped.md b/src/Authoring/WinRT.SourceGenerator/AnalyzerReleases.Unshipped.md index c6cba84fb..f01afd2bf 100644 --- a/src/Authoring/WinRT.SourceGenerator/AnalyzerReleases.Unshipped.md +++ b/src/Authoring/WinRT.SourceGenerator/AnalyzerReleases.Unshipped.md @@ -4,4 +4,3 @@ ### New Rules Rule ID | Category | Severity | Notes --------|----------|----------|------- -CsWinRT1028 | Usage | Info | Class should be marked partial \ No newline at end of file diff --git a/src/Authoring/WinRT.SourceGenerator/AotOptimizer.cs b/src/Authoring/WinRT.SourceGenerator/AotOptimizer.cs index 61d4e7d64..afde6b005 100644 --- a/src/Authoring/WinRT.SourceGenerator/AotOptimizer.cs +++ b/src/Authoring/WinRT.SourceGenerator/AotOptimizer.cs @@ -523,6 +523,17 @@ internal static VtableAttribute GetVtableAttributeToAdd( { if (isWinRTType(iface, mapper)) { + // If the interface projection was generated using an older CsWinRT version, + // it won't have the necessary properties to generate the AOT compatible code + // and we don't want to result in compiler errors. + // We exclude generic types as they are either defined in WinRT.Runtime or the + // Windows SDK projection, so we don't need to check them. + if (!iface.IsGenericType && + GeneratorHelper.IsOldProjectionAssembly(iface.ContainingAssembly)) + { + return default; + } + interfacesToAddToVtable.Add(ToFullyQualifiedString(iface)); AddGenericInterfaceInstantiation(iface); CheckForInterfaceToUseForRuntimeClassName(iface); diff --git a/src/Authoring/WinRT.SourceGenerator/CsWinRTDiagnosticStrings.Designer.cs b/src/Authoring/WinRT.SourceGenerator/CsWinRTDiagnosticStrings.Designer.cs index 36339bdda..88e2f4ce6 100644 --- a/src/Authoring/WinRT.SourceGenerator/CsWinRTDiagnosticStrings.Designer.cs +++ b/src/Authoring/WinRT.SourceGenerator/CsWinRTDiagnosticStrings.Designer.cs @@ -195,6 +195,24 @@ internal static string ClassConstructorRule_Text { } } + /// + /// Looks up a localized string similar to Class not trimming / AOT compatible. + /// + internal static string ClassImplementsOldProjection_Brief { + get { + return ResourceManager.GetString("ClassImplementsOldProjection_Brief", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Class '{0}' implements WinRT interface(s) {1} generated using an older version of CsWinRT. Update to a projection generated using CsWinRT 2.1.0 or later for trimming and AOT compatibility.. + /// + internal static string ClassImplementsOldProjection_Text { + get { + return ResourceManager.GetString("ClassImplementsOldProjection_Text", resourceCulture); + } + } + /// /// Looks up a localized string similar to Class is not marked partial. /// diff --git a/src/Authoring/WinRT.SourceGenerator/CsWinRTDiagnosticStrings.resx b/src/Authoring/WinRT.SourceGenerator/CsWinRTDiagnosticStrings.resx index 95b4491df..20bf34754 100644 --- a/src/Authoring/WinRT.SourceGenerator/CsWinRTDiagnosticStrings.resx +++ b/src/Authoring/WinRT.SourceGenerator/CsWinRTDiagnosticStrings.resx @@ -103,6 +103,12 @@ Classes cannot have multiple constructors of the same arity in the Windows Runtime, class {0} has multiple {1}-arity constructors + + Class not trimming / AOT compatible + + + Class '{0}' implements WinRT interface(s) {1} generated using an older version of CsWinRT. Update to a projection generated using CsWinRT 2.1.0 or later for trimming and AOT compatibility. + Class is not marked partial diff --git a/src/Authoring/WinRT.SourceGenerator/Helper.cs b/src/Authoring/WinRT.SourceGenerator/Helper.cs index 023c8735e..adcef9dc4 100644 --- a/src/Authoring/WinRT.SourceGenerator/Helper.cs +++ b/src/Authoring/WinRT.SourceGenerator/Helper.cs @@ -272,6 +272,33 @@ private static bool IsFundamentalType(ISymbol type) return type.ToDisplayString() == "System.Guid"; } + /// + /// Checks whether an assembly contains old projections. + /// + /// The assembly to inspect. + /// Whether contains old projections. + public static bool IsOldProjectionAssembly(IAssemblySymbol assemblySymbol) + { + // We only care about assemblies that have some dependent assemblies + if (assemblySymbol.Modules.First() is not { ReferencedAssemblies: { Length: > 0 } dependentAssemblies }) + { + return false; + } + + // Scan all dependent assemblies to look for CsWinRT with version < 2.0.8 + foreach (AssemblyIdentity assemblyIdentity in dependentAssemblies) + { + if (assemblyIdentity.Name == "WinRT.Runtime") + { + return assemblyIdentity.Version < new Version(2, 0, 8) && + assemblyIdentity.Version != new Version(0, 0, 0, 0); + } + } + + // This assembly is not a projection assembly + return false; + } + public static bool IsWinRTType(ISymbol type, TypeMapper mapper) { return IsWinRTType(type, null, mapper); diff --git a/src/Authoring/WinRT.SourceGenerator/RcwReflectionFallbackGenerator.cs b/src/Authoring/WinRT.SourceGenerator/RcwReflectionFallbackGenerator.cs index 728c3cd18..35e9a9033 100644 --- a/src/Authoring/WinRT.SourceGenerator/RcwReflectionFallbackGenerator.cs +++ b/src/Authoring/WinRT.SourceGenerator/RcwReflectionFallbackGenerator.cs @@ -83,7 +83,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context) } // If the assembly is not an old projections assembly, we have nothing to do - if (!IsOldProjectionAssembly(assemblySymbol)) + if (!GeneratorHelper.IsOldProjectionAssembly(assemblySymbol)) { return EquatableArray.FromImmutableArray(ImmutableArray.Empty); } @@ -130,7 +130,18 @@ public void Initialize(IncrementalGeneratorInitializationContext context) continue; } - executableTypeNames.Add(typeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)); + var typeName = typeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); + + // These types are in the existing WinUI projection, but have been moved to the Windows SDK projection. + // So if we see those, we want to ignore them. + if (typeName == "global::Windows.UI.Text.ContentLinkInfo" || + typeName == "global::Windows.UI.Text.RichEditTextDocument" || + typeName == "global::Windows.UI.Text.RichEditTextRange") + { + continue; + } + + executableTypeNames.Add(typeName); } token.ThrowIfCancellationRequested(); @@ -195,33 +206,6 @@ public static void InitializeRcwFallback() }); } - /// - /// Checks whether an assembly contains old projections. - /// - /// The assembly to inspect. - /// Whether contains old projections. - private static bool IsOldProjectionAssembly(IAssemblySymbol assemblySymbol) - { - // We only care about assemblies that have some dependent assemblies - if (assemblySymbol.Modules.First() is not { ReferencedAssemblies: { Length: > 0 } dependentAssemblies }) - { - return false; - } - - // Scan all dependent assemblies to look for CsWinRT with version < 2.0.8 - foreach (AssemblyIdentity assemblyIdentity in dependentAssemblies) - { - if (assemblyIdentity.Name == "WinRT.Runtime") - { - return assemblyIdentity.Version < new Version(2, 0, 8) && - assemblyIdentity.Version != new Version(0, 0, 0, 0); - } - } - - // This assembly is not a projection assembly - return false; - } - /// /// Visits all named type symbols in a given assembly, except for ABI types. /// diff --git a/src/Authoring/WinRT.SourceGenerator/WinRTAotCodeFixer.cs b/src/Authoring/WinRT.SourceGenerator/WinRTAotCodeFixer.cs index 9700e9589..ad07c28d8 100644 --- a/src/Authoring/WinRT.SourceGenerator/WinRTAotCodeFixer.cs +++ b/src/Authoring/WinRT.SourceGenerator/WinRTAotCodeFixer.cs @@ -8,6 +8,7 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; +using System.Collections.Generic; using System.Collections.Immutable; using System.Composition; using System.Threading; @@ -18,7 +19,12 @@ namespace WinRT.SourceGenerator [DiagnosticAnalyzer(LanguageNames.CSharp), Shared] public sealed class WinRTAotDiagnosticAnalyzer : DiagnosticAnalyzer { - private static ImmutableArray _supportedDiagnostics = ImmutableArray.Create(WinRTRules.ClassNotAotCompatibleWarning, WinRTRules.ClassNotAotCompatibleInfo); + private static ImmutableArray _supportedDiagnostics = + ImmutableArray.Create( + WinRTRules.ClassNotAotCompatibleWarning, + WinRTRules.ClassNotAotCompatibleInfo, + WinRTRules.ClassNotAotCompatibleOldProjectionWarning, + WinRTRules.ClassNotAotCompatibleOldProjectionInfo); public override ImmutableArray SupportedDiagnostics => _supportedDiagnostics; @@ -55,23 +61,44 @@ public override void Initialize(AnalysisContext context) { // Make sure this is a class that we would generate the WinRTExposedType attribute on // and that it isn't already partial. - if (!GeneratorHelper.IsPartial(namedType) && - !GeneratorHelper.IsWinRTType(namedType, winrtTypeAttribute, typeMapper, isComponentProject, context.Compilation.Assembly) && + if (!GeneratorHelper.IsWinRTType(namedType, winrtTypeAttribute, typeMapper, isComponentProject, context.Compilation.Assembly) && !GeneratorHelper.HasNonInstantiatedWinRTGeneric(namedType, typeMapper) && !GeneratorHelper.HasAttributeWithType(namedType, winrtExposedTypeAttribute)) { + var interfacesFromOldProjections = new HashSet(); + bool implementsWinRTInterfaces = false; + bool implementsCustomMappedInterfaces = false; foreach (var iface in namedType.AllInterfaces) { if (GeneratorHelper.IsWinRTType(iface, winrtTypeAttribute, typeMapper, isComponentProject, context.Compilation.Assembly)) { - // Based on the warning level, emit as a warning or as an info. - var diagnosticDescriptor = (csWinRTAotWarningLevel == 2 || - (csWinRTAotWarningLevel == 1 && !GeneratorHelper.IsCustomMappedType(iface, typeMapper))) ? - WinRTRules.ClassNotAotCompatibleWarning : WinRTRules.ClassNotAotCompatibleInfo; - context.ReportDiagnostic(Diagnostic.Create(diagnosticDescriptor, namedType.Locations[0], namedType.Name)); - return; + if (!iface.IsGenericType && + GeneratorHelper.IsOldProjectionAssembly(iface.ContainingAssembly)) + { + interfacesFromOldProjections.Add(iface.Name); + } + + bool isCustomMappedType = GeneratorHelper.IsCustomMappedType(iface, typeMapper); + implementsCustomMappedInterfaces |= isCustomMappedType; + implementsWinRTInterfaces |= !isCustomMappedType; } } + + if (!GeneratorHelper.IsPartial(namedType) && + (implementsWinRTInterfaces || implementsCustomMappedInterfaces)) + { + // Based on the warning level, emit as a warning or as an info. + var diagnosticDescriptor = (csWinRTAotWarningLevel == 2 || (csWinRTAotWarningLevel == 1 && implementsWinRTInterfaces)) ? + WinRTRules.ClassNotAotCompatibleWarning : WinRTRules.ClassNotAotCompatibleInfo; + context.ReportDiagnostic(Diagnostic.Create(diagnosticDescriptor, namedType.Locations[0], namedType.Name)); + } + + if (interfacesFromOldProjections.Count > 0) + { + var diagnosticDescriptor = csWinRTAotWarningLevel >= 1 ? + WinRTRules.ClassNotAotCompatibleOldProjectionWarning : WinRTRules.ClassNotAotCompatibleOldProjectionInfo; + context.ReportDiagnostic(Diagnostic.Create(diagnosticDescriptor, namedType.Locations[0], namedType.Name, string.Join(", ", interfacesFromOldProjections))); + } } } }, SymbolKind.NamedType); diff --git a/src/Authoring/WinRT.SourceGenerator/WinRTRules.cs b/src/Authoring/WinRT.SourceGenerator/WinRTRules.cs index c9ae07c20..6e03954cd 100644 --- a/src/Authoring/WinRT.SourceGenerator/WinRTRules.cs +++ b/src/Authoring/WinRT.SourceGenerator/WinRTRules.cs @@ -192,5 +192,17 @@ private static DiagnosticDescriptor MakeRule(string id, string title, string mes CsWinRTDiagnosticStrings.ClassNotMarkedPartial_Brief, CsWinRTDiagnosticStrings.ClassNotMarkedPartial_Text, false); + + public static DiagnosticDescriptor ClassNotAotCompatibleOldProjectionWarning = MakeRule( + "CsWinRT1029", + CsWinRTDiagnosticStrings.ClassImplementsOldProjection_Brief, + CsWinRTDiagnosticStrings.ClassImplementsOldProjection_Text, + true); + + public static DiagnosticDescriptor ClassNotAotCompatibleOldProjectionInfo = MakeRule( + "CsWinRT1029", + CsWinRTDiagnosticStrings.ClassImplementsOldProjection_Brief, + CsWinRTDiagnosticStrings.ClassImplementsOldProjection_Text, + false); } }