-
Notifications
You must be signed in to change notification settings - Fork 468
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'dotnet:main' into make-AnalyzerOptionsExtensions-public
- Loading branch information
Showing
30 changed files
with
942 additions
and
11 deletions.
There are no files selected for viewing
This file contains 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,25 @@ | ||
## RS1041: Compiler extensions should be implemented in assemblies targeting netstandard2.0 | ||
|
||
Types which implement compiler extension points should only be declared in assemblies targeting netstandard2.0. More specific target frameworks are only available in a subset of supported compilation scenarios, so targeting them may cause the feature to behave unpredictably. | ||
|
||
|Item|Value| | ||
|-|-| | ||
|Category|MicrosoftCodeAnalysisCorrectness| | ||
|Enabled|True| | ||
|Severity|Warning| | ||
|CodeFix|False| | ||
--- | ||
|
||
This rule helps ensure compiler extensions (e.g. analyzers and source generators) will load correctly in all compilation | ||
scenarios. Depending on the manner in which the compiler is invoked, the compiler may execute under .NET Framework or | ||
.NET, and compiler extensions are expected to work consistently in both cases. By targeting netstandard2.0, compiler | ||
extensions are known to be compatible with both execution environments. | ||
|
||
### Compiler extension points | ||
|
||
The following compiler extension points are examined by this analyzer: | ||
|
||
* `DiagnosticAnalyzer` | ||
* `DiagnosticSuppressor` | ||
* `ISourceGenerator` | ||
* `IIncrementalGenerator` |
This file contains 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
This file contains 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
This file contains 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
This file contains 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
136 changes: 136 additions & 0 deletions
136
...oft.CodeAnalysis.Analyzers/Core/MetaAnalyzers/CompilerExtensionTargetFrameworkAnalyzer.cs
This file contains 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,136 @@ | ||
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information. | ||
|
||
using System.Collections.Immutable; | ||
using System.Diagnostics; | ||
using System.Diagnostics.CodeAnalysis; | ||
using System.Linq; | ||
using System.Runtime.Versioning; | ||
using Analyzer.Utilities; | ||
using Analyzer.Utilities.Extensions; | ||
using Microsoft.CodeAnalysis.Diagnostics; | ||
|
||
namespace Microsoft.CodeAnalysis.Analyzers.MetaAnalyzers | ||
{ | ||
using static CodeAnalysisDiagnosticsResources; | ||
|
||
[DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] | ||
internal sealed class CompilerExtensionTargetFrameworkAnalyzer : DiagnosticAnalyzer | ||
{ | ||
private static readonly LocalizableString s_localizableTitle = CreateLocalizableResourceString(nameof(DoNotDeclareCompilerFeatureInAssemblyWithUnsupportedTargetFrameworkRuleTitle)); | ||
private static readonly LocalizableString s_localizableDescription = CreateLocalizableResourceString(nameof(DoNotDeclareCompilerFeatureInAssemblyWithUnsupportedTargetFrameworkRuleDescription)); | ||
private const string HelpLinkUri = "https://github.com/dotnet/roslyn-analyzers/blob/main/docs/rules/RS1041.md"; | ||
|
||
public static readonly DiagnosticDescriptor DoNotDeclareCompilerFeatureInAssemblyWithUnsupportedTargetFrameworkStrictRule = new( | ||
DiagnosticIds.DoNotRegisterCompilerTypesWithBadTargetFrameworkRuleId, | ||
s_localizableTitle, | ||
CreateLocalizableResourceString(nameof(DoNotDeclareCompilerFeatureInAssemblyWithUnsupportedTargetFrameworkMessage)), | ||
DiagnosticCategory.MicrosoftCodeAnalysisCorrectness, | ||
DiagnosticSeverity.Warning, | ||
isEnabledByDefault: true, | ||
description: s_localizableDescription, | ||
helpLinkUri: HelpLinkUri, | ||
customTags: WellKnownDiagnosticTagsExtensions.Telemetry); | ||
|
||
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create( | ||
DoNotDeclareCompilerFeatureInAssemblyWithUnsupportedTargetFrameworkStrictRule); | ||
|
||
public override void Initialize(AnalysisContext context) | ||
{ | ||
context.EnableConcurrentExecution(); | ||
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); | ||
|
||
context.RegisterCompilationStartAction(context => | ||
{ | ||
var typeProvider = WellKnownTypeProvider.GetOrCreate(context.Compilation); | ||
var diagnosticAnalyzer = typeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnosticsDiagnosticAnalyzer); | ||
if (diagnosticAnalyzer is null) | ||
return; | ||
|
||
var targetFrameworkAttribute = typeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeVersioningTargetFrameworkAttribute); | ||
if (targetFrameworkAttribute is null) | ||
return; | ||
|
||
AttributeData? appliedTargetFrameworkAttribute = context.Compilation.Assembly.GetAttribute(targetFrameworkAttribute); | ||
if (appliedTargetFrameworkAttribute is null) | ||
return; | ||
|
||
if (appliedTargetFrameworkAttribute.ConstructorArguments.IsEmpty) | ||
{ | ||
return; | ||
} | ||
|
||
string displayName; | ||
switch (appliedTargetFrameworkAttribute.ConstructorArguments[0].Value as string) | ||
{ | ||
case ".NETStandard,Version=v1.0": | ||
case ".NETStandard,Version=v1.1": | ||
case ".NETStandard,Version=v1.2": | ||
case ".NETStandard,Version=v1.3": | ||
case ".NETStandard,Version=v1.4": | ||
case ".NETStandard,Version=v1.5": | ||
case ".NETStandard,Version=v1.6": | ||
case ".NETStandard,Version=v2.0": | ||
// The compiler supports this target framework | ||
return; | ||
|
||
default: | ||
displayName = appliedTargetFrameworkAttribute.NamedArguments.FirstOrDefault(arg => arg.Key == nameof(TargetFrameworkAttribute.FrameworkDisplayName)).Value.Value as string | ||
?? appliedTargetFrameworkAttribute.ConstructorArguments[0].Value as string | ||
?? "<unknown>"; | ||
break; | ||
} | ||
|
||
var diagnosticAnalyzerAttribute = typeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisDiagnosticsDiagnosticAnalyzerAttribute); | ||
var sourceGeneratorInterface = typeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisISourceGenerator); | ||
var incrementalGeneratorInterface = typeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisIIncrementalGenerator); | ||
var generatorAttribute = typeProvider.GetOrCreateTypeByMetadataName(WellKnownTypeNames.MicrosoftCodeAnalysisGeneratorAttribute); | ||
|
||
context.RegisterSymbolAction( | ||
context => | ||
{ | ||
var namedType = (INamedTypeSymbol)context.Symbol; | ||
if (!IsRegisteredExtension(namedType, diagnosticAnalyzer, diagnosticAnalyzerAttribute, out var applicationSyntaxReference) | ||
&& !IsRegisteredExtension(namedType, sourceGeneratorInterface, generatorAttribute, out applicationSyntaxReference) | ||
&& !IsRegisteredExtension(namedType, incrementalGeneratorInterface, generatorAttribute, out applicationSyntaxReference)) | ||
{ | ||
// This is not a compiler extension | ||
return; | ||
} | ||
|
||
context.ReportDiagnostic(Diagnostic.Create( | ||
DoNotDeclareCompilerFeatureInAssemblyWithUnsupportedTargetFrameworkStrictRule, | ||
Location.Create(applicationSyntaxReference.SyntaxTree, applicationSyntaxReference.Span), | ||
displayName)); | ||
}, | ||
SymbolKind.NamedType); | ||
}); | ||
} | ||
|
||
private static bool IsRegisteredExtension(INamedTypeSymbol extension, [NotNullWhen(true)] INamedTypeSymbol? extensionClassOrInterface, [NotNullWhen(true)] INamedTypeSymbol? registrationAttributeType, [NotNullWhen(true)] out SyntaxReference? node) | ||
{ | ||
if (!extension.Inherits(extensionClassOrInterface)) | ||
{ | ||
node = null; | ||
return false; | ||
} | ||
|
||
foreach (var attribute in extension.GetAttributes()) | ||
{ | ||
// Only examine extension registrations in source | ||
Debug.Assert(attribute.ApplicationSyntaxReference is not null, | ||
$"Expected attributes returned by {nameof(ISymbol.GetAttributes)} (as opposed to {nameof(ITypeSymbolExtensions.GetApplicableAttributes)}) to have a non-null application."); | ||
if (attribute.ApplicationSyntaxReference is null) | ||
continue; | ||
|
||
if (!attribute.AttributeClass.Inherits(registrationAttributeType)) | ||
continue; | ||
|
||
node = attribute.ApplicationSyntaxReference; | ||
return true; | ||
} | ||
|
||
node = null; | ||
return false; | ||
} | ||
} | ||
} |
This file contains 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
This file contains 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
Oops, something went wrong.