diff --git a/src/Authoring/WinRT.SourceGenerator2/AnalyzerReleases.Shipped.md b/src/Authoring/WinRT.SourceGenerator2/AnalyzerReleases.Shipped.md
index 9d60f534d9..8acb415788 100644
--- a/src/Authoring/WinRT.SourceGenerator2/AnalyzerReleases.Shipped.md
+++ b/src/Authoring/WinRT.SourceGenerator2/AnalyzerReleases.Shipped.md
@@ -15,4 +15,12 @@ CSWINRT2005 | WindowsRuntime.SourceGenerator | Error | Null indexer type in '[Ge
CSWINRT2006 | WindowsRuntime.SourceGenerator | Error | Property name not found for '[GeneratedCustomPropertyProvider]'
CSWINRT2007 | WindowsRuntime.SourceGenerator | Error | Indexer type not found for '[GeneratedCustomPropertyProvider]'
CSWINRT2008 | WindowsRuntime.SourceGenerator | Error | Static indexer for '[GeneratedCustomPropertyProvider]'
-CSWINRT2009 | WindowsRuntime.SourceGenerator | Warning | Cast to '[ComImport]' type not supported
\ No newline at end of file
+CSWINRT2009 | WindowsRuntime.SourceGenerator | Warning | Cast to '[ComImport]' type not supported
+CSWINRT2010 | WindowsRuntime.SourceGenerator | Warning | API contract enum type with enum cases
+CSWINRT2011 | WindowsRuntime.SourceGenerator | Warning | Invalid 'ContractVersionAttribute' target for version-only constructor
+CSWINRT2012 | WindowsRuntime.SourceGenerator | Warning | Invalid 'ContractVersionAttribute' target for contract-type constructor
+CSWINRT2013 | WindowsRuntime.SourceGenerator | Warning | Invalid 'ContractVersionAttribute' contract type argument
+CSWINRT2014 | WindowsRuntime.SourceGenerator | Warning | API contract type missing 'ContractVersionAttribute'
+CSWINRT2015 | WindowsRuntime.SourceGenerator | Info | Public authored type missing version metadata
+CSWINRT2016 | WindowsRuntime.SourceGenerator | Warning | Public authored type missing 'ContractVersionAttribute'
+CSWINRT2017 | WindowsRuntime.SourceGenerator | Warning | Public authored type mixing '[ContractVersion]' and '[Version]'
\ No newline at end of file
diff --git a/src/Authoring/WinRT.SourceGenerator2/Diagnostics/Analyzers/ApiContractTypeRequiresContractVersionAnalyzer.cs b/src/Authoring/WinRT.SourceGenerator2/Diagnostics/Analyzers/ApiContractTypeRequiresContractVersionAnalyzer.cs
new file mode 100644
index 0000000000..2858761dd2
--- /dev/null
+++ b/src/Authoring/WinRT.SourceGenerator2/Diagnostics/Analyzers/ApiContractTypeRequiresContractVersionAnalyzer.cs
@@ -0,0 +1,78 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System.Collections.Immutable;
+using System.Linq;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace WindowsRuntime.SourceGenerator.Diagnostics;
+
+///
+/// A diagnostic analyzer that validates that [ApiContract] enum types declare their contract version using [ContractVersion].
+///
+[DiagnosticAnalyzer(LanguageNames.CSharp)]
+public sealed class ApiContractTypeRequiresContractVersionAnalyzer : DiagnosticAnalyzer
+{
+ ///
+ public override ImmutableArray SupportedDiagnostics { get; } = [DiagnosticDescriptors.ApiContractTypeMissingContractVersion];
+
+ ///
+ public override void Initialize(AnalysisContext context)
+ {
+ context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
+ context.EnableConcurrentExecution();
+
+ context.RegisterCompilationStartAction(static context =>
+ {
+ // This analyzer only applies to Windows Runtime component authoring scenarios
+ if (!context.Options.AnalyzerConfigOptionsProvider.GlobalOptions.GetCsWinRTComponent())
+ {
+ return;
+ }
+
+ // Get the '[ApiContract]' symbol
+ if (context.Compilation.GetTypeByMetadataName("Windows.Foundation.Metadata.ApiContractAttribute") is not { } apiContractAttributeType)
+ {
+ return;
+ }
+
+ // Get the '[ContractVersion]' symbol
+ if (context.Compilation.GetTypeByMetadataName("Windows.Foundation.Metadata.ContractVersionAttribute") is not { } contractVersionAttributeType)
+ {
+ return;
+ }
+
+ context.RegisterSymbolAction(context =>
+ {
+ // Only enum types can be valid API contract types
+ if (context.Symbol is not INamedTypeSymbol { TypeKind: TypeKind.Enum } typeSymbol)
+ {
+ return;
+ }
+
+ // Immediately bail if the type is not an API contract type
+ if (!typeSymbol.HasAttributeWithType(apiContractAttributeType))
+ {
+ return;
+ }
+
+ // Check whether any '[ContractVersion]' attribute using a version-only constructor is applied
+ foreach (AttributeData attribute in typeSymbol.GetAttributes(contractVersionAttributeType))
+ {
+ // The version-only constructors are '(uint)' and '(string, uint)'.
+ // The contract-type constructor is '(Type, uint)', which we want to ignore here.
+ if (attribute.AttributeConstructor?.Parameters is [{ Type.SpecialType: SpecialType.System_UInt32 or SpecialType.System_String }, ..])
+ {
+ return;
+ }
+ }
+
+ context.ReportDiagnostic(Diagnostic.Create(
+ DiagnosticDescriptors.ApiContractTypeMissingContractVersion,
+ typeSymbol.Locations.FirstOrDefault(),
+ typeSymbol));
+ }, SymbolKind.NamedType);
+ });
+ }
+}
diff --git a/src/Authoring/WinRT.SourceGenerator2/Diagnostics/Analyzers/PublicTypeMixedVersioningAttributesAnalyzer.cs b/src/Authoring/WinRT.SourceGenerator2/Diagnostics/Analyzers/PublicTypeMixedVersioningAttributesAnalyzer.cs
new file mode 100644
index 0000000000..e003545728
--- /dev/null
+++ b/src/Authoring/WinRT.SourceGenerator2/Diagnostics/Analyzers/PublicTypeMixedVersioningAttributesAnalyzer.cs
@@ -0,0 +1,82 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System.Collections.Immutable;
+using System.Linq;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace WindowsRuntime.SourceGenerator.Diagnostics;
+
+///
+/// A diagnostic analyzer that reports when a public authored type has both a [ContractVersion]
+/// and a [Version] attribute applied. Public types in a Windows Runtime component should
+/// use only one of these two versioning schemes, applied consistently across the public API surface.
+///
+[DiagnosticAnalyzer(LanguageNames.CSharp)]
+public sealed class PublicTypeMixedVersioningAttributesAnalyzer : DiagnosticAnalyzer
+{
+ ///
+ public override ImmutableArray SupportedDiagnostics { get; } = [DiagnosticDescriptors.PublicTypeMixedVersioningAttributes];
+
+ ///
+ public override void Initialize(AnalysisContext context)
+ {
+ context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
+ context.EnableConcurrentExecution();
+
+ context.RegisterCompilationStartAction(static context =>
+ {
+ // This analyzer only applies to Windows Runtime component authoring scenarios
+ if (!context.Options.AnalyzerConfigOptionsProvider.GlobalOptions.GetCsWinRTComponent())
+ {
+ return;
+ }
+
+ // Get the '[ContractVersion]' symbol
+ if (context.Compilation.GetTypeByMetadataName("Windows.Foundation.Metadata.ContractVersionAttribute") is not { } contractVersionAttributeType)
+ {
+ return;
+ }
+
+ // Get the '[Version]' symbol
+ if (context.Compilation.GetTypeByMetadataName("Windows.Foundation.Metadata.VersionAttribute") is not { } versionAttributeType)
+ {
+ return;
+ }
+
+ // Get the '[ApiContract]' symbol (used to skip API contract types, which are validated by a different analyzer)
+ INamedTypeSymbol? apiContractAttributeType = context.Compilation.GetTypeByMetadataName("Windows.Foundation.Metadata.ApiContractAttribute");
+
+ context.RegisterSymbolAction(context =>
+ {
+ // Only consider top-level public types
+ if (context.Symbol is not INamedTypeSymbol { DeclaredAccessibility: Accessibility.Public, ContainingType: null } typeSymbol)
+ {
+ return;
+ }
+
+ // Skip API contract types: those use '[ContractVersion]' with the version-only constructors
+ // to declare their own contract version, which is a different scenario from this analyzer.
+ if (apiContractAttributeType is not null &&
+ typeSymbol is { TypeKind: TypeKind.Enum } &&
+ typeSymbol.HasAttributeWithType(apiContractAttributeType))
+ {
+ return;
+ }
+
+ // Only report if both '[ContractVersion]' and '[Version]' are applied to the type
+ if (!typeSymbol.HasAttributeWithType(contractVersionAttributeType) ||
+ !typeSymbol.HasAttributeWithType(versionAttributeType))
+ {
+ return;
+ }
+
+ context.ReportDiagnostic(Diagnostic.Create(
+ DiagnosticDescriptors.PublicTypeMixedVersioningAttributes,
+ typeSymbol.Locations.FirstOrDefault(),
+ typeSymbol));
+ }, SymbolKind.NamedType);
+ });
+ }
+}
diff --git a/src/Authoring/WinRT.SourceGenerator2/Diagnostics/Analyzers/PublicTypeRequiresContractVersionAnalyzer.cs b/src/Authoring/WinRT.SourceGenerator2/Diagnostics/Analyzers/PublicTypeRequiresContractVersionAnalyzer.cs
new file mode 100644
index 0000000000..12e7eb8b7b
--- /dev/null
+++ b/src/Authoring/WinRT.SourceGenerator2/Diagnostics/Analyzers/PublicTypeRequiresContractVersionAnalyzer.cs
@@ -0,0 +1,104 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Immutable;
+using System.Linq;
+using System.Threading;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace WindowsRuntime.SourceGenerator.Diagnostics;
+
+///
+/// A diagnostic analyzer that reports when a public authored type is missing a [ContractVersion] attribute,
+/// but at least one other public type in the same compilation does have one applied. This enforces a consistent
+/// versioning scheme across the public API surface of a Windows Runtime component.
+///
+[DiagnosticAnalyzer(LanguageNames.CSharp)]
+public sealed class PublicTypeRequiresContractVersionAnalyzer : DiagnosticAnalyzer
+{
+ ///
+ public override ImmutableArray SupportedDiagnostics { get; } = [DiagnosticDescriptors.PublicTypeMissingContractVersion];
+
+ ///
+ public override void Initialize(AnalysisContext context)
+ {
+ context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
+ context.EnableConcurrentExecution();
+
+ context.RegisterCompilationStartAction(static context =>
+ {
+ // This analyzer only applies to Windows Runtime component authoring scenarios
+ if (!context.Options.AnalyzerConfigOptionsProvider.GlobalOptions.GetCsWinRTComponent())
+ {
+ return;
+ }
+
+ // Get the '[ContractVersion]' symbol
+ if (context.Compilation.GetTypeByMetadataName("Windows.Foundation.Metadata.ContractVersionAttribute") is not { } contractVersionAttributeType)
+ {
+ return;
+ }
+
+ // Get the '[ApiContract]' symbol (used to skip API contract types, which use '[ContractVersion]'
+ // with the version-only constructors to declare their own contract version, not as an association).
+ INamedTypeSymbol? apiContractAttributeType = context.Compilation.GetTypeByMetadataName("Windows.Foundation.Metadata.ApiContractAttribute");
+
+ // Shared state across symbol actions: collect public types missing '[ContractVersion]' and track
+ // whether at least one public type in the compilation does have a '[ContractVersion]' applied.
+ ConcurrentBag typesMissingContractVersion = [];
+ int anyTypeHasContractVersion = 0;
+
+ context.RegisterSymbolAction(context =>
+ {
+ // Only consider top-level public types
+ if (context.Symbol is not INamedTypeSymbol { DeclaredAccessibility: Accessibility.Public, ContainingType: null } typeSymbol)
+ {
+ return;
+ }
+
+ // Skip API contract types: those use '[ContractVersion]' with a different semantics
+ // (declaring their own contract version, not associating with another contract).
+ if (apiContractAttributeType is not null &&
+ typeSymbol is { TypeKind: TypeKind.Enum } &&
+ typeSymbol.HasAttributeWithType(apiContractAttributeType))
+ {
+ return;
+ }
+
+ if (typeSymbol.HasAttributeWithType(contractVersionAttributeType))
+ {
+ _ = Interlocked.Exchange(ref anyTypeHasContractVersion, 1);
+ }
+ else
+ {
+ typesMissingContractVersion.Add(typeSymbol);
+ }
+ }, SymbolKind.NamedType);
+
+ context.RegisterCompilationEndAction(context =>
+ {
+ // Only report if at least one public type does have a '[ContractVersion]' applied: the
+ // analyzer specifically targets components that have opted into contract versioning, but
+ // are inconsistently applying it across the public API surface.
+ if (Volatile.Read(ref anyTypeHasContractVersion) == 0)
+ {
+ return;
+ }
+
+ // Sort by source location for deterministic diagnostic reporting order
+ foreach (INamedTypeSymbol typeSymbol in typesMissingContractVersion
+ .OrderBy(static t => t.Locations.FirstOrDefault()?.SourceTree?.FilePath, StringComparer.Ordinal)
+ .ThenBy(static t => t.Locations.FirstOrDefault()?.SourceSpan.Start ?? 0))
+ {
+ context.ReportDiagnostic(Diagnostic.Create(
+ DiagnosticDescriptors.PublicTypeMissingContractVersion,
+ typeSymbol.Locations.FirstOrDefault(),
+ typeSymbol));
+ }
+ });
+ });
+ }
+}
diff --git a/src/Authoring/WinRT.SourceGenerator2/Diagnostics/Analyzers/PublicTypeRequiresVersioningAnalyzer.cs b/src/Authoring/WinRT.SourceGenerator2/Diagnostics/Analyzers/PublicTypeRequiresVersioningAnalyzer.cs
new file mode 100644
index 0000000000..a4aa04c2ab
--- /dev/null
+++ b/src/Authoring/WinRT.SourceGenerator2/Diagnostics/Analyzers/PublicTypeRequiresVersioningAnalyzer.cs
@@ -0,0 +1,85 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System.Collections.Immutable;
+using System.Linq;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace WindowsRuntime.SourceGenerator.Diagnostics;
+
+///
+/// A diagnostic analyzer that reports when a public authored type is missing version metadata
+/// (i.e. is missing both a [ContractVersion] and a [Version] attribute).
+///
+[DiagnosticAnalyzer(LanguageNames.CSharp)]
+public sealed class PublicTypeRequiresVersioningAnalyzer : DiagnosticAnalyzer
+{
+ ///
+ public override ImmutableArray SupportedDiagnostics { get; } = [DiagnosticDescriptors.PublicTypeMissingVersioning];
+
+ ///
+ public override void Initialize(AnalysisContext context)
+ {
+ context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
+ context.EnableConcurrentExecution();
+
+ context.RegisterCompilationStartAction(static context =>
+ {
+ // This analyzer only applies to Windows Runtime component authoring scenarios
+ if (!context.Options.AnalyzerConfigOptionsProvider.GlobalOptions.GetCsWinRTComponent())
+ {
+ return;
+ }
+
+ // Get the '[ContractVersion]' symbol
+ if (context.Compilation.GetTypeByMetadataName("Windows.Foundation.Metadata.ContractVersionAttribute") is not { } contractVersionAttributeType)
+ {
+ return;
+ }
+
+ // Get the '[Version]' symbol (used to also accept '[Version]' as valid version metadata,
+ // as an alternative to '[ContractVersion]' for declaring the version of a public type).
+ INamedTypeSymbol? versionAttributeType = context.Compilation.GetTypeByMetadataName("Windows.Foundation.Metadata.VersionAttribute");
+
+ // Get the '[ApiContract]' symbol (used to skip API contract types, which are validated by a different analyzer)
+ INamedTypeSymbol? apiContractAttributeType = context.Compilation.GetTypeByMetadataName("Windows.Foundation.Metadata.ApiContractAttribute");
+
+ context.RegisterSymbolAction(context =>
+ {
+ // Only consider top-level public types
+ if (context.Symbol is not INamedTypeSymbol { DeclaredAccessibility: Accessibility.Public, ContainingType: null } typeSymbol)
+ {
+ return;
+ }
+
+ // Skip API contract types: those are handled by 'ApiContractTypeRequiresContractVersionAnalyzer'
+ if (apiContractAttributeType is not null &&
+ typeSymbol is { TypeKind: TypeKind.Enum } &&
+ typeSymbol.HasAttributeWithType(apiContractAttributeType))
+ {
+ return;
+ }
+
+ // Skip if any '[ContractVersion]' attribute is already applied (validity of the
+ // specific constructor used is reported by the other 'ContractVersion' analyzers).
+ if (typeSymbol.HasAttributeWithType(contractVersionAttributeType))
+ {
+ return;
+ }
+
+ // Skip if any '[Version]' attribute is applied: '[Version]' is an alternative versioning
+ // metadata that also satisfies the requirement of declaring a version for the type.
+ if (versionAttributeType is not null && typeSymbol.HasAttributeWithType(versionAttributeType))
+ {
+ return;
+ }
+
+ context.ReportDiagnostic(Diagnostic.Create(
+ DiagnosticDescriptors.PublicTypeMissingVersioning,
+ typeSymbol.Locations.FirstOrDefault(),
+ typeSymbol));
+ }, SymbolKind.NamedType);
+ });
+ }
+}
diff --git a/src/Authoring/WinRT.SourceGenerator2/Diagnostics/Analyzers/ValidApiContractEnumTypeAnalyzer.cs b/src/Authoring/WinRT.SourceGenerator2/Diagnostics/Analyzers/ValidApiContractEnumTypeAnalyzer.cs
new file mode 100644
index 0000000000..dfb499a28e
--- /dev/null
+++ b/src/Authoring/WinRT.SourceGenerator2/Diagnostics/Analyzers/ValidApiContractEnumTypeAnalyzer.cs
@@ -0,0 +1,65 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System.Collections.Immutable;
+using System.Linq;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace WindowsRuntime.SourceGenerator.Diagnostics;
+
+///
+/// A diagnostic analyzer that validates that [ApiContract] enum types do not define any enum cases.
+///
+[DiagnosticAnalyzer(LanguageNames.CSharp)]
+public sealed class ValidApiContractEnumTypeAnalyzer : DiagnosticAnalyzer
+{
+ ///
+ public override ImmutableArray SupportedDiagnostics { get; } = [DiagnosticDescriptors.ApiContractEnumWithCases];
+
+ ///
+ public override void Initialize(AnalysisContext context)
+ {
+ context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
+ context.EnableConcurrentExecution();
+
+ context.RegisterCompilationStartAction(static context =>
+ {
+ // This analyzer only applies to Windows Runtime component authoring scenarios
+ if (!context.Options.AnalyzerConfigOptionsProvider.GlobalOptions.GetCsWinRTComponent())
+ {
+ return;
+ }
+
+ // Get the '[ApiContract]' symbol
+ if (context.Compilation.GetTypeByMetadataName("Windows.Foundation.Metadata.ApiContractAttribute") is not { } attributeType)
+ {
+ return;
+ }
+
+ context.RegisterSymbolAction(context =>
+ {
+ // Only enum types can be annotated with '[ApiContract]'
+ if (context.Symbol is not INamedTypeSymbol { TypeKind: TypeKind.Enum } typeSymbol)
+ {
+ return;
+ }
+
+ // Immediately bail if the type doesn't have the attribute
+ if (!typeSymbol.HasAttributeWithType(attributeType))
+ {
+ return;
+ }
+
+ // Warn if the enum defines any enum cases (i.e. any fields other than the implicit value field)
+ if (typeSymbol.GetMembers().Any(static member => member is IFieldSymbol { IsConst: true }))
+ {
+ context.ReportDiagnostic(Diagnostic.Create(
+ DiagnosticDescriptors.ApiContractEnumWithCases,
+ typeSymbol.Locations.FirstOrDefault(),
+ typeSymbol));
+ }
+ }, SymbolKind.NamedType);
+ });
+ }
+}
diff --git a/src/Authoring/WinRT.SourceGenerator2/Diagnostics/Analyzers/ValidContractVersionAttributeAnalyzer.cs b/src/Authoring/WinRT.SourceGenerator2/Diagnostics/Analyzers/ValidContractVersionAttributeAnalyzer.cs
new file mode 100644
index 0000000000..a7ebc27429
--- /dev/null
+++ b/src/Authoring/WinRT.SourceGenerator2/Diagnostics/Analyzers/ValidContractVersionAttributeAnalyzer.cs
@@ -0,0 +1,126 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System.Collections.Immutable;
+using System.Linq;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace WindowsRuntime.SourceGenerator.Diagnostics;
+
+///
+/// A diagnostic analyzer that validates applications of [ContractVersion] attributes.
+///
+[DiagnosticAnalyzer(LanguageNames.CSharp)]
+public sealed class ValidContractVersionAttributeAnalyzer : DiagnosticAnalyzer
+{
+ ///
+ public override ImmutableArray SupportedDiagnostics { get; } = [
+ DiagnosticDescriptors.ContractVersionAttributeRequiresApiContractTarget,
+ DiagnosticDescriptors.ContractVersionAttributeNotAllowedOnApiContractTarget,
+ DiagnosticDescriptors.ContractVersionAttributeInvalidContractTypeArgument];
+
+ ///
+ public override void Initialize(AnalysisContext context)
+ {
+ context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
+ context.EnableConcurrentExecution();
+
+ context.RegisterCompilationStartAction(static context =>
+ {
+ // This analyzer only applies to Windows Runtime component authoring scenarios
+ if (!context.Options.AnalyzerConfigOptionsProvider.GlobalOptions.GetCsWinRTComponent())
+ {
+ return;
+ }
+
+ // Get the '[ContractVersion]' symbol
+ if (context.Compilation.GetTypeByMetadataName("Windows.Foundation.Metadata.ContractVersionAttribute") is not { } contractVersionAttributeType)
+ {
+ return;
+ }
+
+ // Get the '[ApiContract]' symbol
+ if (context.Compilation.GetTypeByMetadataName("Windows.Foundation.Metadata.ApiContractAttribute") is not { } apiContractAttributeType)
+ {
+ return;
+ }
+
+ context.RegisterSymbolAction(context =>
+ {
+ if (context.Symbol is not INamedTypeSymbol typeSymbol)
+ {
+ return;
+ }
+
+ bool isApiContractType = IsApiContractType(typeSymbol, apiContractAttributeType);
+
+ foreach (AttributeData attribute in typeSymbol.GetAttributes(contractVersionAttributeType))
+ {
+ // We can have multiple '[ContractVersion]' uses, so we need to iterate and check each of them
+ if (attribute.AttributeConstructor is not { } constructor)
+ {
+ continue;
+ }
+
+ ImmutableArray parameters = constructor.Parameters;
+
+ // Identify the constructor by its first parameter type:
+ // - 'ContractVersionAttribute(uint)'
+ // - 'ContractVersionAttribute(string, uint)'
+ // - 'ContractVersionAttribute(Type, uint)'
+ bool isVersionOnlyConstructor = parameters is
+ [{ Type.SpecialType: SpecialType.System_UInt32 }] or
+ [{ Type.SpecialType: SpecialType.System_String }, _];
+
+ bool isContractTypeConstructor = parameters is
+ [{ Type: INamedTypeSymbol { MetadataName: "Type", ContainingNamespace.Name: "System" } }, _];
+
+ // The version-only constructors must be applied to API contract types
+ if (isVersionOnlyConstructor && !isApiContractType)
+ {
+ context.ReportDiagnostic(Diagnostic.Create(
+ DiagnosticDescriptors.ContractVersionAttributeRequiresApiContractTarget,
+ attribute.GetLocation(context.CancellationToken) ?? typeSymbol.Locations.FirstOrDefault(),
+ typeSymbol));
+ }
+ else if (isContractTypeConstructor)
+ {
+ // The contract-type constructor must not be applied to API contract types
+ if (isApiContractType)
+ {
+ context.ReportDiagnostic(Diagnostic.Create(
+ DiagnosticDescriptors.ContractVersionAttributeNotAllowedOnApiContractTarget,
+ attribute.GetLocation(context.CancellationToken) ?? typeSymbol.Locations.FirstOrDefault(),
+ typeSymbol));
+ }
+
+ // The contract type argument must be a valid API contract type
+ if (attribute.ConstructorArguments is [{ Value: INamedTypeSymbol contractTypeArgument }, ..] &&
+ !IsApiContractType(contractTypeArgument, apiContractAttributeType))
+ {
+ context.ReportDiagnostic(Diagnostic.Create(
+ DiagnosticDescriptors.ContractVersionAttributeInvalidContractTypeArgument,
+ attribute.GetArgumentLocation(argumentIndex: 0, context.CancellationToken)
+ ?? attribute.GetLocation(context.CancellationToken)
+ ?? typeSymbol.Locations.FirstOrDefault(),
+ typeSymbol,
+ contractTypeArgument));
+ }
+ }
+ }
+ }, SymbolKind.NamedType);
+ });
+ }
+
+ ///
+ /// Checks whether a type is a valid API contract type (an enum type annotated with [ApiContract]).
+ ///
+ /// The type symbol to check.
+ /// The [ApiContract] attribute symbol.
+ /// Whether is a valid API contract type.
+ private static bool IsApiContractType(INamedTypeSymbol typeSymbol, INamedTypeSymbol apiContractAttributeType)
+ {
+ return typeSymbol is { TypeKind: TypeKind.Enum } && typeSymbol.HasAttributeWithType(apiContractAttributeType);
+ }
+}
diff --git a/src/Authoring/WinRT.SourceGenerator2/Diagnostics/DiagnosticDescriptors.cs b/src/Authoring/WinRT.SourceGenerator2/Diagnostics/DiagnosticDescriptors.cs
index 112d8156c8..65b19e356e 100644
--- a/src/Authoring/WinRT.SourceGenerator2/Diagnostics/DiagnosticDescriptors.cs
+++ b/src/Authoring/WinRT.SourceGenerator2/Diagnostics/DiagnosticDescriptors.cs
@@ -139,4 +139,109 @@ internal static partial class DiagnosticDescriptors
isEnabledByDefault: true,
description: "Types used in cast operations must not be '[ComImport]' interfaces, as they are not compatible with Windows Runtime objects marshalled by CsWinRT.",
helpLinkUri: "https://github.com/microsoft/CsWinRT");
+
+ ///
+ /// Gets a for an [ApiContract] enum type that defines enum cases.
+ ///
+ public static readonly DiagnosticDescriptor ApiContractEnumWithCases = new(
+ id: "CSWINRT2010",
+ title: "API contract enum type with enum cases",
+ messageFormat: """The type '{0}' is annotated with '[ApiContract]', but it defines one or more enum cases. API contract types are represented by empty struct types in the Windows Runtime type system, and as such defining any enum cases is invalid. The enum cases will be ignored when generating the resulting .winmd file.""",
+ category: "WindowsRuntime.SourceGenerator",
+ defaultSeverity: DiagnosticSeverity.Warning,
+ isEnabledByDefault: true,
+ description: "Enum types annotated with '[ApiContract]' must not define any enum cases, as API contract types are represented by empty struct types in the Windows Runtime type system. Any enum cases will be ignored when generating the resulting .winmd file.",
+ helpLinkUri: "https://github.com/microsoft/CsWinRT");
+
+ ///
+ /// Gets a for a [ContractVersion] attribute using the version-only constructors on a non-API contract type.
+ ///
+ public static readonly DiagnosticDescriptor ContractVersionAttributeRequiresApiContractTarget = new(
+ id: "CSWINRT2011",
+ title: "Invalid 'ContractVersionAttribute' target for version-only constructor",
+ messageFormat: """The type '{0}' is annotated with '[ContractVersion]' using a constructor that only specifies the contract version, but '{0}' is not an API contract type (an enum type annotated with '[ApiContract]'). These constructors only apply to API contract types and are used to specify the contract version of that API contract.""",
+ category: "WindowsRuntime.SourceGenerator",
+ defaultSeverity: DiagnosticSeverity.Warning,
+ isEnabledByDefault: true,
+ description: "The 'ContractVersionAttribute' constructors taking only the contract version (or the contract name and version) only apply to API contract types (enum types annotated with '[ApiContract]'), and are used to specify the contract version of that API contract.",
+ helpLinkUri: "https://github.com/microsoft/CsWinRT");
+
+ ///
+ /// Gets a for a [ContractVersion] attribute using the contract-type constructor on an API contract type.
+ ///
+ public static readonly DiagnosticDescriptor ContractVersionAttributeNotAllowedOnApiContractTarget = new(
+ id: "CSWINRT2012",
+ title: "Invalid 'ContractVersionAttribute' target for contract-type constructor",
+ messageFormat: """The type '{0}' is annotated with '[ContractVersion]' using the constructor that takes a contract type and version, but '{0}' is itself an API contract type. This constructor is used to associate a non-contract type with an API contract; use the constructor that only takes the contract version instead.""",
+ category: "WindowsRuntime.SourceGenerator",
+ defaultSeverity: DiagnosticSeverity.Warning,
+ isEnabledByDefault: true,
+ description: "The 'ContractVersionAttribute' constructor taking a contract type and version cannot be applied to API contract types, as it is meant to associate a non-contract type with an API contract.",
+ helpLinkUri: "https://github.com/microsoft/CsWinRT");
+
+ ///
+ /// Gets a for a [ContractVersion] attribute whose contract type argument is not a valid API contract type.
+ ///
+ public static readonly DiagnosticDescriptor ContractVersionAttributeInvalidContractTypeArgument = new(
+ id: "CSWINRT2013",
+ title: "Invalid 'ContractVersionAttribute' contract type argument",
+ messageFormat: """The 'ContractVersionAttribute' applied to '{0}' specifies '{1}' as the contract type, but '{1}' is not a valid API contract type (an enum type annotated with '[ApiContract]')""",
+ category: "WindowsRuntime.SourceGenerator",
+ defaultSeverity: DiagnosticSeverity.Warning,
+ isEnabledByDefault: true,
+ description: "The contract type argument of '[ContractVersion]' must be a valid API contract type (an enum type annotated with '[ApiContract]').",
+ helpLinkUri: "https://github.com/microsoft/CsWinRT");
+
+ ///
+ /// Gets a for an [ApiContract] enum type that is missing a [ContractVersion] attribute.
+ ///
+ public static readonly DiagnosticDescriptor ApiContractTypeMissingContractVersion = new(
+ id: "CSWINRT2014",
+ title: "API contract type missing 'ContractVersionAttribute'",
+ messageFormat: """The type '{0}' is annotated with '[ApiContract]', but it does not have a '[ContractVersion]' attribute applied to it. API contract types must declare their contract version using one of the version-only constructors of '[ContractVersion]'.""",
+ category: "WindowsRuntime.SourceGenerator",
+ defaultSeverity: DiagnosticSeverity.Warning,
+ isEnabledByDefault: true,
+ description: "Enum types annotated with '[ApiContract]' must also have a '[ContractVersion]' attribute applied to them, using one of the version-only constructors, to declare the contract version of the API contract.",
+ helpLinkUri: "https://github.com/microsoft/CsWinRT");
+
+ ///
+ /// Gets a for a public authored type missing version metadata (either [ContractVersion] or [Version]).
+ ///
+ public static readonly DiagnosticDescriptor PublicTypeMissingVersioning = new(
+ id: "CSWINRT2015",
+ title: "Public authored type missing version metadata",
+ messageFormat: """The type '{0}' is publicly exposed in a Windows Runtime component, but it does not have either a '[ContractVersion]' or a '[Version]' attribute applied to it. Public types should declare their associated API contract version using '[ContractVersion(typeof(SomeContract), version)]', or specify a Windows Runtime version using '[Version(version)]', so that consumers can target a specific version.""",
+ category: "WindowsRuntime.SourceGenerator",
+ defaultSeverity: DiagnosticSeverity.Info,
+ isEnabledByDefault: true,
+ description: "Public types in a Windows Runtime component should declare their version using either '[ContractVersion(typeof(SomeContract), version)]' (to associate with an API contract) or '[Version(version)]' (to specify a Windows Runtime version), so that consumers can target a specific version.",
+ helpLinkUri: "https://github.com/microsoft/CsWinRT");
+
+ ///
+ /// Gets a for a public authored type missing a [ContractVersion] attribute when at least one other public type in the same component has one applied.
+ ///
+ public static readonly DiagnosticDescriptor PublicTypeMissingContractVersion = new(
+ id: "CSWINRT2016",
+ title: "Public authored type missing 'ContractVersionAttribute'",
+ messageFormat: """The type '{0}' is publicly exposed in a Windows Runtime component that uses '[ContractVersion]' on at least one other public type, but '{0}' itself does not have a '[ContractVersion]' attribute applied. Public types in a component using contract versioning should consistently declare their associated API contract using '[ContractVersion(typeof(SomeContract), version)]'.""",
+ category: "WindowsRuntime.SourceGenerator",
+ defaultSeverity: DiagnosticSeverity.Warning,
+ isEnabledByDefault: true,
+ description: "Public types in a Windows Runtime component should use a consistent versioning scheme. If at least one public type uses '[ContractVersion]', all other public types should also use '[ContractVersion]' to declare their associated API contract.",
+ helpLinkUri: "https://github.com/microsoft/CsWinRT",
+ customTags: WellKnownDiagnosticTags.CompilationEnd);
+
+ ///
+ /// Gets a for a public authored type that has both a [ContractVersion] and a [Version] attribute applied.
+ ///
+ public static readonly DiagnosticDescriptor PublicTypeMixedVersioningAttributes = new(
+ id: "CSWINRT2017",
+ title: "Public authored type mixing '[ContractVersion]' and '[Version]'",
+ messageFormat: """The type '{0}' is publicly exposed in a Windows Runtime component and has both a '[ContractVersion]' and a '[Version]' attribute applied. Public types in a component should not mix two different versioning schemes; pick one of '[ContractVersion]' (to associate with an API contract) or '[Version]' (to specify a Windows Runtime version), and apply it consistently.""",
+ category: "WindowsRuntime.SourceGenerator",
+ defaultSeverity: DiagnosticSeverity.Warning,
+ isEnabledByDefault: true,
+ description: "Public types in a Windows Runtime component should not mix '[ContractVersion]' and '[Version]', as these represent two different versioning schemes. Pick one and apply it consistently across the public API surface of the component.",
+ helpLinkUri: "https://github.com/microsoft/CsWinRT");
}
\ No newline at end of file
diff --git a/src/Authoring/WinRT.SourceGenerator2/Extensions/AttributeDataExtensions.cs b/src/Authoring/WinRT.SourceGenerator2/Extensions/AttributeDataExtensions.cs
new file mode 100644
index 0000000000..2697f70d32
--- /dev/null
+++ b/src/Authoring/WinRT.SourceGenerator2/Extensions/AttributeDataExtensions.cs
@@ -0,0 +1,43 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System.Threading;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+#pragma warning disable CS1734
+
+namespace WindowsRuntime.SourceGenerator;
+
+///
+/// Extensions for .
+///
+internal static class AttributeDataExtensions
+{
+ /// The input instance.
+ extension(AttributeData attribute)
+ {
+ ///
+ /// Gets the location of the syntax node where the attribute is applied.
+ ///
+ /// The cancellation token to use.
+ /// The location of the attribute application, or if it cannot be determined.
+ public Location? GetLocation(CancellationToken cancellationToken)
+ {
+ return attribute.ApplicationSyntaxReference?.GetSyntax(cancellationToken).GetLocation();
+ }
+
+ ///
+ /// Gets the location of a specific positional argument of the attribute application.
+ ///
+ /// The index of the positional argument.
+ /// The cancellation token to use.
+ /// The location of the argument, or if it cannot be determined.
+ public Location? GetArgumentLocation(int argumentIndex, CancellationToken cancellationToken)
+ {
+ return attribute.ApplicationSyntaxReference?.GetSyntax(cancellationToken) is AttributeSyntax { ArgumentList.Arguments: { } arguments } && argumentIndex < arguments.Count
+ ? arguments[argumentIndex].GetLocation()
+ : null;
+ }
+ }
+}
diff --git a/src/Authoring/WinRT.SourceGenerator2/Extensions/ISymbolExtensions.cs b/src/Authoring/WinRT.SourceGenerator2/Extensions/ISymbolExtensions.cs
index 59441b65cb..3bc2402c79 100644
--- a/src/Authoring/WinRT.SourceGenerator2/Extensions/ISymbolExtensions.cs
+++ b/src/Authoring/WinRT.SourceGenerator2/Extensions/ISymbolExtensions.cs
@@ -68,6 +68,22 @@ public bool TryGetAttributeWithType(ITypeSymbol typeSymbol, [NotNullWhen(true)]
return false;
}
+ ///
+ /// Gets all attributes applied to a symbol with a specified type.
+ ///
+ /// The instance for the attribute type to look for.
+ /// The sequence of attributes applied to with the specified type.
+ public IEnumerable GetAttributes(INamedTypeSymbol typeSymbol)
+ {
+ foreach (AttributeData attribute in symbol.GetAttributes())
+ {
+ if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, typeSymbol))
+ {
+ yield return attribute;
+ }
+ }
+ }
+
///
/// Checks whether a given symbol is accessible from the assembly of a given compilation (including eg. through nested types).
///
diff --git a/src/Tests/AuthoringTest/AuthoringTest.csproj b/src/Tests/AuthoringTest/AuthoringTest.csproj
index 72ed2eacc1..24afdfea4b 100644
--- a/src/Tests/AuthoringTest/AuthoringTest.csproj
+++ b/src/Tests/AuthoringTest/AuthoringTest.csproj
@@ -30,6 +30,14 @@
location it is assigned to."
-->
$(WarningsNotAsErrors);CS0067;CS0282;IL2087
+
+
+ $(NoWarn);CSWINRT2016
diff --git a/src/Tests/AuthoringTest/Program.cs b/src/Tests/AuthoringTest/Program.cs
index 68026d883c..9eeb3111c7 100644
--- a/src/Tests/AuthoringTest/Program.cs
+++ b/src/Tests/AuthoringTest/Program.cs
@@ -2285,6 +2285,7 @@ public void RaiseDataChanged()
// Contract versioning
[Windows.Foundation.Metadata.ApiContract]
+ [Windows.Foundation.Metadata.ContractVersion(1u)]
public enum AnotherNamespaceContract { }
[Windows.Foundation.Metadata.ContractVersion(typeof(AnotherNamespaceContract), 1u)]
@@ -2364,4 +2365,4 @@ public void RaiseUrgencyChanged()
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Tests/SourceGenerator2Test/Helpers/CSharpAnalyzerTest{TAnalyzer}.cs b/src/Tests/SourceGenerator2Test/Helpers/CSharpAnalyzerTest{TAnalyzer}.cs
index d1976d465e..ad67904e34 100644
--- a/src/Tests/SourceGenerator2Test/Helpers/CSharpAnalyzerTest{TAnalyzer}.cs
+++ b/src/Tests/SourceGenerator2Test/Helpers/CSharpAnalyzerTest{TAnalyzer}.cs
@@ -59,11 +59,13 @@ protected override ParseOptions CreateParseOptions()
/// The list of expected diagnostic for the test (used as alternative to the markdown syntax).
/// Whether to enable unsafe blocks.
/// The language version to use to run the test.
+ /// Whether to set the "CsWinRTComponent" MSBuild property to .
public static Task VerifyAnalyzerAsync(
string source,
ReadOnlySpan expectedDiagnostics = default,
bool allowUnsafeBlocks = true,
- LanguageVersion languageVersion = LanguageVersion.CSharp14)
+ LanguageVersion languageVersion = LanguageVersion.CSharp14,
+ bool isCsWinRTComponent = false)
{
CSharpAnalyzerTest test = new(allowUnsafeBlocks, languageVersion) { TestCode = source };
@@ -73,6 +75,16 @@ public static Task VerifyAnalyzerAsync(
test.TestState.AdditionalReferences.Add(MetadataReference.CreateFromFile(typeof(Button).Assembly.Location));
test.TestState.ExpectedDiagnostics.AddRange([.. expectedDiagnostics]);
+ // Configure the desired MSBuild properties via a global analyzer config file
+ if (isCsWinRTComponent)
+ {
+ test.TestState.AnalyzerConfigFiles.Add(("/.globalconfig", """
+ is_global = true
+
+ build_property.CsWinRTComponent = true
+ """));
+ }
+
return test.RunAsync(CancellationToken.None);
}
}
\ No newline at end of file
diff --git a/src/Tests/SourceGenerator2Test/Test_ApiContractTypeRequiresContractVersionAnalyzer.cs b/src/Tests/SourceGenerator2Test/Test_ApiContractTypeRequiresContractVersionAnalyzer.cs
new file mode 100644
index 0000000000..e8f5873173
--- /dev/null
+++ b/src/Tests/SourceGenerator2Test/Test_ApiContractTypeRequiresContractVersionAnalyzer.cs
@@ -0,0 +1,99 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System.Threading.Tasks;
+using WindowsRuntime.SourceGenerator.Diagnostics;
+using WindowsRuntime.SourceGenerator.Tests.Helpers;
+
+namespace WindowsRuntime.SourceGenerator.Tests;
+
+using VerifyCS = CSharpAnalyzerTest;
+
+///
+/// Tests for .
+///
+[TestClass]
+public sealed class Test_ApiContractTypeRequiresContractVersionAnalyzer
+{
+ [TestMethod]
+ public async Task ApiContractEnum_WithVersionOnlyConstructor_DoesNotWarn()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ [ApiContract]
+ [ContractVersion(1u)]
+ public enum MyContract;
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task ApiContractEnum_WithNameAndVersionConstructor_DoesNotWarn()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ [ApiContract]
+ [ContractVersion("OtherContract", 1u)]
+ public enum MyContract;
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task NonApiContractEnum_DoesNotWarn()
+ {
+ const string source = """
+ public enum MyEnum
+ {
+ A,
+ B
+ }
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task ApiContractEnum_NotComponent_DoesNotWarn()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ [ApiContract]
+ public enum MyContract;
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source);
+ }
+
+ [TestMethod]
+ public async Task ApiContractEnum_NoContractVersion_Warns()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ [ApiContract]
+ public enum {|CSWINRT2014:MyContract|};
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task ApiContractEnum_OnlyContractTypeConstructor_Warns()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ [ApiContract]
+ [ContractVersion(typeof(MyContract), 1u)]
+ public enum {|CSWINRT2014:MyContract|};
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+}
diff --git a/src/Tests/SourceGenerator2Test/Test_PublicTypeMixedVersioningAttributesAnalyzer.cs b/src/Tests/SourceGenerator2Test/Test_PublicTypeMixedVersioningAttributesAnalyzer.cs
new file mode 100644
index 0000000000..f0d0623476
--- /dev/null
+++ b/src/Tests/SourceGenerator2Test/Test_PublicTypeMixedVersioningAttributesAnalyzer.cs
@@ -0,0 +1,224 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System.Threading.Tasks;
+using WindowsRuntime.SourceGenerator.Diagnostics;
+using WindowsRuntime.SourceGenerator.Tests.Helpers;
+
+namespace WindowsRuntime.SourceGenerator.Tests;
+
+using VerifyCS = CSharpAnalyzerTest;
+
+///
+/// Tests for .
+///
+[TestClass]
+public sealed class Test_PublicTypeMixedVersioningAttributesAnalyzer
+{
+ [TestMethod]
+ public async Task PublicClass_NoVersioning_DoesNotWarn()
+ {
+ const string source = """
+ public sealed class MyClass;
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task PublicClass_OnlyContractVersion_DoesNotWarn()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ [ApiContract]
+ [ContractVersion(1u)]
+ public enum MyContract;
+
+ [ContractVersion(typeof(MyContract), 1u)]
+ public sealed class MyClass;
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task PublicClass_OnlyVersion_DoesNotWarn()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ [Version(1u)]
+ public sealed class MyClass;
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task ApiContractEnum_WithContractVersionAndVersion_DoesNotWarn()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ [ApiContract]
+ [ContractVersion(1u)]
+ [Version(1u)]
+ public enum MyContract;
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task InternalClass_WithBothAttributes_DoesNotWarn()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ [ApiContract]
+ [ContractVersion(1u)]
+ public enum MyContract;
+
+ [ContractVersion(typeof(MyContract), 1u)]
+ [Version(1u)]
+ internal sealed class MyClass;
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task NestedPublicClass_WithBothAttributes_DoesNotWarn()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ [ApiContract]
+ [ContractVersion(1u)]
+ public enum MyContract;
+
+ [ContractVersion(typeof(MyContract), 1u)]
+ public sealed class Outer
+ {
+ [ContractVersion(typeof(MyContract), 1u)]
+ [Version(1u)]
+ public sealed class Nested;
+ }
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task PublicClass_WithBothAttributes_NotComponent_DoesNotWarn()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ [ApiContract]
+ [ContractVersion(1u)]
+ public enum MyContract;
+
+ [ContractVersion(typeof(MyContract), 1u)]
+ [Version(1u)]
+ public sealed class MyClass;
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source);
+ }
+
+ [TestMethod]
+ public async Task PublicClass_WithBothAttributes_Warns()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ [ApiContract]
+ [ContractVersion(1u)]
+ public enum MyContract;
+
+ [ContractVersion(typeof(MyContract), 1u)]
+ [Version(1u)]
+ public sealed class {|CSWINRT2017:MyClass|};
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task PublicInterface_WithBothAttributes_Warns()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ [ApiContract]
+ [ContractVersion(1u)]
+ public enum MyContract;
+
+ [ContractVersion(typeof(MyContract), 1u)]
+ [Version(1u)]
+ public interface {|CSWINRT2017:IMyInterface|};
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task PublicStruct_WithBothAttributes_Warns()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ [ApiContract]
+ [ContractVersion(1u)]
+ public enum MyContract;
+
+ [ContractVersion(typeof(MyContract), 1u)]
+ [Version(1u)]
+ public struct {|CSWINRT2017:MyStruct|};
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task PublicEnum_WithBothAttributes_Warns()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ [ApiContract]
+ [ContractVersion(1u)]
+ public enum MyContract;
+
+ [ContractVersion(typeof(MyContract), 1u)]
+ [Version(1u)]
+ public enum {|CSWINRT2017:MyEnum|}
+ {
+ A,
+ B
+ }
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task PublicDelegate_WithBothAttributes_Warns()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ [ApiContract]
+ [ContractVersion(1u)]
+ public enum MyContract;
+
+ [ContractVersion(typeof(MyContract), 1u)]
+ [Version(1u)]
+ public delegate void {|CSWINRT2017:MyDelegate|}();
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+}
diff --git a/src/Tests/SourceGenerator2Test/Test_PublicTypeRequiresContractVersionAnalyzer.cs b/src/Tests/SourceGenerator2Test/Test_PublicTypeRequiresContractVersionAnalyzer.cs
new file mode 100644
index 0000000000..61409c48eb
--- /dev/null
+++ b/src/Tests/SourceGenerator2Test/Test_PublicTypeRequiresContractVersionAnalyzer.cs
@@ -0,0 +1,239 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System.Threading.Tasks;
+using WindowsRuntime.SourceGenerator.Diagnostics;
+using WindowsRuntime.SourceGenerator.Tests.Helpers;
+
+namespace WindowsRuntime.SourceGenerator.Tests;
+
+using VerifyCS = CSharpAnalyzerTest;
+
+///
+/// Tests for .
+///
+[TestClass]
+public sealed class Test_PublicTypeRequiresContractVersionAnalyzer
+{
+ [TestMethod]
+ public async Task NoPublicTypes_DoesNotWarn()
+ {
+ const string source = """
+ internal sealed class MyClass;
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task SinglePublicType_WithContractVersion_DoesNotWarn()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ [ApiContract]
+ [ContractVersion(1u)]
+ public enum MyContract;
+
+ [ContractVersion(typeof(MyContract), 1u)]
+ public sealed class MyClass;
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task SinglePublicType_WithoutContractVersion_DoesNotWarn()
+ {
+ const string source = """
+ public sealed class MyClass;
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task AllPublicTypes_WithoutContractVersion_DoesNotWarn()
+ {
+ const string source = """
+ public sealed class MyClass1;
+
+ public sealed class MyClass2;
+
+ public interface IMyInterface;
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task AllPublicTypes_WithContractVersion_DoesNotWarn()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ [ApiContract]
+ [ContractVersion(1u)]
+ public enum MyContract;
+
+ [ContractVersion(typeof(MyContract), 1u)]
+ public sealed class MyClass1;
+
+ [ContractVersion(typeof(MyContract), 1u)]
+ public sealed class MyClass2;
+
+ [ContractVersion(typeof(MyContract), 1u)]
+ public interface IMyInterface;
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task MixedPublicTypes_NotComponent_DoesNotWarn()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ [ApiContract]
+ [ContractVersion(1u)]
+ public enum MyContract;
+
+ [ContractVersion(typeof(MyContract), 1u)]
+ public sealed class MyClass1;
+
+ public sealed class MyClass2;
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source);
+ }
+
+ [TestMethod]
+ public async Task ApiContractEnum_WithoutContractVersion_DoesNotWarn()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ [ApiContract]
+ [ContractVersion(1u)]
+ public enum MyContract;
+
+ [ContractVersion(typeof(MyContract), 1u)]
+ public sealed class MyClass1;
+
+ [ApiContract]
+ public enum MyOtherContract;
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task NestedPublicType_WithoutContractVersion_DoesNotWarn()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ [ApiContract]
+ [ContractVersion(1u)]
+ public enum MyContract;
+
+ [ContractVersion(typeof(MyContract), 1u)]
+ public sealed class Outer
+ {
+ public sealed class Nested;
+ }
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task InternalType_WithoutContractVersion_DoesNotWarn()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ [ApiContract]
+ [ContractVersion(1u)]
+ public enum MyContract;
+
+ [ContractVersion(typeof(MyContract), 1u)]
+ public sealed class MyClass1;
+
+ internal sealed class MyClass2;
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task MixedPublicTypes_WithoutContractVersion_Warns()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ [ApiContract]
+ [ContractVersion(1u)]
+ public enum MyContract;
+
+ [ContractVersion(typeof(MyContract), 1u)]
+ public sealed class MyClass1;
+
+ public sealed class {|CSWINRT2016:MyClass2|};
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task PublicTypeWithVersionOnly_OtherTypeWithContractVersion_Warns()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ [ApiContract]
+ [ContractVersion(1u)]
+ public enum MyContract;
+
+ [ContractVersion(typeof(MyContract), 1u)]
+ public sealed class MyClass1;
+
+ [Version(1u)]
+ public sealed class {|CSWINRT2016:MyClass2|};
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task MultiplePublicTypes_WithoutContractVersion_AllWarn()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ [ApiContract]
+ [ContractVersion(1u)]
+ public enum MyContract;
+
+ [ContractVersion(typeof(MyContract), 1u)]
+ public sealed class MyClass1;
+
+ public sealed class {|CSWINRT2016:MyClass2|};
+
+ public interface {|CSWINRT2016:IMyInterface|};
+
+ public struct {|CSWINRT2016:MyStruct|};
+
+ public enum {|CSWINRT2016:MyEnum|}
+ {
+ A,
+ B
+ }
+
+ public delegate void {|CSWINRT2016:MyDelegate|}();
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+}
diff --git a/src/Tests/SourceGenerator2Test/Test_PublicTypeRequiresVersioningAnalyzer.cs b/src/Tests/SourceGenerator2Test/Test_PublicTypeRequiresVersioningAnalyzer.cs
new file mode 100644
index 0000000000..44cb1e729c
--- /dev/null
+++ b/src/Tests/SourceGenerator2Test/Test_PublicTypeRequiresVersioningAnalyzer.cs
@@ -0,0 +1,172 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System.Threading.Tasks;
+using WindowsRuntime.SourceGenerator.Diagnostics;
+using WindowsRuntime.SourceGenerator.Tests.Helpers;
+
+namespace WindowsRuntime.SourceGenerator.Tests;
+
+using VerifyCS = CSharpAnalyzerTest;
+
+///
+/// Tests for .
+///
+[TestClass]
+public sealed class Test_PublicTypeRequiresVersioningAnalyzer
+{
+ [TestMethod]
+ public async Task PublicClass_WithContractVersion_DoesNotWarn()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ [ApiContract]
+ [ContractVersion(1u)]
+ public enum MyContract;
+
+ [ContractVersion(typeof(MyContract), 1u)]
+ public sealed class MyClass;
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task PublicClass_WithVersion_DoesNotWarn()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ [Version(1u)]
+ public sealed class MyClass;
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task PublicClass_WithBothContractVersionAndVersion_DoesNotWarn()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ [ApiContract]
+ [ContractVersion(1u)]
+ public enum MyContract;
+
+ [ContractVersion(typeof(MyContract), 1u)]
+ [Version(1u)]
+ public sealed class MyClass;
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task ApiContractEnum_WithoutContractVersion_DoesNotWarn()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ [ApiContract]
+ public enum MyContract;
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task InternalClass_WithoutVersioning_DoesNotWarn()
+ {
+ const string source = """
+ internal sealed class MyClass;
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task NestedPublicClass_WithoutVersioning_DoesNotWarn()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ [ApiContract]
+ [ContractVersion(1u)]
+ public enum MyContract;
+
+ [ContractVersion(typeof(MyContract), 1u)]
+ public sealed class Outer
+ {
+ public sealed class Nested;
+ }
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task PublicClass_NotComponent_DoesNotWarn()
+ {
+ const string source = """
+ public sealed class MyClass;
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source);
+ }
+
+ [TestMethod]
+ public async Task PublicClass_WithoutVersioning_Warns()
+ {
+ const string source = """
+ public sealed class {|CSWINRT2015:MyClass|};
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task PublicInterface_WithoutVersioning_Warns()
+ {
+ const string source = """
+ public interface {|CSWINRT2015:IMyInterface|};
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task PublicStruct_WithoutVersioning_Warns()
+ {
+ const string source = """
+ public struct {|CSWINRT2015:MyStruct|};
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task PublicEnum_WithoutVersioning_Warns()
+ {
+ const string source = """
+ public enum {|CSWINRT2015:MyEnum|}
+ {
+ A,
+ B
+ }
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task PublicDelegate_WithoutVersioning_Warns()
+ {
+ const string source = """
+ public delegate void {|CSWINRT2015:MyDelegate|}();
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+}
diff --git a/src/Tests/SourceGenerator2Test/Test_ValidApiContractEnumTypeAnalyzer.cs b/src/Tests/SourceGenerator2Test/Test_ValidApiContractEnumTypeAnalyzer.cs
new file mode 100644
index 0000000000..35f2e98c48
--- /dev/null
+++ b/src/Tests/SourceGenerator2Test/Test_ValidApiContractEnumTypeAnalyzer.cs
@@ -0,0 +1,94 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System.Threading.Tasks;
+using WindowsRuntime.SourceGenerator.Diagnostics;
+using WindowsRuntime.SourceGenerator.Tests.Helpers;
+
+namespace WindowsRuntime.SourceGenerator.Tests;
+
+using VerifyCS = CSharpAnalyzerTest;
+
+///
+/// Tests for .
+///
+[TestClass]
+public sealed class Test_ValidApiContractEnumTypeAnalyzer
+{
+ [TestMethod]
+ public async Task EmptyApiContractEnum_DoesNotWarn()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ [ApiContract]
+ public enum MyContract;
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task EnumWithCases_NoApiContract_DoesNotWarn()
+ {
+ const string source = """
+ public enum MyEnum
+ {
+ A,
+ B,
+ C
+ }
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task ApiContractEnumWithCases_NotComponent_DoesNotWarn()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ [ApiContract]
+ public enum MyContract
+ {
+ A
+ }
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source);
+ }
+
+ [TestMethod]
+ public async Task ApiContractEnumWithCases_Warns()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ [ApiContract]
+ public enum {|CSWINRT2010:MyContract|}
+ {
+ A,
+ B
+ }
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task ApiContractEnumWithSingleCase_Warns()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ [ApiContract]
+ public enum {|CSWINRT2010:MyContract|}
+ {
+ Version1
+ }
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+}
diff --git a/src/Tests/SourceGenerator2Test/Test_ValidContractVersionAttributeAnalyzer.cs b/src/Tests/SourceGenerator2Test/Test_ValidContractVersionAttributeAnalyzer.cs
new file mode 100644
index 0000000000..9c019179b6
--- /dev/null
+++ b/src/Tests/SourceGenerator2Test/Test_ValidContractVersionAttributeAnalyzer.cs
@@ -0,0 +1,186 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System.Threading.Tasks;
+using WindowsRuntime.SourceGenerator.Diagnostics;
+using WindowsRuntime.SourceGenerator.Tests.Helpers;
+
+namespace WindowsRuntime.SourceGenerator.Tests;
+
+using VerifyCS = CSharpAnalyzerTest;
+
+///
+/// Tests for .
+///
+[TestClass]
+public sealed class Test_ValidContractVersionAttributeAnalyzer
+{
+ [TestMethod]
+ public async Task VersionOnlyConstructor_OnApiContractType_DoesNotWarn()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ [ApiContract]
+ [ContractVersion(1u)]
+ public enum MyContract;
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task ContractNameAndVersionConstructor_OnApiContractType_DoesNotWarn()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ [ApiContract]
+ [ContractVersion("MyContractName", 1u)]
+ public enum MyContract;
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task ContractTypeConstructor_WithValidContract_OnNonContractType_DoesNotWarn()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ [ApiContract]
+ public enum MyContract;
+
+ [ContractVersion(typeof(MyContract), 1u)]
+ public class MyType;
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task InvalidUsage_NotComponent_DoesNotWarn()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ [ApiContract]
+ public enum MyContract;
+
+ [ContractVersion(1u)]
+ public class NotAContract;
+
+ [ContractVersion(typeof(MyContract), 1u)]
+ [ApiContract]
+ public enum AlsoAContract;
+
+ [ContractVersion(typeof(NotAContract), 1u)]
+ public class TypeWithBadContract;
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source);
+ }
+
+ [TestMethod]
+ public async Task VersionOnlyConstructor_OnNonContractType_Warns()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ [{|CSWINRT2011:ContractVersion(1u)|}]
+ public class MyType;
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task VersionOnlyConstructor_OnEnumWithoutApiContract_Warns()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ [{|CSWINRT2011:ContractVersion(1u)|}]
+ public enum MyEnum;
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task ContractNameAndVersionConstructor_OnNonContractType_Warns()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ [{|CSWINRT2011:ContractVersion("MyContractName", 1u)|}]
+ public class MyType;
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task ContractTypeConstructor_OnApiContractType_Warns()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ [ApiContract]
+ public enum OtherContract;
+
+ [ApiContract]
+ [{|CSWINRT2012:ContractVersion(typeof(OtherContract), 1u)|}]
+ public enum MyContract;
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task ContractTypeConstructor_WithNonContractTypeArgument_Warns()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ public class NotAContract;
+
+ [ContractVersion({|CSWINRT2013:typeof(NotAContract)|}, 1u)]
+ public class MyType;
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task ContractTypeConstructor_WithEnumWithoutApiContractArgument_Warns()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ public enum NotAContract;
+
+ [ContractVersion({|CSWINRT2013:typeof(NotAContract)|}, 1u)]
+ public class MyType;
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+
+ [TestMethod]
+ public async Task ContractTypeConstructor_OnApiContractType_WithNonContractTypeArgument_WarnsBoth()
+ {
+ const string source = """
+ using Windows.Foundation.Metadata;
+
+ public class NotAContract;
+
+ [ApiContract]
+ [{|CSWINRT2012:ContractVersion({|CSWINRT2013:typeof(NotAContract)|}, 1u)|}]
+ public enum MyContract;
+ """;
+
+ await VerifyCS.VerifyAnalyzerAsync(source, isCsWinRTComponent: true);
+ }
+}
diff --git a/src/WinRT.Runtime2/Windows.Foundation/Metadata/ContractVersionAttribute.cs b/src/WinRT.Runtime2/Windows.Foundation/Metadata/ContractVersionAttribute.cs
index 49f0db22da..0566bfca1e 100644
--- a/src/WinRT.Runtime2/Windows.Foundation/Metadata/ContractVersionAttribute.cs
+++ b/src/WinRT.Runtime2/Windows.Foundation/Metadata/ContractVersionAttribute.cs
@@ -13,7 +13,16 @@ namespace Windows.Foundation.Metadata;
/// Indicates the version of the API contract.
///
[WindowsRuntimeMetadata("Windows.Foundation.FoundationContract")]
-[AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+[AttributeUsage(
+ AttributeTargets.Delegate |
+ AttributeTargets.Enum |
+ AttributeTargets.Event |
+ AttributeTargets.Field |
+ AttributeTargets.Interface |
+ AttributeTargets.Method |
+ AttributeTargets.Property |
+ AttributeTargets.Class |
+ AttributeTargets.Struct, AllowMultiple = true)]
[SupportedOSPlatform("Windows10.0.10240.0")]
[ContractVersion(typeof(FoundationContract), 65536u)]
public sealed class ContractVersionAttribute : Attribute
@@ -22,6 +31,9 @@ public sealed class ContractVersionAttribute : Attribute
/// Creates a new instance with the specified parameters.
///
/// The version of the API contract.
+ ///
+ /// This constructor applies to a type with the and specifies the contract version of that API contract.
+ ///
public ContractVersionAttribute(uint version)
{
}
@@ -31,7 +43,10 @@ public ContractVersionAttribute(uint version)
///
/// The type to associate with the API contract.
/// The version of the API contract.
- public ContractVersionAttribute(Type contract, uint version)
+ ///
+ /// This constructor applies to a type with the and specifies the contract version of that API contract.
+ ///
+ public ContractVersionAttribute(string contract, uint version)
{
}
@@ -40,7 +55,11 @@ public ContractVersionAttribute(Type contract, uint version)
///
/// The type to associate with the API contract.
/// The version of the API contract.
- public ContractVersionAttribute(string contract, uint version)
+ ///
+ /// This constructor applies to any type that does not have the and
+ /// indicates the API contract version in which this type was added to the specified API contract.
+ ///
+ public ContractVersionAttribute(Type contract, uint version)
{
}
}
\ No newline at end of file