-
Notifications
You must be signed in to change notification settings - Fork 124
Add authoring analyzers for [ApiContract] and [ContractVersion] #2407
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Sergio0694
wants to merge
25
commits into
staging/3.0
Choose a base branch
from
user/sergiopedri/authoring-analyzers
base: staging/3.0
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
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 88dbe95
Add ValidApiContractEnumTypeAnalyzer for CSWINRT2010
Sergio0694 28a7efa
Add tests for ValidApiContractEnumTypeAnalyzer
Sergio0694 48a4503
Add CSWINRT2011-CSWINRT2013 diagnostic descriptors for invalid '[Cont…
Sergio0694 6871805
Add ValidContractVersionAttributeAnalyzer for CSWINRT2011-CSWINRT2013
Sergio0694 7cc5a2d
Add tests for ValidContractVersionAttributeAnalyzer
Sergio0694 f1e2c51
Update ContractVersionAttribute API and docs
Sergio0694 ab17e38
Validate multiple ContractVersion attributes
Sergio0694 93c5e0b
Move AttributeData location helpers to AttributeDataExtensions
Sergio0694 0e5946f
Add CSWINRT2014 diagnostic descriptor for API contract types missing …
Sergio0694 3132e1a
Add ApiContractTypeRequiresContractVersionAnalyzer for CSWINRT2014
Sergio0694 35dbacc
Add tests for ApiContractTypeRequiresContractVersionAnalyzer
Sergio0694 6f188e1
Add CSWINRT2015 diagnostic descriptor for public types missing 'Contr…
Sergio0694 4d9d3f6
Add PublicTypeRequiresContractVersionAnalyzer for CSWINRT2015
Sergio0694 fdfb225
Add tests for PublicTypeRequiresContractVersionAnalyzer
Sergio0694 61a4562
Add ISymbol.GetAttributes(INamedTypeSymbol) extension and use it in a…
Sergio0694 f951cc3
Use pattern matching for symbol extraction
Sergio0694 a802b85
Update CSWINRT2015 to also accept '[VersionAttribute]'
Sergio0694 e7a6ddb
Add CSWINRT2016 diagnostic descriptor for inconsistent contract versi…
Sergio0694 7908e4f
Add PublicTypeRequiresContractVersionAnalyzer for CSWINRT2016
Sergio0694 1aa80c0
Add tests for PublicTypeRequiresContractVersionAnalyzer
Sergio0694 6bf224d
Add CSWINRT2017 diagnostic descriptor for mixed versioning attributes
Sergio0694 3c88fb9
Add PublicTypeMixedVersioningAttributesAnalyzer for CSWINRT2017
Sergio0694 8e76e39
Add tests for PublicTypeMixedVersioningAttributesAnalyzer
Sergio0694 18487a9
Address new authoring analyzer warnings in 'AuthoringTest'
Sergio0694 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
78 changes: 78 additions & 0 deletions
78
....SourceGenerator2/Diagnostics/Analyzers/ApiContractTypeRequiresContractVersionAnalyzer.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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) | ||
| { | ||
| 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); | ||
| }); | ||
| } | ||
| } | ||
82 changes: 82 additions & 0 deletions
82
...nRT.SourceGenerator2/Diagnostics/Analyzers/PublicTypeMixedVersioningAttributesAnalyzer.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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); | ||
| }); | ||
| } | ||
| } |
104 changes: 104 additions & 0 deletions
104
...WinRT.SourceGenerator2/Diagnostics/Analyzers/PublicTypeRequiresContractVersionAnalyzer.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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)); | ||
| } | ||
| }); | ||
| }); | ||
| } | ||
| } |
85 changes: 85 additions & 0 deletions
85
...ring/WinRT.SourceGenerator2/Diagnostics/Analyzers/PublicTypeRequiresVersioningAnalyzer.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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); | ||
| }); | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.