diff --git a/src/Authoring/WinRT.SourceGenerator/AotOptimizer.cs b/src/Authoring/WinRT.SourceGenerator/AotOptimizer.cs
index 0b473f14d..3f40f9ada 100644
--- a/src/Authoring/WinRT.SourceGenerator/AotOptimizer.cs
+++ b/src/Authoring/WinRT.SourceGenerator/AotOptimizer.cs
@@ -122,7 +122,7 @@ private static bool NeedVtableAttribute(SyntaxNode node)
{
return node is ClassDeclarationSyntax declaration &&
!declaration.Modifiers.Any(m => m.IsKind(SyntaxKind.StaticKeyword) || m.IsKind(SyntaxKind.AbstractKeyword)) &&
- declaration.Modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword)) &&
+ GeneratorHelper.IsPartial(declaration) &&
!GeneratorHelper.IsWinRTType(declaration); // Making sure it isn't an RCW we are projecting.
}
@@ -140,7 +140,7 @@ private static bool NeedCustomPropertyImplementation(SyntaxNode node)
{
return node is ClassDeclarationSyntax declaration &&
!declaration.Modifiers.Any(m => m.IsKind(SyntaxKind.StaticKeyword) || m.IsKind(SyntaxKind.AbstractKeyword)) &&
- declaration.Modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword)) &&
+ GeneratorHelper.IsPartial(declaration) &&
GeneratorHelper.HasBindableCustomPropertyAttribute(declaration);
}
diff --git a/src/Authoring/WinRT.SourceGenerator/CsWinRTDiagnosticStrings.Designer.cs b/src/Authoring/WinRT.SourceGenerator/CsWinRTDiagnosticStrings.Designer.cs
index 352361083..ddfce497f 100644
--- a/src/Authoring/WinRT.SourceGenerator/CsWinRTDiagnosticStrings.Designer.cs
+++ b/src/Authoring/WinRT.SourceGenerator/CsWinRTDiagnosticStrings.Designer.cs
@@ -177,6 +177,15 @@ internal static string ArrayParamNotMarked_Text2 {
}
}
+ ///
+ /// Looks up a localized string similar to Class '{0}' has attribute GeneratedBindableCustomProperty but it or a parent type isn't marked partial. Type and any parent types should be marked partial to allow source generation for trimming and AOT compatibility..
+ ///
+ internal static string BindableCustomPropertyClassNotMarkedPartial_Text {
+ get {
+ return ResourceManager.GetString("BindableCustomPropertyClassNotMarkedPartial_Text", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Class Constructor Rule.
///
@@ -223,7 +232,7 @@ internal static string ClassNotMarkedPartial_Brief {
}
///
- /// Looks up a localized string similar to Class '{0}' implements WinRT interfaces but isn't marked partial. Type should be marked partial for trimming and AOT compatibility if passed across the WinRT ABI..
+ /// Looks up a localized string similar to Class '{0}' implements WinRT interfaces but it or a parent type isn't marked partial. Type and any parent types should be marked partial for trimming and AOT compatibility if passed across the WinRT ABI..
///
internal static string ClassNotMarkedPartial_Text {
get {
diff --git a/src/Authoring/WinRT.SourceGenerator/CsWinRTDiagnosticStrings.resx b/src/Authoring/WinRT.SourceGenerator/CsWinRTDiagnosticStrings.resx
index 8702c22f9..6f0ffe13c 100644
--- a/src/Authoring/WinRT.SourceGenerator/CsWinRTDiagnosticStrings.resx
+++ b/src/Authoring/WinRT.SourceGenerator/CsWinRTDiagnosticStrings.resx
@@ -113,8 +113,11 @@
Class is not marked partial
- Class '{0}' implements WinRT interfaces but isn't marked partial. Type should be marked partial for trimming and AOT compatibility if passed across the WinRT ABI.
+ Class '{0}' implements WinRT interfaces but it or a parent type isn't marked partial. Type and any parent types should be marked partial for trimming and AOT compatibility if passed across the WinRT ABI.
+
+ Class '{0}' has attribute GeneratedBindableCustomProperty but it or a parent type isn't marked partial. Type and any parent types should be marked partial to allow source generation for trimming and AOT compatibility.
+
Namespace is disjoint from main (winmd) namespace
diff --git a/src/Authoring/WinRT.SourceGenerator/Helper.cs b/src/Authoring/WinRT.SourceGenerator/Helper.cs
index 45faf0b15..459de7cab 100644
--- a/src/Authoring/WinRT.SourceGenerator/Helper.cs
+++ b/src/Authoring/WinRT.SourceGenerator/Helper.cs
@@ -488,7 +488,24 @@ static bool IsArgumentTypeParameter(ITypeSymbol argument)
public static bool IsPartial(INamedTypeSymbol symbol)
{
- return symbol.DeclaringSyntaxReferences.Any(syntax => syntax.GetSyntax() is BaseTypeDeclarationSyntax declaration && declaration.Modifiers.Any(SyntaxKind.PartialKeyword));
+ bool isPartial = true;
+ for (ITypeSymbol parent = symbol; parent is not null; parent = parent.ContainingType)
+ {
+ isPartial &= parent.DeclaringSyntaxReferences.Any(
+ syntax => syntax.GetSyntax() is BaseTypeDeclarationSyntax declaration &&
+ declaration.Modifiers.Any(SyntaxKind.PartialKeyword));
+ }
+ return isPartial;
+ }
+
+ public static bool IsPartial(TypeDeclarationSyntax node)
+ {
+ bool isPartial = true;
+ for (TypeDeclarationSyntax parent = node; parent is not null; parent = parent.Parent as TypeDeclarationSyntax)
+ {
+ isPartial &= parent.Modifiers.Any(static m => m.IsKind(SyntaxKind.PartialKeyword));
+ }
+ return isPartial;
}
public static bool HasPrivateclass(ITypeSymbol symbol)
@@ -1028,7 +1045,7 @@ public static string GetAbiMarshalerType(string type, string abiType, TypeKind k
public static string EscapeTypeNameForIdentifier(string typeName)
{
- return Regex.Replace(typeName, """[(\ |:<>,\.)]""", "_");
+ return Regex.Replace(typeName, """[(\ |:<>,\.\-@)]""", "_");
}
public readonly struct MappedType
diff --git a/src/Authoring/WinRT.SourceGenerator/WinRTAotCodeFixer.cs b/src/Authoring/WinRT.SourceGenerator/WinRTAotCodeFixer.cs
index c06f6c167..2a2ad6a5c 100644
--- a/src/Authoring/WinRT.SourceGenerator/WinRTAotCodeFixer.cs
+++ b/src/Authoring/WinRT.SourceGenerator/WinRTAotCodeFixer.cs
@@ -28,7 +28,8 @@ public sealed class WinRTAotDiagnosticAnalyzer : DiagnosticAnalyzer
WinRTRules.ClassNotAotCompatibleOldProjectionWarning,
WinRTRules.ClassNotAotCompatibleOldProjectionInfo,
WinRTRules.ClassEnableUnsafeWarning,
- WinRTRules.ClassEnableUnsafeInfo);
+ WinRTRules.ClassEnableUnsafeInfo,
+ WinRTRules.ClassWithBindableCustomPropertyNotPartial);
public override ImmutableArray SupportedDiagnostics => _supportedDiagnostics;
@@ -47,7 +48,8 @@ public override void Initialize(AnalysisContext context)
bool isComponentProject = context.Options.AnalyzerConfigOptionsProvider.IsCsWinRTComponent();
var winrtTypeAttribute = context.Compilation.GetTypeByMetadataName("WinRT.WindowsRuntimeTypeAttribute");
var winrtExposedTypeAttribute = context.Compilation.GetTypeByMetadataName("WinRT.WinRTExposedTypeAttribute");
- if (winrtTypeAttribute is null || winrtExposedTypeAttribute is null)
+ var generatedBindableCustomPropertyAttribute = context.Compilation.GetTypeByMetadataName("WinRT.GeneratedBindableCustomPropertyAttribute");
+ if (winrtTypeAttribute is null || winrtExposedTypeAttribute is null || generatedBindableCustomPropertyAttribute is null)
{
return;
}
@@ -115,6 +117,13 @@ public override void Initialize(AnalysisContext context)
context.ReportDiagnostic(Diagnostic.Create(diagnosticDescriptor, namedType.Locations[0], namedType.Name, string.Join(", ", interfacesFromOldProjections)));
}
}
+
+ // Make sure classes with the GeneratedBindableCustomProperty attribute are marked partial.
+ if (GeneratorHelper.HasAttributeWithType(namedType, generatedBindableCustomPropertyAttribute) &&
+ !GeneratorHelper.IsPartial(namedType))
+ {
+ context.ReportDiagnostic(Diagnostic.Create(WinRTRules.ClassWithBindableCustomPropertyNotPartial, namedType.Locations[0], namedType.Name));
+ }
}
}, SymbolKind.NamedType);
@@ -416,13 +425,21 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
private static async Task MakeTypePartial(Document document, ClassDeclarationSyntax @class, CancellationToken token)
{
- var newClass = @class.AddModifiers(SyntaxFactory.Token(SyntaxKind.PartialKeyword));
-
var oldRoot = await document.GetSyntaxRootAsync(token).ConfigureAwait(false);
if (oldRoot is null)
return document;
- var newRoot = oldRoot.ReplaceNode(@class, newClass);
+ var newRoot = oldRoot.ReplaceNodes(@class.AncestorsAndSelf().OfType(),
+ (_, typeDeclaration) =>
+ {
+ if (!typeDeclaration.Modifiers.Any(SyntaxKind.PartialKeyword))
+ {
+ return typeDeclaration.AddModifiers(SyntaxFactory.Token(SyntaxKind.PartialKeyword));
+ }
+
+ return typeDeclaration;
+ });
+
return document.WithSyntaxRoot(newRoot);
}
diff --git a/src/Authoring/WinRT.SourceGenerator/WinRTRules.cs b/src/Authoring/WinRT.SourceGenerator/WinRTRules.cs
index db66b49b8..d244c9d8c 100644
--- a/src/Authoring/WinRT.SourceGenerator/WinRTRules.cs
+++ b/src/Authoring/WinRT.SourceGenerator/WinRTRules.cs
@@ -218,5 +218,12 @@ private static DiagnosticDescriptor MakeRule(string id, string title, string mes
CsWinRTDiagnosticStrings.EnableUnsafe_Brief,
CsWinRTDiagnosticStrings.EnableUnsafe_Text,
false);
+
+ public static DiagnosticDescriptor ClassWithBindableCustomPropertyNotPartial = MakeRule(
+ "CsWinRT1028",
+ CsWinRTDiagnosticStrings.ClassNotMarkedPartial_Brief,
+ CsWinRTDiagnosticStrings.BindableCustomPropertyClassNotMarkedPartial_Text,
+ false,
+ true);
}
}
diff --git a/src/Tests/FunctionalTests/CCW/Program.cs b/src/Tests/FunctionalTests/CCW/Program.cs
index 744c0cd68..f50015572 100644
--- a/src/Tests/FunctionalTests/CCW/Program.cs
+++ b/src/Tests/FunctionalTests/CCW/Program.cs
@@ -622,6 +622,17 @@ internal static IProperties2 GetGenericInstance()
}
}
+class TestClass3
+{
+ // Making sure it compiles if the parent class isn't partial, but the actual class is.
+#pragma warning disable CsWinRT1028 // Class is not marked partial
+ partial class NestedTestClass2 : IProperties2
+#pragma warning restore CsWinRT1028 // Class is not marked partial
+ {
+ private int _value;
+ public int ReadWriteProperty { get => _value; set => _value = value; }
+ }
+}
sealed partial class CustomCommand : ICommand
{
public event EventHandler CanExecuteChanged;
@@ -663,6 +674,28 @@ partial class LanguageDervied2 : Language
public int Derived { get; set; }
}
+// Testing code compiles when not marked partial
+[GeneratedBindableCustomProperty]
+#pragma warning disable CsWinRT1028 // Class is not marked partial
+class LanguageDervied3 : Language
+#pragma warning restore CsWinRT1028 // Class is not marked partial
+{
+ public int Derived { get; set; }
+}
+
+class ParentClass
+{
+ // Testing code compiles when not marked partial
+ [GeneratedBindableCustomProperty]
+#pragma warning disable CsWinRT1028 // Class is not marked partial
+ partial class LanguageDervied3 : Language
+#pragma warning restore CsWinRT1028 // Class is not marked partial
+ {
+ public int Derived { get; set; }
+ }
+}
+
+
[GeneratedBindableCustomPropertyAttribute]
sealed partial class Language2
{
diff --git a/src/Tests/FunctionalTests/Collections/Collections.csproj b/src/Tests/FunctionalTests/Collections/Collections.csproj
index 4d63a770f..3559f5c97 100644
--- a/src/Tests/FunctionalTests/Collections/Collections.csproj
+++ b/src/Tests/FunctionalTests/Collections/Collections.csproj
@@ -9,10 +9,10 @@
-
+
-
+
diff --git a/src/Tests/FunctionalTests/TestLibrary/TestLibrary.csproj b/src/Tests/FunctionalTests/TestLibrary/Test-Library.csproj
similarity index 100%
rename from src/Tests/FunctionalTests/TestLibrary/TestLibrary.csproj
rename to src/Tests/FunctionalTests/TestLibrary/Test-Library.csproj
diff --git a/src/cswinrt.sln b/src/cswinrt.sln
index 91412924b..6235db9b4 100644
--- a/src/cswinrt.sln
+++ b/src/cswinrt.sln
@@ -158,7 +158,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CCW", "Tests\FunctionalTest
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WinAppSDK", "Projections\WinAppSDK\WinAppSDK.csproj", "{7B803846-91AE-4B98-AC93-D3FCFB2DE5AA}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestLibrary", "Tests\FunctionalTests\TestLibrary\TestLibrary.csproj", "{335D51AC-1DCF-4487-A2BD-34CE2F17B3C4}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test-Library", "Tests\FunctionalTests\TestLibrary\Test-Library.csproj", "{335D51AC-1DCF-4487-A2BD-34CE2F17B3C4}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Windows.UI.Xaml", "Projections\Windows.UI.Xaml\Windows.UI.Xaml.csproj", "{E85F3614-79B6-4652-BDB0-64AF68874CE0}"
EndProject