diff --git a/D2L.CodeStyle.sln b/D2L.CodeStyle.sln
index f1c91aba..eafd8ee7 100644
--- a/D2L.CodeStyle.sln
+++ b/D2L.CodeStyle.sln
@@ -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
@@ -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
@@ -78,6 +86,22 @@ 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
@@ -85,6 +109,9 @@ Global
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}
diff --git a/src/D2L.CodeStyle.Analyzers/D2L.CodeStyle.Analyzers.csproj b/src/D2L.CodeStyle.Analyzers/D2L.CodeStyle.Analyzers.csproj
index 22a53545..48d1e0ce 100644
--- a/src/D2L.CodeStyle.Analyzers/D2L.CodeStyle.Analyzers.csproj
+++ b/src/D2L.CodeStyle.Analyzers/D2L.CodeStyle.Analyzers.csproj
@@ -6,7 +6,7 @@
D2L.CodeStyle.Analyzers
D2L.CodeStyle
D2L.CodeStyle analyzers
- 0.223.0
+ 0.224.0
Apache-2.0
https://github.com/Brightspace/D2L.CodeStyle
D2L
@@ -27,7 +27,14 @@
+
+
+
+
+
+
+
all
@@ -49,4 +56,13 @@
+
+
+ all
+
+
+ all
+
+
+
diff --git a/src/D2L.CodeStyle.SpecTests/D2L.CodeStyle.SpecTests.csproj b/src/D2L.CodeStyle.SpecTests/D2L.CodeStyle.SpecTests.csproj
index 790fa362..9a804523 100644
--- a/src/D2L.CodeStyle.SpecTests/D2L.CodeStyle.SpecTests.csproj
+++ b/src/D2L.CodeStyle.SpecTests/D2L.CodeStyle.SpecTests.csproj
@@ -1,4 +1,4 @@
-
+
netstandard2.0
diff --git a/src/D2L.CodeStyle.TestAnalyzers/D2L.CodeStyle.TestAnalyzers.csproj b/src/D2L.CodeStyle.TestAnalyzers/D2L.CodeStyle.TestAnalyzers.csproj
index fc80de0b..9458317a 100644
--- a/src/D2L.CodeStyle.TestAnalyzers/D2L.CodeStyle.TestAnalyzers.csproj
+++ b/src/D2L.CodeStyle.TestAnalyzers/D2L.CodeStyle.TestAnalyzers.csproj
@@ -6,7 +6,7 @@
D2L.CodeStyle.TestAnalyzers
D2L.CodeStyle
D2L.CodeStyle test analyzers
- 0.29.0
+ 0.30.0
Apache-2.0
https://github.com/Brightspace/D2L.CodeStyle
D2L
@@ -26,8 +26,16 @@
+
+
+
+
+
+
+
+
all
@@ -41,4 +49,13 @@
+
+
+ all
+
+
+ all
+
+
+
diff --git a/src/D2L.CodeStyle.Analyzers/ApiUsage/ConstantAttributeAnalyzer.cs b/src/Rules/D2L.CodeStyle.Analyzers.Rule.Constant/D2L.CodeStyle.Analyzers.Rule.Constant/ConstantAttributeAnalyzer.cs
similarity index 76%
rename from src/D2L.CodeStyle.Analyzers/ApiUsage/ConstantAttributeAnalyzer.cs
rename to src/Rules/D2L.CodeStyle.Analyzers.Rule.Constant/D2L.CodeStyle.Analyzers.Rule.Constant/ConstantAttributeAnalyzer.cs
index 79490eae..942b2359 100644
--- a/src/D2L.CodeStyle.Analyzers/ApiUsage/ConstantAttributeAnalyzer.cs
+++ b/src/Rules/D2L.CodeStyle.Analyzers.Rule.Constant/D2L.CodeStyle.Analyzers.Rule.Constant/ConstantAttributeAnalyzer.cs
@@ -10,6 +10,7 @@
namespace D2L.CodeStyle.Analyzers.ApiUsage {
[DiagnosticAnalyzer( LanguageNames.CSharp )]
public sealed class ConstantAttributeAnalyzer : DiagnosticAnalyzer {
+
public override ImmutableArray 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.Is.Anything), so do nothing
+ if( IsMockArgumentConstraint( argument.Value ) ) {
+ return;
+ }
+
// 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.Is.Anything or Arg.Is.Equal(...) (Rhino Mocks)
+ // Arg.Is.Anything is a property chain: Arg.Is (static) -> .Anything (instance)
+ // Arg.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 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
+ return IsArgType( type.ContainingType );
+ }
+
private static bool IsStringEmpty( IOperation operation ) {
if( operation is IFieldReferenceOperation fieldRef ) {
return fieldRef.Field.ContainingType.SpecialType == SpecialType.System_String
diff --git a/src/Rules/D2L.CodeStyle.Analyzers.Rule.Constant/D2L.CodeStyle.Analyzers.Rule.Constant/D2L.CodeStyle.Analyzers.Rule.Constant.csproj b/src/Rules/D2L.CodeStyle.Analyzers.Rule.Constant/D2L.CodeStyle.Analyzers.Rule.Constant/D2L.CodeStyle.Analyzers.Rule.Constant.csproj
new file mode 100644
index 00000000..c48bb3e7
--- /dev/null
+++ b/src/Rules/D2L.CodeStyle.Analyzers.Rule.Constant/D2L.CodeStyle.Analyzers.Rule.Constant/D2L.CodeStyle.Analyzers.Rule.Constant.csproj
@@ -0,0 +1,31 @@
+
+
+
+ netstandard2.0
+ true
+ true
+ false
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+
diff --git a/src/Rules/D2L.CodeStyle.Analyzers.Rule/D2L.CodeStyle.Analyzers.Rule/D2L.CodeStyle.Analyzers.Rule.csproj b/src/Rules/D2L.CodeStyle.Analyzers.Rule/D2L.CodeStyle.Analyzers.Rule/D2L.CodeStyle.Analyzers.Rule.csproj
new file mode 100644
index 00000000..404aae11
--- /dev/null
+++ b/src/Rules/D2L.CodeStyle.Analyzers.Rule/D2L.CodeStyle.Analyzers.Rule/D2L.CodeStyle.Analyzers.Rule.csproj
@@ -0,0 +1,23 @@
+
+
+
+ netstandard2.0
+ false
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
diff --git a/src/D2L.CodeStyle.Analyzers/Diagnostics.cs b/src/Rules/D2L.CodeStyle.Analyzers.Rule/D2L.CodeStyle.Analyzers.Rule/Diagnostics.cs
similarity index 100%
rename from src/D2L.CodeStyle.Analyzers/Diagnostics.cs
rename to src/Rules/D2L.CodeStyle.Analyzers.Rule/D2L.CodeStyle.Analyzers.Rule/Diagnostics.cs
diff --git a/src/Rules/D2L.CodeStyle.Analyzers.Rule/D2L.CodeStyle.Analyzers.Rule/GlobalSuppressions.cs b/src/Rules/D2L.CodeStyle.Analyzers.Rule/D2L.CodeStyle.Analyzers.Rule/GlobalSuppressions.cs
new file mode 100644
index 00000000..dded2a97
--- /dev/null
+++ b/src/Rules/D2L.CodeStyle.Analyzers.Rule/D2L.CodeStyle.Analyzers.Rule/GlobalSuppressions.cs
@@ -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."
+)]
diff --git a/src/D2L.CodeStyle.Analyzers/Extensions/ReportDiagnosticExtensions.cs b/src/Rules/D2L.CodeStyle.Analyzers.Rule/D2L.CodeStyle.Analyzers.Rule/ReportDiagnosticExtensions.cs
similarity index 93%
rename from src/D2L.CodeStyle.Analyzers/Extensions/ReportDiagnosticExtensions.cs
rename to src/Rules/D2L.CodeStyle.Analyzers.Rule/D2L.CodeStyle.Analyzers.Rule/ReportDiagnosticExtensions.cs
index 77feaf86..128cc544 100644
--- a/src/D2L.CodeStyle.Analyzers/Extensions/ReportDiagnosticExtensions.cs
+++ b/src/Rules/D2L.CodeStyle.Analyzers.Rule/D2L.CodeStyle.Analyzers.Rule/ReportDiagnosticExtensions.cs
@@ -2,7 +2,7 @@
namespace Microsoft.CodeAnalysis {
- internal static class ReportDiagnosticExtensions {
+ public static class ReportDiagnosticExtensions {
public static void ReportDiagnostic(
this SourceProductionContext context,
@@ -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,
diff --git a/tests/D2L.CodeStyle.Analyzers.Test/D2L.CodeStyle.Analyzers.Tests.csproj b/tests/D2L.CodeStyle.Analyzers.Test/D2L.CodeStyle.Analyzers.Tests.csproj
index 0722e33f..f699d725 100644
--- a/tests/D2L.CodeStyle.Analyzers.Test/D2L.CodeStyle.Analyzers.Tests.csproj
+++ b/tests/D2L.CodeStyle.Analyzers.Test/D2L.CodeStyle.Analyzers.Tests.csproj
@@ -1,6 +1,7 @@
net48
+ false
@@ -16,6 +17,8 @@
+
+
diff --git a/tests/D2L.CodeStyle.Analyzers.Test/Specs/ConstantAttributeAnalyzer.cs b/tests/D2L.CodeStyle.Analyzers.Test/Specs/ConstantAttributeAnalyzer.cs
index bc39f0af..08655493 100644
--- a/tests/D2L.CodeStyle.Analyzers.Test/Specs/ConstantAttributeAnalyzer.cs
+++ b/tests/D2L.CodeStyle.Analyzers.Test/Specs/ConstantAttributeAnalyzer.cs
@@ -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;
diff --git a/tests/D2L.CodeStyle.TestAnalyzers.Tests/D2L.CodeStyle.TestAnalyzers.Tests.csproj b/tests/D2L.CodeStyle.TestAnalyzers.Tests/D2L.CodeStyle.TestAnalyzers.Tests.csproj
index 1b993201..7dbfb0a3 100644
--- a/tests/D2L.CodeStyle.TestAnalyzers.Tests/D2L.CodeStyle.TestAnalyzers.Tests.csproj
+++ b/tests/D2L.CodeStyle.TestAnalyzers.Tests/D2L.CodeStyle.TestAnalyzers.Tests.csproj
@@ -1,6 +1,7 @@
net48
+ false