Skip to content

Commit

Permalink
Add analyzer tests (#3990)
Browse files Browse the repository at this point in the history
Co-authored-by: Martin Taillefer <mataille@microsoft.com>
  • Loading branch information
geeknoid and Martin Taillefer committed May 23, 2023
1 parent b689484 commit 4f2f809
Show file tree
Hide file tree
Showing 81 changed files with 10,672 additions and 50 deletions.
1 change: 1 addition & 0 deletions eng/Packages/General-latest.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Condition="'$(TargetFramework)' == '$(LatestTargetFramework)'">
<PackageVersion Include="Microsoft.Bcl.TimeProvider" Version="$(MicrosoftBclTimeProviderVersion)" />
<PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="$(MicrosoftExtensionsConfigurationAbstractionsVersion)" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="$(MicrosoftExtensionsConfigurationAbstractionsVersion)" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="$(MicrosoftExtensionsConfigurationAbstractionsVersion)" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="$(MicrosoftExtensionsConfigurationBinderVersion)" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,18 @@ public Arrays(CallAnalyzer.Registrar reg)
reg.RegisterConstructors(_collectionTypes, HandleConstructor);
reg.RegisterMethods(_collectionFactories, HandleMethod);

var freezer = reg.Compilation.GetTypeByMetadataName("Microsoft.Extensions.Collections.Frozen.Freezer");
var freezer = reg.Compilation.GetTypeByMetadataName("System.Collections.Frozen.FrozenDictionary");
if (freezer != null)
{
foreach (var method in freezer.GetMembers("ToFrozenDictionary").OfType<IMethodSymbol>().Where(m => m.TypeParameters.Length == 2))
{
reg.RegisterMethod(method, HandleMethod);
}
}

freezer = reg.Compilation.GetTypeByMetadataName("System.Collections.Frozen.FrozenSet");
if (freezer != null)
{
foreach (var method in freezer.GetMembers("ToFrozenSet").OfType<IMethodSymbol>().Where(m => m.TypeParameters.Length == 1))
{
reg.RegisterMethod(method, HandleMethod);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.Extensions.ExtraAnalyzers.Utilities;

namespace Microsoft.Extensions.ExtraAnalyzers;

Expand All @@ -22,6 +23,8 @@ public override void Initialize(AnalysisContext context)

context.RegisterCompilationStartAction(context =>
{
var experimentalAttribute = context.Compilation.GetTypeByMetadataName("System.Diagnostics.CodeAnalysis.ExperimentalAttribute");
context.RegisterSyntaxNodeAction(context =>
{
var sn = (IdentifierNameSyntax)context.Node;
Expand All @@ -32,34 +35,16 @@ public override void Initialize(AnalysisContext context)
var sym = context.SemanticModel.GetSymbolInfo(sn).Symbol;
if (sym != null && HasExperimentalAttribute(sym))
{
var diagnostic = Diagnostic.Create(DiagDescriptors.UsingExperimentalApi, sn.GetLocation(), sym.Name);
context.ReportDiagnostic(diagnostic);
}
else if (sym is INamedTypeSymbol type && HasExperimentalAttribute(type.ContainingAssembly))
{
var diagnostic = Diagnostic.Create(DiagDescriptors.UsingExperimentalApi, sn.GetLocation(), type.ContainingAssembly.Name);
context.ReportDiagnostic(diagnostic);
}
}, SyntaxKind.IdentifierName);
static bool HasExperimentalAttribute(ISymbol sym)
{
foreach (var attributeData in sym.GetAttributes())
if (sym != null
&& (sym.Kind is not SymbolKind.Namespace and not SymbolKind.Label and not SymbolKind.Discard)
&& sym.IsContaminated(experimentalAttribute))
{
if (attributeData.AttributeClass?.Name == "ExperimentalAttribute")
if (sym.IsExternallyVisible())
{
var ns = attributeData.AttributeClass.ContainingNamespace.ToString();
if (ns is "System.Diagnostics.CodeAnalysis")
{
return true;
}
context.ReportDiagnostic(Diagnostic.Create(DiagDescriptors.UsingExperimentalApi, sn.GetLocation(), sym));
}
}
return false;
}
}, SyntaxKind.IdentifierName);
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -119,4 +119,67 @@ public static bool InheritsFromType(this ITypeSymbol type, string baseTypeFullNa

return false;
}

public static bool HasAttribute(this ISymbol sym, INamedTypeSymbol attribute)
{
foreach (var a in sym.GetAttributes())
{
if (SymbolEqualityComparer.Default.Equals(a.AttributeClass, attribute))
{
return true;
}
}

return false;
}

public static bool IsContaminated(this ISymbol symbol, INamedTypeSymbol? contaminationAttribute)
{
return (contaminationAttribute != null) && IsContaminated(symbol);

bool IsContaminated(ISymbol symbol)
{
if (symbol.HasAttribute(contaminationAttribute))
{
// symbol is annotated
return true;
}

if (symbol.ContainingAssembly != null
&& symbol.ContainingAssembly.HasAttribute(contaminationAttribute))
{
// symbol's assembly is annotated
return true;
}

var container = symbol.ContainingType;
while (container != null)
{
if (IsContaminated(container))
{
// symbol's container is annotated
return true;
}

container = container.ContainingType;
}

if (symbol is INamedTypeSymbol type)
{
var baseType = type.BaseType;
while (baseType != null)
{
if (IsContaminated(baseType))
{
// symbol's base type is annotated
return true;
}

baseType = baseType.BaseType;
}
}

return false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ static void Handle(OperationAnalysisContext context, IThrowOperation op)
var diagnostic = Diagnostic.Create(
DiagDescriptors.ThrowsStatement,
op.Syntax.GetLocation(),
$"Microsoft.Extensions.Diagnostics.Throws.{creationOp.Type.Name}");
$"Microsoft.Shared.Diagnostics.Throws.{creationOp.Type.Name}");

context.ReportDiagnostic(diagnostic);
}
Expand All @@ -58,7 +58,7 @@ static void Handle(OperationAnalysisContext context, IThrowOperation op)
var diagnostic = Diagnostic.Create(
DiagDescriptors.ThrowsExpression,
binaryExpression.GetLocation(),
"Microsoft.Extensions.Diagnostics.Throws.IfNull");
"Microsoft.Shared.Diagnostics.Throws.IfNull");

context.ReportDiagnostic(diagnostic);
}
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 9 additions & 9 deletions src/Analyzers/Microsoft.Extensions.LocalAnalyzers/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -118,37 +118,37 @@
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="ThrowsExpressionDescription" xml:space="preserve">
<value>Recommends replacing explicit argument throwing with the more efficient 'Microsoft.R9.Extensions.Diagnostics.Throws' class</value>
<value>Recommends replacing explicit argument throwing with the more efficient 'Microsoft.Extensions.Diagnostics.Throws' class</value>
</data>
<data name="ThrowsExpressionMessage" xml:space="preserve">
<value>Use '{0}' to throw the exception instead to improve performance</value>
</data>
<data name="ThrowsExpressionTitle" xml:space="preserve">
<value>Use the 'Microsoft.R9.Extensions.Diagnostics.Throws' class instead of explicitly throwing exception for improved performance</value>
<value>Use the 'Microsoft.Shared.Diagnostics.Throws' class instead of explicitly throwing exception for improved performance</value>
</data>
<data name="ThrowsStatementDescription" xml:space="preserve">
<value>Recommends replacing explicit argument throwing with the more efficient 'Microsoft.R9.Extensions.Diagnostics.Throws' class</value>
<value>Recommends replacing explicit argument throwing with the more efficient 'Microsoft.Shared.Diagnostics.Throws' class</value>
</data>
<data name="ThrowsStatementMessage" xml:space="preserve">
<value>Use '{0}' to throw the exception instead to improve performance</value>
</data>
<data name="ThrowsStatementTitle" xml:space="preserve">
<value>Use the 'Microsoft.R9.Extensions.Diagnostics.Throws' class instead of explicitly throwing exception for improved performance</value>
<value>Use the 'Microsoft.Shared.Diagnostics.Throws' class instead of explicitly throwing exception for improved performance</value>
</data>
<data name="ReplaceWithStaticNullCheckMethod" xml:space="preserve">
<value>Replace explicit null check with call to 'Throws.IfNull' (needs a 'PackageReference' to 'Microsoft.R9.Extensions.Essentials')</value>
<value>Replace explicit null check with call to 'Throws.IfNull' (needs a 'PackageReference' to 'Microsoft.Extensions.Essentials')</value>
</data>
<data name="ReplaceWithStaticThrowMethod" xml:space="preserve">
<value>Replace explicit throw with call to 'Throws' (needs a 'PackageReference' to 'Microsoft.R9.Extensions.Essentials')</value>
<value>Replace explicit throw with call to 'Throws' (needs a 'PackageReference' to 'Microsoft.Extensions.Essentials')</value>
</data>
<data name="ToInvariantStringDescription" xml:space="preserve">
<value>'Microsoft.R9.Extensions.Text.NumericExtensions.ToInvariantString' provides caching for common numeric values, avoiding the need to allocate new strings in many situations</value>
<value>'Microsoft.Shared.Text.NumericExtensions.ToInvariantString' provides caching for common numeric values, avoiding the need to allocate new strings in many situations</value>
</data>
<data name="ToInvariantStringMessage" xml:space="preserve">
<value>Use 'Microsoft.R9.Extensions.Text.NumericExtensions.ToInvariantString' for improved performance</value>
<value>Use 'Microsoft.Shared.Text.NumericExtensions.ToInvariantString' for improved performance</value>
</data>
<data name="ToInvariantStringTitle" xml:space="preserve">
<value>Use 'Microsoft.R9.Extensions.Text.NumericExtensions.ToInvariantString' for improved performance</value>
<value>Use 'Microsoft.Shared.Text.NumericExtensions.ToInvariantString' for improved performance</value>
</data>
<data name="ExperimentalSymbolsCantBeMarkedObsoleteDescription" xml:space="preserve">
<value>Symbols being added to the public API of an assembly cannot be marked as obsolete</value>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@
<value>Can't have multiple data classification attributes for a single data value</value>
</data>
<data name="MissingRedactorProviderArgumentMessage" xml:space="preserve">
<value>One of the parameters to a logging method that performs redaction must implement the Microsoft.R9.Extensions.Redaction.IRedactorProvider interface</value>
<value>One of the parameters to a logging method that performs redaction must implement the Microsoft.Extensions.Redaction.IRedactorProvider interface</value>
</data>
<data name="MissingRedactorProviderArgumentTitle" xml:space="preserve">
<value>A parameter to a logging method must implement the "IRedactorProvider" interface</value>
Expand All @@ -250,16 +250,16 @@
<value>Logging method parameters must be annotated with a data classification attribute</value>
</data>
<data name="MissingRedactorProviderFieldMessage" xml:space="preserve">
<value>Couldn't find a field of type "Microsoft.R9.Extensions.Redaction.IRedactorProvider" in type "{0}"</value>
<value>Couldn't find a field of type "Microsoft.Extensions.Redaction.IRedactorProvider" in type "{0}"</value>
</data>
<data name="MissingRedactorProviderFieldTitle" xml:space="preserve">
<value>Couldn't find a field of type "Microsoft.R9.Extensions.Redaction.IRedactorProvider"</value>
<value>Couldn't find a field of type "Microsoft.Extensions.Redaction.IRedactorProvider"</value>
</data>
<data name="MultipleRedactorProviderFieldsMessage" xml:space="preserve">
<value>Found multiple fields of type "Microsoft.R9.Extensions.Redaction.IRedactorProvider" in type "{0}"</value>
<value>Found multiple fields of type "Microsoft.Extensions.Redaction.IRedactorProvider" in type "{0}"</value>
</data>
<data name="MultipleRedactorProviderFieldsTitle" xml:space="preserve">
<value>Multiple fields of type "Microsoft.R9.Extensions.Redaction.IRedactorProvider" were found</value>
<value>Multiple fields of type "Microsoft.Extensions.Redaction.IRedactorProvider" were found</value>
</data>
<data name="InvalidTypeToLogPropertiesMessage" xml:space="preserve">
<value>Can't log properties of parameters of type "{0}"</value>
Expand Down
Loading

0 comments on commit 4f2f809

Please sign in to comment.