-
Notifications
You must be signed in to change notification settings - Fork 20
SPLA-4322 - Sharing the constant analyzer between regular and test analyzers #981
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
Changes from all commits
267aa40
eec2eb4
fa1a7b7
53e5f30
03e40c8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,6 +10,7 @@ | |
| namespace D2L.CodeStyle.Analyzers.ApiUsage { | ||
| [DiagnosticAnalyzer( LanguageNames.CSharp )] | ||
| public sealed class ConstantAttributeAnalyzer : DiagnosticAnalyzer { | ||
|
|
||
| public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => | ||
| ImmutableArray.Create( | ||
| Diagnostics.NonConstantPassedToConstantParameter, | ||
|
|
@@ -31,7 +32,7 @@ CompilationStartAnalysisContext context | |
| ); | ||
|
|
||
| // The D2L.CodeStyle.Annotations reference is optional | ||
| if ( constantAttribute == null || constantAttribute .Kind == SymbolKind.ErrorType ) { | ||
| if( constantAttribute == null || constantAttribute.Kind == SymbolKind.ErrorType ) { | ||
| return; | ||
| } | ||
|
|
||
|
|
@@ -123,7 +124,7 @@ ISymbol constantAttribute | |
| var parameter = argument.Parameter; | ||
|
|
||
| // Parameter is not [Constant], so do nothing | ||
| if( !HasAttribute( parameter, constantAttribute ) ) { | ||
| if( !HasAttribute( parameter, constantAttribute )) { | ||
| return; | ||
| } | ||
|
|
||
|
|
@@ -132,6 +133,16 @@ ISymbol constantAttribute | |
| return; | ||
| } | ||
|
|
||
| // Argument is inside an expression tree (e.g. Moq Verify/Setup), so do nothing | ||
| if( IsInsideExpressionTree( argument ) ) { | ||
| return; | ||
| } | ||
|
|
||
| // Argument is a mock argument constraint (e.g. Arg<string>.Is.Anything), so do nothing | ||
| if( IsMockArgumentConstraint( argument.Value ) ) { | ||
| return; | ||
| } | ||
|
Comment on lines
+136
to
+144
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was wondering if there was a way we could make this toggleable, like
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could do it via AnalyzerOptions, but, follow-up-able |
||
|
|
||
| // Argument was defined as [Constant] already, so trust it | ||
| var argumentSymbol = argument.SemanticModel.GetSymbolInfo( argument.Value.Syntax, context.CancellationToken ).Symbol; | ||
| if( argumentSymbol != null && HasAttribute( argumentSymbol, constantAttribute ) ) { | ||
|
|
@@ -161,7 +172,7 @@ INamedTypeSymbol constantAttribute | |
| } | ||
|
|
||
| // Operator parameter is not [Constant], so do nothing | ||
| IParameterSymbol parameter = @operator.Parameters[ 0 ]; | ||
| IParameterSymbol parameter = @operator.Parameters[0]; | ||
| if( !HasAttribute( parameter, constantAttribute ) ) { | ||
| return; | ||
| } | ||
|
|
@@ -258,6 +269,58 @@ ISymbol attributeSymbol | |
| ); | ||
| } | ||
|
|
||
| private static bool IsInsideExpressionTree( IOperation operation ) { | ||
| IOperation current = operation; | ||
| while( current != null ) { | ||
| if( current is IConversionOperation conversion | ||
| && conversion.Type is INamedTypeSymbol namedType | ||
| && namedType.BaseType != null | ||
| && namedType.BaseType.Name == "LambdaExpression" | ||
| ) { | ||
| return true; | ||
| } | ||
| current = current.Parent; | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| private static bool IsMockArgumentConstraint( IOperation operation ) { | ||
| // Matches patterns like Arg<T>.Is.Anything or Arg<T>.Is.Equal(...) (Rhino Mocks) | ||
| // Arg<T>.Is.Anything is a property chain: Arg<string>.Is (static) -> .Anything (instance) | ||
| // Arg<T>.Is.Equal(...) is a method call on the .Is property result | ||
| if( operation is IInvocationOperation invocation ) { | ||
| var containingType = invocation.TargetMethod.ContainingType; | ||
| if( IsArgType( containingType ) ) { | ||
| return true; | ||
| } | ||
| // Check if the instance receiver is an Arg<T> property chain | ||
| if( invocation.Instance != null && IsMockArgumentConstraint( invocation.Instance ) ) { | ||
| return true; | ||
| } | ||
| } | ||
|
|
||
| IOperation current = operation; | ||
| while( current is IPropertyReferenceOperation propertyRef ) { | ||
| var containingType = propertyRef.Property.ContainingType; | ||
| if( IsArgType( containingType ) ) { | ||
| return true; | ||
| } | ||
| current = propertyRef.Instance; | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| private static bool IsArgType( INamedTypeSymbol type ) { | ||
| if( type == null ) { | ||
| return false; | ||
| } | ||
| if( type.Name == "Arg" && type.IsGenericType ) { | ||
| return true; | ||
| } | ||
| // Check containing types for nested types within Arg<T> | ||
| return IsArgType( type.ContainingType ); | ||
| } | ||
|
|
||
| private static bool IsStringEmpty( IOperation operation ) { | ||
| if( operation is IFieldReferenceOperation fieldRef ) { | ||
| return fieldRef.Field.ContainingType.SpecialType == SpecialType.System_String | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
|
|
||
| <PropertyGroup> | ||
| <TargetFramework>netstandard2.0</TargetFramework> | ||
| <EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules> | ||
| <TreatWarningsAsErrors>true</TreatWarningsAsErrors> | ||
| <IsPackable>false</IsPackable> | ||
| </PropertyGroup> | ||
|
|
||
| <ItemGroup> | ||
| <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4"> | ||
| <PrivateAssets>all</PrivateAssets> | ||
| <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
| </PackageReference> | ||
| <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.7.0" PrivateAssets="All" /> | ||
| <PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="6.0.0"> | ||
| <PrivateAssets>all</PrivateAssets> | ||
| <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
| </PackageReference> | ||
| <PackageReference Include="Nullable" Version="1.3.0"> | ||
| <PrivateAssets>all</PrivateAssets> | ||
| <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
| </PackageReference> | ||
| <PackageReference Include="System.Collections.Immutable" Version="7.0.0" PrivateAssets="All" /> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <ProjectReference Include="..\..\D2L.CodeStyle.Analyzers.Rule\D2L.CodeStyle.Analyzers.Rule\D2L.CodeStyle.Analyzers.Rule.csproj" /> | ||
| </ItemGroup> | ||
|
|
||
| </Project> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
|
|
||
| <PropertyGroup> | ||
| <TargetFramework>netstandard2.0</TargetFramework> | ||
| <IsPackable>false</IsPackable> | ||
| </PropertyGroup> | ||
| <ItemGroup> | ||
| <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4"> | ||
| <PrivateAssets>all</PrivateAssets> | ||
| <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
| </PackageReference> | ||
| <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.7.0" PrivateAssets="All" /> | ||
| <PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="6.0.0"> | ||
| <PrivateAssets>all</PrivateAssets> | ||
| <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
| </PackageReference> | ||
| <PackageReference Include="Nullable" Version="1.3.0"> | ||
| <PrivateAssets>all</PrivateAssets> | ||
| <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
| </PackageReference> | ||
| <PackageReference Include="System.Collections.Immutable" Version="7.0.0" PrivateAssets="All" /> | ||
| </ItemGroup> | ||
| </Project> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| // This file is used by Code Analysis to maintain SuppressMessage | ||
| // attributes that are applied to this project. | ||
| // Project-level suppressions either have no target or are given | ||
| // a specific target and scoped to a namespace, type, member, etc. | ||
|
|
||
| using System.Diagnostics.CodeAnalysis; | ||
|
|
||
| [assembly: SuppressMessage( | ||
| "MicrosoftCodeAnalysisReleaseTracking", | ||
| "RS2008:Enable analyzer release tracking", | ||
| Justification = "Not maintaing a releases file at this time." | ||
| )] |
Uh oh!
There was an error while loading. Please reload this page.