Skip to content

Commit

Permalink
Make generator compatible with older version of projections (#1710)
Browse files Browse the repository at this point in the history
* Make generator compatible with older version of projections

* Increment version
  • Loading branch information
manodasanW authored Aug 7, 2024
1 parent aeaaf0c commit eebf9ae
Show file tree
Hide file tree
Showing 10 changed files with 133 additions and 41 deletions.
2 changes: 1 addition & 1 deletion build/AzurePipelineTemplates/CsWinRT-Variables.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 9 additions & 1 deletion src/Authoring/WinRT.SourceGenerator/AnalyzerReleases.Shipped.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
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.
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,3 @@
### New Rules
Rule ID | Category | Severity | Notes
--------|----------|----------|-------
CsWinRT1028 | Usage | Info | Class should be marked partial
11 changes: 11 additions & 0 deletions src/Authoring/WinRT.SourceGenerator/AotOptimizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,12 @@
<data name="ClassConstructorRule_Text" xml:space="preserve">
<value>Classes cannot have multiple constructors of the same arity in the Windows Runtime, class {0} has multiple {1}-arity constructors</value>
</data>
<data name="ClassImplementsOldProjection_Brief" xml:space="preserve">
<value>Class not trimming / AOT compatible</value>
</data>
<data name="ClassImplementsOldProjection_Text" xml:space="preserve">
<value>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.</value>
</data>
<data name="ClassNotMarkedPartial_Brief" xml:space="preserve">
<value>Class is not marked partial</value>
</data>
Expand Down
27 changes: 27 additions & 0 deletions src/Authoring/WinRT.SourceGenerator/Helper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,33 @@ private static bool IsFundamentalType(ISymbol type)
return type.ToDisplayString() == "System.Guid";
}

/// <summary>
/// Checks whether an assembly contains old projections.
/// </summary>
/// <param name="assemblySymbol">The assembly to inspect.</param>
/// <returns>Whether <paramref name="assemblySymbol"/> contains old projections.</returns>
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<string>.FromImmutableArray(ImmutableArray<string>.Empty);
}
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -195,33 +206,6 @@ public static void InitializeRcwFallback()
});
}

/// <summary>
/// Checks whether an assembly contains old projections.
/// </summary>
/// <param name="assemblySymbol">The assembly to inspect.</param>
/// <returns>Whether <paramref name="assemblySymbol"/> contains old projections.</returns>
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;
}

/// <summary>
/// Visits all named type symbols in a given assembly, except for ABI types.
/// </summary>
Expand Down
45 changes: 36 additions & 9 deletions src/Authoring/WinRT.SourceGenerator/WinRTAotCodeFixer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -18,7 +19,12 @@ namespace WinRT.SourceGenerator
[DiagnosticAnalyzer(LanguageNames.CSharp), Shared]
public sealed class WinRTAotDiagnosticAnalyzer : DiagnosticAnalyzer
{
private static ImmutableArray<DiagnosticDescriptor> _supportedDiagnostics = ImmutableArray.Create(WinRTRules.ClassNotAotCompatibleWarning, WinRTRules.ClassNotAotCompatibleInfo);
private static ImmutableArray<DiagnosticDescriptor> _supportedDiagnostics =
ImmutableArray.Create(
WinRTRules.ClassNotAotCompatibleWarning,
WinRTRules.ClassNotAotCompatibleInfo,
WinRTRules.ClassNotAotCompatibleOldProjectionWarning,
WinRTRules.ClassNotAotCompatibleOldProjectionInfo);

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => _supportedDiagnostics;

Expand Down Expand Up @@ -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<string>();
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);
Expand Down
12 changes: 12 additions & 0 deletions src/Authoring/WinRT.SourceGenerator/WinRTRules.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

0 comments on commit eebf9ae

Please sign in to comment.