Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
d0ad453
Add CSWINRT2010 diagnostic descriptor for invalid '[ApiContract]' enu…
Sergio0694 Apr 24, 2026
88dbe95
Add ValidApiContractEnumTypeAnalyzer for CSWINRT2010
Sergio0694 Apr 24, 2026
28a7efa
Add tests for ValidApiContractEnumTypeAnalyzer
Sergio0694 Apr 24, 2026
48a4503
Add CSWINRT2011-CSWINRT2013 diagnostic descriptors for invalid '[Cont…
Sergio0694 Apr 24, 2026
6871805
Add ValidContractVersionAttributeAnalyzer for CSWINRT2011-CSWINRT2013
Sergio0694 Apr 24, 2026
7cc5a2d
Add tests for ValidContractVersionAttributeAnalyzer
Sergio0694 Apr 24, 2026
f1e2c51
Update ContractVersionAttribute API and docs
Sergio0694 Apr 24, 2026
ab17e38
Validate multiple ContractVersion attributes
Sergio0694 Apr 24, 2026
93c5e0b
Move AttributeData location helpers to AttributeDataExtensions
Sergio0694 Apr 24, 2026
0e5946f
Add CSWINRT2014 diagnostic descriptor for API contract types missing …
Sergio0694 Apr 24, 2026
3132e1a
Add ApiContractTypeRequiresContractVersionAnalyzer for CSWINRT2014
Sergio0694 Apr 24, 2026
35dbacc
Add tests for ApiContractTypeRequiresContractVersionAnalyzer
Sergio0694 Apr 24, 2026
6f188e1
Add CSWINRT2015 diagnostic descriptor for public types missing 'Contr…
Sergio0694 Apr 24, 2026
4d9d3f6
Add PublicTypeRequiresContractVersionAnalyzer for CSWINRT2015
Sergio0694 Apr 24, 2026
fdfb225
Add tests for PublicTypeRequiresContractVersionAnalyzer
Sergio0694 Apr 24, 2026
61a4562
Add ISymbol.GetAttributes(INamedTypeSymbol) extension and use it in a…
Sergio0694 Apr 24, 2026
f951cc3
Use pattern matching for symbol extraction
Sergio0694 Apr 24, 2026
a802b85
Update CSWINRT2015 to also accept '[VersionAttribute]'
Sergio0694 May 6, 2026
e7a6ddb
Add CSWINRT2016 diagnostic descriptor for inconsistent contract versi…
Sergio0694 May 6, 2026
7908e4f
Add PublicTypeRequiresContractVersionAnalyzer for CSWINRT2016
Sergio0694 May 6, 2026
1aa80c0
Add tests for PublicTypeRequiresContractVersionAnalyzer
Sergio0694 May 6, 2026
6bf224d
Add CSWINRT2017 diagnostic descriptor for mixed versioning attributes
Sergio0694 May 6, 2026
3c88fb9
Add PublicTypeMixedVersioningAttributesAnalyzer for CSWINRT2017
Sergio0694 May 6, 2026
8e76e39
Add tests for PublicTypeMixedVersioningAttributesAnalyzer
Sergio0694 May 6, 2026
18487a9
Address new authoring analyzer warnings in 'AuthoringTest'
Sergio0694 May 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
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]'
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// A diagnostic analyzer that validates that <c>[ApiContract]</c> enum types declare their contract version using <c>[ContractVersion]</c>.
/// </summary>
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class ApiContractTypeRequiresContractVersionAnalyzer : DiagnosticAnalyzer
{
/// <inheritdoc/>
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = [DiagnosticDescriptors.ApiContractTypeMissingContractVersion];

/// <inheritdoc/>
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)
Comment thread
Sergio0694 marked this conversation as resolved.
{
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);
});
}
}
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// A diagnostic analyzer that reports when a public authored type has both a <c>[ContractVersion]</c>
/// and a <c>[Version]</c> 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.
/// </summary>
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class PublicTypeMixedVersioningAttributesAnalyzer : DiagnosticAnalyzer
{
/// <inheritdoc/>
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = [DiagnosticDescriptors.PublicTypeMixedVersioningAttributes];

/// <inheritdoc/>
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);
});
}
}
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// A diagnostic analyzer that reports when a public authored type is missing a <c>[ContractVersion]</c> 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.
/// </summary>
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class PublicTypeRequiresContractVersionAnalyzer : DiagnosticAnalyzer
{
/// <inheritdoc/>
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = [DiagnosticDescriptors.PublicTypeMissingContractVersion];

/// <inheritdoc/>
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<INamedTypeSymbol> 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));
}
});
});
}
}
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// A diagnostic analyzer that reports when a public authored type is missing version metadata
/// (i.e. is missing both a <c>[ContractVersion]</c> and a <c>[Version]</c> attribute).
/// </summary>
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class PublicTypeRequiresVersioningAnalyzer : DiagnosticAnalyzer
{
/// <inheritdoc/>
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = [DiagnosticDescriptors.PublicTypeMissingVersioning];

/// <inheritdoc/>
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);
});
}
}
Loading