Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
31 changes: 29 additions & 2 deletions D2L.CodeStyle.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.32112.339
# Visual Studio Version 18
VisualStudioVersion = 18.5.11626.173
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "D2L.CodeStyle.Annotations", "src\D2L.CodeStyle.Annotations\D2L.CodeStyle.Annotations.csproj", "{1812AF7E-59B1-4764-8090-F84446A71C4E}"
EndProject
Expand All @@ -22,6 +22,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
Directory.build.props = Directory.build.props
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "D2L.CodeStyle.Analyzers.Rule.Constant", "src\Rules\D2L.CodeStyle.Analyzers.Rule.Constant\D2L.CodeStyle.Analyzers.Rule.Constant\D2L.CodeStyle.Analyzers.Rule.Constant.csproj", "{9F66950B-83B2-4A9E-8C26-ABAC97E55CA9}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Rules", "Rules", "{1D7FCA55-CA25-4264-BB5D-331CF09C1A00}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "D2L.CodeStyle.Analyzers.Rule", "src\Rules\D2L.CodeStyle.Analyzers.Rule\D2L.CodeStyle.Analyzers.Rule\D2L.CodeStyle.Analyzers.Rule.csproj", "{04534CAC-57B7-4462-BE31-D8C1C6B7BAA7}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Constant", "Constant", "{FE0449B9-2A7B-48DD-BBDB-B6A0FB19A2A9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -78,13 +86,32 @@ Global
{C7B3A703-A549-48AA-90A6-A145C91D4F85}.Release|Any CPU.Build.0 = Release|Any CPU
{C7B3A703-A549-48AA-90A6-A145C91D4F85}.Release|x86.ActiveCfg = Release|Any CPU
{C7B3A703-A549-48AA-90A6-A145C91D4F85}.Release|x86.Build.0 = Release|Any CPU
{9F66950B-83B2-4A9E-8C26-ABAC97E55CA9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9F66950B-83B2-4A9E-8C26-ABAC97E55CA9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9F66950B-83B2-4A9E-8C26-ABAC97E55CA9}.Debug|x86.ActiveCfg = Debug|Any CPU
{9F66950B-83B2-4A9E-8C26-ABAC97E55CA9}.Debug|x86.Build.0 = Debug|Any CPU
{9F66950B-83B2-4A9E-8C26-ABAC97E55CA9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9F66950B-83B2-4A9E-8C26-ABAC97E55CA9}.Release|Any CPU.Build.0 = Release|Any CPU
{9F66950B-83B2-4A9E-8C26-ABAC97E55CA9}.Release|x86.ActiveCfg = Release|Any CPU
{9F66950B-83B2-4A9E-8C26-ABAC97E55CA9}.Release|x86.Build.0 = Release|Any CPU
{04534CAC-57B7-4462-BE31-D8C1C6B7BAA7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{04534CAC-57B7-4462-BE31-D8C1C6B7BAA7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{04534CAC-57B7-4462-BE31-D8C1C6B7BAA7}.Debug|x86.ActiveCfg = Debug|Any CPU
{04534CAC-57B7-4462-BE31-D8C1C6B7BAA7}.Debug|x86.Build.0 = Debug|Any CPU
{04534CAC-57B7-4462-BE31-D8C1C6B7BAA7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{04534CAC-57B7-4462-BE31-D8C1C6B7BAA7}.Release|Any CPU.Build.0 = Release|Any CPU
{04534CAC-57B7-4462-BE31-D8C1C6B7BAA7}.Release|x86.ActiveCfg = Release|Any CPU
{04534CAC-57B7-4462-BE31-D8C1C6B7BAA7}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{1C54E78A-E12B-45C3-BA6E-31DB5C63B389} = {3873E5AD-A61F-4CE4-B2E1-5AD83D25BF79}
{2F10EDAF-8585-4B4C-A82A-BABC5D23A186} = {3873E5AD-A61F-4CE4-B2E1-5AD83D25BF79}
{9F66950B-83B2-4A9E-8C26-ABAC97E55CA9} = {FE0449B9-2A7B-48DD-BBDB-B6A0FB19A2A9}
{04534CAC-57B7-4462-BE31-D8C1C6B7BAA7} = {1D7FCA55-CA25-4264-BB5D-331CF09C1A00}
{FE0449B9-2A7B-48DD-BBDB-B6A0FB19A2A9} = {1D7FCA55-CA25-4264-BB5D-331CF09C1A00}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B1068C74-1517-443E-99B8-CB4834D47628}
Expand Down
18 changes: 17 additions & 1 deletion src/D2L.CodeStyle.Analyzers/D2L.CodeStyle.Analyzers.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<Title>D2L.CodeStyle.Analyzers</Title>
<Product>D2L.CodeStyle</Product>
<Description>D2L.CodeStyle analyzers</Description>
<Version>0.223.0</Version>
<Version>0.224.0</Version>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/Brightspace/D2L.CodeStyle</PackageProjectUrl>
<Authors>D2L</Authors>
Expand All @@ -27,7 +27,14 @@
<ItemGroup>
<None Include="tools\*.ps1" Pack="true" PackagePath="tools" />
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
<None Include="$(OutputPath)\$(AssemblyName).Rule.dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
<None Include="$(OutputPath)\$(AssemblyName).Rule.Constant.dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
</ItemGroup>
<Target Name="ValidateRequiredFiles" BeforeTargets="Build">
<Error Condition="!Exists('$(OutputPath)$(AssemblyName).dll')" Text="Required file '$(OutputPath)$(AssemblyName).dll' is missing." />
<Error Condition="!Exists('$(OutputPath)D2L.CodeStyle.Analyzers.Rule.dll')" Text="Required file '$(OutputPath)D2L.CodeStyle.Analyzers.Rule.dll' is missing." />
<Error Condition="!Exists('$(OutputPath)D2L.CodeStyle.Analyzers.Rule.Constant.dll')" Text="Required file '$(OutputPath)D2L.CodeStyle.Analyzers.Rule.Constant.dll' is missing." />
</Target>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4">
<PrivateAssets>all</PrivateAssets>
Expand All @@ -49,4 +56,13 @@
<CompilerVisibleProperty Include="SuppressSyncGenerator" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Rules\D2L.CodeStyle.Analyzers.Rule.Constant\D2L.CodeStyle.Analyzers.Rule.Constant\D2L.CodeStyle.Analyzers.Rule.Constant.csproj">
<PrivateAssets>all</PrivateAssets>
</ProjectReference>
<ProjectReference Include="..\Rules\D2L.CodeStyle.Analyzers.Rule\D2L.CodeStyle.Analyzers.Rule\D2L.CodeStyle.Analyzers.Rule.csproj">
<PrivateAssets>all</PrivateAssets>
</ProjectReference>
</ItemGroup>
Comment thread
omsmith marked this conversation as resolved.

</Project>
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<Title>D2L.CodeStyle.TestAnalyzers</Title>
<Product>D2L.CodeStyle</Product>
<Description>D2L.CodeStyle test analyzers</Description>
<Version>0.29.0</Version>
<Version>0.30.0</Version>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/Brightspace/D2L.CodeStyle</PackageProjectUrl>
<Authors>D2L</Authors>
Expand All @@ -26,8 +26,16 @@
<ItemGroup>
<None Include="tools\*.ps1" Pack="true" PackagePath="tools" />
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
<None Include="$(OutputPath)\D2L.CodeStyle.Analyzers.Rule.dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
<None Include="$(OutputPath)\D2L.CodeStyle.Analyzers.Rule.Constant.dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
</ItemGroup>

<Target Name="ValidateRequiredFiles" BeforeTargets="Build">
<Error Condition="!Exists('$(OutputPath)$(AssemblyName).dll')" Text="Required file '$(OutputPath)$(AssemblyName).dll' is missing." />
<Error Condition="!Exists('$(OutputPath)D2L.CodeStyle.Analyzers.Rule.dll')" Text="Required file '$(OutputPath)D2L.CodeStyle.Analyzers.Rule.dll' is missing." />
<Error Condition="!Exists('$(OutputPath)D2L.CodeStyle.Analyzers.Rule.Constant.dll')" Text="Required file '$(OutputPath)D2L.CodeStyle.Analyzers.Rule.Constant.dll' is missing." />
</Target>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4">
<PrivateAssets>all</PrivateAssets>
Expand All @@ -41,4 +49,13 @@
<PackageReference Include="System.Collections.Immutable" Version="7.0.0" PrivateAssets="All" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Rules\D2L.CodeStyle.Analyzers.Rule.Constant\D2L.CodeStyle.Analyzers.Rule.Constant\D2L.CodeStyle.Analyzers.Rule.Constant.csproj">
<PrivateAssets>all</PrivateAssets>
</ProjectReference>
<ProjectReference Include="..\Rules\D2L.CodeStyle.Analyzers.Rule\D2L.CodeStyle.Analyzers.Rule\D2L.CodeStyle.Analyzers.Rule.csproj">
<PrivateAssets>all</PrivateAssets>
</ProjectReference>
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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;
}

Expand Down Expand Up @@ -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;
}

Expand All @@ -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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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 #if D2L_INCLUDE_TEST_CHECKS and pass that via <AdditionalProperties> in the ProjectReference, but I couldn't figure it out quickly (and may not work having two versions of the same DLL loaded into analyzers)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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 ) ) {
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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
Expand Down
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."
)]
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace Microsoft.CodeAnalysis {

internal static class ReportDiagnosticExtensions {
public static class ReportDiagnosticExtensions {

public static void ReportDiagnostic(
this SourceProductionContext context,
Expand All @@ -28,7 +28,7 @@ public static void ReportDiagnostic(

namespace Microsoft.CodeAnalysis.Diagnostics {

internal static class ReportDiagnosticExtensions {
public static class ReportDiagnosticExtensions {

public static void ReportDiagnostic(
this CompilationAnalysisContext context,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net48</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
Expand All @@ -16,6 +17,8 @@
<ProjectReference Include="..\..\src\D2L.CodeStyle.Analyzers\D2L.CodeStyle.Analyzers.csproj" />
<ProjectReference Include="..\..\src\D2L.CodeStyle.Annotations\D2L.CodeStyle.Annotations.csproj" />
<ProjectReference Include="..\..\src\D2L.CodeStyle.SpecTests\D2L.CodeStyle.SpecTests.csproj" />
<ProjectReference Include="..\..\src\Rules\D2L.CodeStyle.Analyzers.Rule.Constant\D2L.CodeStyle.Analyzers.Rule.Constant\D2L.CodeStyle.Analyzers.Rule.Constant.csproj" />
<ProjectReference Include="..\..\src\Rules\D2L.CodeStyle.Analyzers.Rule\D2L.CodeStyle.Analyzers.Rule\D2L.CodeStyle.Analyzers.Rule.csproj" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// analyzer: D2L.CodeStyle.Analyzers.ApiUsage.ConstantAttributeAnalyzer, D2L.CodeStyle.Analyzers
// analyzer: D2L.CodeStyle.Analyzers.ApiUsage.ConstantAttributeAnalyzer, D2L.CodeStyle.Analyzers.Rule.Constant

using System;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net48</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
Expand Down
Loading