diff --git a/Philips.CodeAnalysis.Common/ParameterPredicates.cs b/Philips.CodeAnalysis.Common/ParameterPredicates.cs
new file mode 100644
index 000000000..f8721e575
--- /dev/null
+++ b/Philips.CodeAnalysis.Common/ParameterPredicates.cs
@@ -0,0 +1,19 @@
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace Philips.CodeAnalysis.Common
+{
+ public static class ParameterPredicates
+ {
+ public static bool IsEnum(this ParameterSyntax p, SyntaxNodeAnalysisContext context)
+ {
+ if (context.SemanticModel.GetDeclaredSymbol(p).Type is ITypeSymbol typ)
+ {
+ return typ.TypeKind == TypeKind.Enum;
+ }
+ return false;
+ }
+ }
+}
diff --git a/Philips.CodeAnalysis.Common/Philips.CodeAnalysis.Common.csproj b/Philips.CodeAnalysis.Common/Philips.CodeAnalysis.Common.csproj
index 4b713672d..07c52838b 100644
--- a/Philips.CodeAnalysis.Common/Philips.CodeAnalysis.Common.csproj
+++ b/Philips.CodeAnalysis.Common/Philips.CodeAnalysis.Common.csproj
@@ -12,6 +12,7 @@
1.0.3.0
+
diff --git a/Philips.CodeAnalysis.Common/SingleDiagnosticAnalyzer{TU}.cs b/Philips.CodeAnalysis.Common/SingleDiagnosticAnalyzer{TU}.cs
index 76136620d..0a60b821a 100644
--- a/Philips.CodeAnalysis.Common/SingleDiagnosticAnalyzer{TU}.cs
+++ b/Philips.CodeAnalysis.Common/SingleDiagnosticAnalyzer{TU}.cs
@@ -1,11 +1,15 @@
// © 2023 Koninklijke Philips N.V. See License.md in the project root for license information.
using System;
+using System.Linq;
+using System.Collections.Generic;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
+using static LanguageExt.Prelude;
+
namespace Philips.CodeAnalysis.Common
{
public abstract class SingleDiagnosticAnalyzer : SingleDiagnosticAnalyzer where T : SyntaxNode where TSyntaxNodeAction : SyntaxNodeAction, new()
@@ -39,26 +43,23 @@ public override void Initialize(AnalysisContext context)
return;
}
- startContext.RegisterSyntaxNodeAction(StartAnalysis, syntaxKind);
+ startContext.RegisterSyntaxNodeAction(RunAnalysis, syntaxKind);
});
}
- private void StartAnalysis(SyntaxNodeAnalysisContext context)
+ private void RunAnalysis(SyntaxNodeAnalysisContext context)
{
- GeneratedCodeDetector generatedCodeDetector = new();
- if (generatedCodeDetector.IsGeneratedCode(context))
- {
- return;
- }
-
- TSyntaxNodeAction syntaxNodeAction = new()
- {
- Context = context,
- Node = (T)context.Node,
- Rule = Rule,
- Analyzer = this,
- };
- syntaxNodeAction.Analyze();
+ _ = new List { new() }
+ .Filter((g) => !g.IsGeneratedCode(context))
+ .Select((g) => new TSyntaxNodeAction()
+ {
+ Context = context,
+ Node = (T)context.Node,
+ Rule = Rule,
+ Analyzer = this,
+ })
+ .SelectMany((s) => s.Analyze())
+ .Iter(context.ReportDiagnostic);
}
protected virtual SyntaxKind GetSyntaxKind()
diff --git a/Philips.CodeAnalysis.Common/SyntaxNodeAction.cs b/Philips.CodeAnalysis.Common/SyntaxNodeAction.cs
index f1e5296de..2024ca981 100644
--- a/Philips.CodeAnalysis.Common/SyntaxNodeAction.cs
+++ b/Philips.CodeAnalysis.Common/SyntaxNodeAction.cs
@@ -1,5 +1,6 @@
// © 2023 Koninklijke Philips N.V. See License.md in the project root for license information.
+using System.Collections.Generic;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
@@ -14,12 +15,11 @@ public abstract class SyntaxNodeAction where T : SyntaxNode
protected Helper Helper { get; init; } = new Helper();
- public abstract void Analyze();
+ public abstract IEnumerable Analyze();
- public void ReportDiagnostic(Location location = null, params object[] messageArgs)
+ public Diagnostic PrepareDiagnostic(Location location = null, params object[] messageArgs)
{
- var diagnostic = Diagnostic.Create(Rule, location, messageArgs);
- Context.ReportDiagnostic(diagnostic);
+ return Diagnostic.Create(Rule, location, messageArgs);
}
}
}
diff --git a/Philips.CodeAnalysis.MaintainabilityAnalyzers/Cardinality/AvoidEnumParametersAnalyzer.cs b/Philips.CodeAnalysis.MaintainabilityAnalyzers/Cardinality/AvoidEnumParametersAnalyzer.cs
index 334f32981..b6806e0be 100644
--- a/Philips.CodeAnalysis.MaintainabilityAnalyzers/Cardinality/AvoidEnumParametersAnalyzer.cs
+++ b/Philips.CodeAnalysis.MaintainabilityAnalyzers/Cardinality/AvoidEnumParametersAnalyzer.cs
@@ -11,6 +11,7 @@
using Philips.CodeAnalysis.Common;
using static LanguageExt.Prelude;
+using LanguageExt;
namespace Philips.CodeAnalysis.MaintainabilityAnalyzers.Cardinality
{
@@ -29,25 +30,20 @@ public AvoidEnumParametersAnalyzer()
public class AvoidEnumParametersSyntaxNodeAction : SyntaxNodeAction
{
- public override void Analyze()
+ public override IEnumerable Analyze()
{
- _ = List(Node)
+ return List(Node)
.Filter((m) => !m.IsOverridden())
- .SelectMany(AnalyzeMethodParameters)
- .Iter(Context.ReportDiagnostic);
+ .SelectMany(AnalyzeMethodParameters);
}
private IEnumerable AnalyzeMethodParameters(MethodDeclarationSyntax m)
{
- return m.ParameterList.Parameters.SelectMany((p) => AnalyzeParam(m, p));
+ return m.ParameterList.Parameters
+ .Filter((p) => p.IsEnum(Context))
+ .Select((p) => m.CreateDiagnostic(Rule));
}
- private Optional AnalyzeParam(MethodDeclarationSyntax m, ParameterSyntax p)
- {
- return Optional(Context.SemanticModel.GetDeclaredSymbol(p).Type)
- .Select((t) => m.CreateDiagnostic(Rule));
-
- }
}
}
diff --git a/Philips.CodeAnalysis.MaintainabilityAnalyzers/Cardinality/AvoidVoidReturnAnalyzer.cs b/Philips.CodeAnalysis.MaintainabilityAnalyzers/Cardinality/AvoidVoidReturnAnalyzer.cs
index b6dfa42c0..da1c72e3d 100644
--- a/Philips.CodeAnalysis.MaintainabilityAnalyzers/Cardinality/AvoidVoidReturnAnalyzer.cs
+++ b/Philips.CodeAnalysis.MaintainabilityAnalyzers/Cardinality/AvoidVoidReturnAnalyzer.cs
@@ -1,5 +1,6 @@
// © 2023 Koninklijke Philips N.V. See License.md in the project root for license information.
+using System.Collections.Generic;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
@@ -23,13 +24,12 @@ public AvoidVoidReturnAnalyzer()
public class AvoidVoidReturnSyntaxNodeAction : SyntaxNodeAction
{
- public override void Analyze()
+ public override IEnumerable Analyze()
{
- _ = Optional(Node)
+ return Optional(Node)
.Filter((m) => m.ReturnsVoid())
.Filter((m) => !m.IsOverridden())
- .Select((m) => m.CreateDiagnostic(Rule))
- .Iter(Context.ReportDiagnostic);
+ .Select((m) => m.CreateDiagnostic(Rule));
}
}
}
diff --git a/Philips.CodeAnalysis.MaintainabilityAnalyzers/Cardinality/MethodPredicates.cs b/Philips.CodeAnalysis.MaintainabilityAnalyzers/Cardinality/MethodPredicates.cs
index c15f911df..ce454afa9 100644
--- a/Philips.CodeAnalysis.MaintainabilityAnalyzers/Cardinality/MethodPredicates.cs
+++ b/Philips.CodeAnalysis.MaintainabilityAnalyzers/Cardinality/MethodPredicates.cs
@@ -12,8 +12,8 @@
namespace Philips.CodeAnalysis.MaintainabilityAnalyzers.Cardinality
{
- public static class MethodPredicates
- {
+ public static class MethodPredicates
+ {
public static bool IsNotOverridenMethod(MethodDeclarationSyntax m)
{
return !m.Modifiers.Any(SyntaxKind.OverrideKeyword);
@@ -34,5 +34,5 @@ public static (SyntaxToken MethodId, PredefinedTypeSyntax ReturnType) MethodRetu
return m.ParameterList.Parameters.Select((p) => (m.Identifier.Text, p, context.SemanticModel.GetDeclaredSymbol(p)));
}
- }
+ }
}
diff --git a/Philips.CodeAnalysis.MaintainabilityAnalyzers/Documentation/CopyrightPresentAnalyzer.cs b/Philips.CodeAnalysis.MaintainabilityAnalyzers/Documentation/CopyrightPresentAnalyzer.cs
index 3958fe5ba..57418fa1c 100644
--- a/Philips.CodeAnalysis.MaintainabilityAnalyzers/Documentation/CopyrightPresentAnalyzer.cs
+++ b/Philips.CodeAnalysis.MaintainabilityAnalyzers/Documentation/CopyrightPresentAnalyzer.cs
@@ -1,9 +1,10 @@
// © 2019 Koninklijke Philips N.V. See License.md in the project root for license information.
using System;
+using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
-
+using LanguageExt;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
@@ -11,6 +12,8 @@
using Microsoft.CodeAnalysis.Text;
using Philips.CodeAnalysis.Common;
+using static LanguageExt.Prelude;
+
namespace Philips.CodeAnalysis.MaintainabilityAnalyzers.Documentation
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
@@ -29,16 +32,17 @@ public class CopyrightPresentSyntaxNodeAction : SyntaxNodeAction Analyze()
{
if (Helper.IsAssemblyInfo(Context) || Helper.HasAutoGeneratedComment(Node))
{
- return;
+ return Option.None;
}
if (Node.FindToken(0).IsKind(SyntaxKind.EndOfFileToken))
{
- return;
+ return Option.None;
+ ;
}
Location location = GetSquiggleLocation(Node.SyntaxTree);
@@ -47,21 +51,22 @@ public override void Analyze()
if (!leadingTrivia.Any(SyntaxKind.SingleLineCommentTrivia) && !leadingTrivia.Any(SyntaxKind.RegionDirectiveTrivia))
{
- ReportDiagnostic(location);
- return;
+ return Optional(PrepareDiagnostic(location));
}
// Special case: there's a #region, and the Copyright is in the name of the region
if (leadingTrivia[0].IsKind(SyntaxKind.RegionDirectiveTrivia) && CheckCopyrightStatement(leadingTrivia[0]))
{
- return;
+ return Option.None;
}
SyntaxTrivia syntaxTrivia = leadingTrivia.FirstOrDefault(t => t.IsKind(SyntaxKind.SingleLineCommentTrivia));
if (!CheckCopyrightStatement(syntaxTrivia))
{
- ReportDiagnostic(location);
+ return Optional(PrepareDiagnostic(location));
}
+
+ return Option.None;
}
private Location GetSquiggleLocation(SyntaxTree tree)
diff --git a/Philips.CodeAnalysis.MaintainabilityAnalyzers/Documentation/DocumentUnhandledExceptionsAnalyzer.cs b/Philips.CodeAnalysis.MaintainabilityAnalyzers/Documentation/DocumentUnhandledExceptionsAnalyzer.cs
index 583944d70..7df973bb3 100644
--- a/Philips.CodeAnalysis.MaintainabilityAnalyzers/Documentation/DocumentUnhandledExceptionsAnalyzer.cs
+++ b/Philips.CodeAnalysis.MaintainabilityAnalyzers/Documentation/DocumentUnhandledExceptionsAnalyzer.cs
@@ -3,11 +3,14 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
+using LanguageExt;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Philips.CodeAnalysis.Common;
+using static LanguageExt.Prelude;
+
namespace Philips.CodeAnalysis.MaintainabilityAnalyzers.Documentation
{
///
@@ -27,11 +30,11 @@ public DocumentUnhandledExceptionsAnalyzer()
public class DocumentUnhandledExceptionsSyntaxNodeAction : SyntaxNodeAction
{
- public override void Analyze()
+ public override IEnumerable Analyze()
{
if (Context.Compilation?.SyntaxTrees.FirstOrDefault()?.Options.DocumentationMode == DocumentationMode.None)
{
- return;
+ return Option.None;
}
IReadOnlyDictionary aliases = Helper.GetUsingAliases(Node);
@@ -62,9 +65,9 @@ public override void Analyze()
var methodName = Node.Identifier.Text;
var remainingExceptionsString = string.Join(",", remainingExceptions);
ImmutableDictionary properties = ImmutableDictionary.Empty.Add(StringConstants.ThrownExceptionPropertyKey, remainingExceptionsString);
- var diagnostic = Diagnostic.Create(Rule, loc, properties, methodName, remainingExceptionsString);
- Context.ReportDiagnostic(diagnostic);
+ return Optional(Diagnostic.Create(Rule, loc, properties, methodName, remainingExceptionsString));
}
+ return Option.None;
}
}
}
diff --git a/Philips.CodeAnalysis.MaintainabilityAnalyzers/Documentation/EnableDocumentationCreationAnalyzer.cs b/Philips.CodeAnalysis.MaintainabilityAnalyzers/Documentation/EnableDocumentationCreationAnalyzer.cs
index ff3fc3dcd..6d96d1d19 100644
--- a/Philips.CodeAnalysis.MaintainabilityAnalyzers/Documentation/EnableDocumentationCreationAnalyzer.cs
+++ b/Philips.CodeAnalysis.MaintainabilityAnalyzers/Documentation/EnableDocumentationCreationAnalyzer.cs
@@ -1,10 +1,14 @@
// © 2023 Koninklijke Philips N.V. See License.md in the project root for license information.
+using System.Collections.Generic;
+using LanguageExt;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Philips.CodeAnalysis.Common;
+using static LanguageExt.Prelude;
+
namespace Philips.CodeAnalysis.MaintainabilityAnalyzers.Documentation
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
@@ -21,12 +25,13 @@ public EnableDocumentationCreationAnalyzer()
public class EnableDocumentationCreationAction : SyntaxNodeAction
{
- public override void Analyze()
+ public override IEnumerable Analyze()
{
if (Node.SyntaxTree.Options.DocumentationMode == DocumentationMode.None)
{
- ReportDiagnostic();
+ return Optional(PrepareDiagnostic());
}
+ return Option.None;
}
}
}
diff --git a/Philips.CodeAnalysis.MaintainabilityAnalyzers/Documentation/OrderPropertyAccessorsAnalyzer.cs b/Philips.CodeAnalysis.MaintainabilityAnalyzers/Documentation/OrderPropertyAccessorsAnalyzer.cs
index 3eedc3a86..403f5c91a 100644
--- a/Philips.CodeAnalysis.MaintainabilityAnalyzers/Documentation/OrderPropertyAccessorsAnalyzer.cs
+++ b/Philips.CodeAnalysis.MaintainabilityAnalyzers/Documentation/OrderPropertyAccessorsAnalyzer.cs
@@ -1,11 +1,16 @@
// © 2019 Koninklijke Philips N.V. See License.md in the project root for license information.
+using System.Collections.Generic;
+using System.Linq;
+using LanguageExt;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Philips.CodeAnalysis.Common;
+using static LanguageExt.Prelude;
+
namespace Philips.CodeAnalysis.MaintainabilityAnalyzers.Documentation
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
@@ -21,40 +26,31 @@ public OrderPropertyAccessorsAnalyzer()
public class OrderPropertyAccessorsSyntaxNodeAction : SyntaxNodeAction
{
- public override void Analyze()
+ public override IEnumerable Analyze()
{
- AccessorListSyntax accessors = Node.AccessorList;
-
- if (accessors is null)
- {
- return;
- }
-
- var getIndex = -1;
- var setIndex = int.MaxValue;
+ return Optional(Node.AccessorList)
+ .SelectMany(accsessors =>
+ accsessors.Accessors
+ .Fold(Option.None, ReduceSetIsBeforeGet)
+ .Filter(setIsBeforeGet => setIsBeforeGet)
+ .Select((setIsBeforeGet) => PrepareDiagnostic(accsessors.GetLocation()))
+ );
+ }
- for (var i = 0; i < accessors.Accessors.Count; i++)
+ private static Option ReduceSetIsBeforeGet(Option setIsBeforeGet, AccessorDeclarationSyntax accessor)
+ {
+ if (setIsBeforeGet.IsNone)
{
- AccessorDeclarationSyntax accessor = accessors.Accessors[i];
-
if (accessor.Keyword.IsKind(SyntaxKind.GetKeyword))
{
- getIndex = i;
- continue;
+ return Optional(false);
}
-
- // SyntaxKind.InitKeyword doesn't exist in the currently used version of Roslyn (it exists in at least 3.9.0)
- if (accessor.Keyword.IsKind(SyntaxKind.SetKeyword) || accessor.Keyword.Text == "init")
+ else if (accessor.Keyword.IsKind(SyntaxKind.SetKeyword) || accessor.Keyword.Text == "init")
{
- setIndex = i;
+ return Optional(true);
}
}
-
- if (setIndex < getIndex)
- {
- Location location = accessors.GetLocation();
- ReportDiagnostic(location);
- }
+ return setIsBeforeGet;
}
}
}
diff --git a/Philips.CodeAnalysis.MaintainabilityAnalyzers/Documentation/RemoveCommentedCodeAnalyzer.cs b/Philips.CodeAnalysis.MaintainabilityAnalyzers/Documentation/RemoveCommentedCodeAnalyzer.cs
index ae6fa068a..1d97a9c1e 100644
--- a/Philips.CodeAnalysis.MaintainabilityAnalyzers/Documentation/RemoveCommentedCodeAnalyzer.cs
+++ b/Philips.CodeAnalysis.MaintainabilityAnalyzers/Documentation/RemoveCommentedCodeAnalyzer.cs
@@ -1,5 +1,6 @@
// © 2023 Koninklijke Philips N.V. See License.md in the project root for license information.
+using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis;
@@ -26,25 +27,21 @@ public class RemoveCommentedCodeSyntaxNodeAction : SyntaxNodeAction Analyze()
{
- System.Collections.Generic.IEnumerable comments = Node.DescendantTrivia().Where(trivia => trivia.IsKind(SyntaxKind.SingleLineCommentTrivia));
- if (!comments.Any())
- {
- return;
- }
-
- var previousViolationLine = InitialCodeLine;
- foreach (Location location in comments.Where(comment => comment.ToString().EndsWith(";"))
- .Select(node => node.GetLocation()))
- {
- var lineNumber = location.GetLineSpan().StartLinePosition.Line + 1;
- if (lineNumber - previousViolationLine > 1)
+ return Node.DescendantTrivia().Where(trivia => trivia.IsKind(SyntaxKind.SingleLineCommentTrivia))
+ .Where(comment => comment.ToString().EndsWith(";"))
+ .Select(node => node.GetLocation())
+ .Fold<(List Diagnostics, int Line), Location>((new List(), InitialCodeLine),
+ (prevStep, location) =>
{
- ReportDiagnostic(location, lineNumber);
- }
- previousViolationLine = lineNumber;
- }
+ var lineNumber = location.GetLineSpan().StartLinePosition.Line + 1;
+ if (lineNumber - prevStep.Line > 1)
+ {
+ prevStep.Diagnostics.Add(PrepareDiagnostic(location, lineNumber));
+ }
+ return (prevStep.Diagnostics, lineNumber);
+ }).Diagnostics;
}
}
}
diff --git a/Philips.CodeAnalysis.MaintainabilityAnalyzers/Maintainability/AvoidArrayListAnalyzer.cs b/Philips.CodeAnalysis.MaintainabilityAnalyzers/Maintainability/AvoidArrayListAnalyzer.cs
index 642c68db5..334474e85 100644
--- a/Philips.CodeAnalysis.MaintainabilityAnalyzers/Maintainability/AvoidArrayListAnalyzer.cs
+++ b/Philips.CodeAnalysis.MaintainabilityAnalyzers/Maintainability/AvoidArrayListAnalyzer.cs
@@ -1,11 +1,15 @@
// © 2023 Koninklijke Philips N.V. See License.md in the project root for license information.
+using System.Collections.Generic;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Philips.CodeAnalysis.Common;
+using LanguageExt;
+using LanguageExt.SomeHelp;
+
namespace Philips.CodeAnalysis.MaintainabilityAnalyzers.Maintainability
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
@@ -22,7 +26,7 @@ public class AvoidArrayListSyntaxNodeAction : SyntaxNodeAction Analyze()
{
if (Node.Type is not SimpleNameSyntax typeName)
{
@@ -34,13 +38,13 @@ public override void Analyze()
else
{
// Some thing else is mentioned here.
- return;
+ return Option.None;
}
}
if (!typeName.Identifier.Text.Contains("ArrayList"))
{
- return;
+ return Option.None;
}
// Sanity check if we got ArrayList from the correct namespace.
@@ -49,8 +53,9 @@ public override void Analyze()
{
var variableName = Node.Variables.FirstOrDefault()?.Identifier.Text ?? string.Empty;
Location location = typeName.GetLocation();
- ReportDiagnostic(location, variableName);
+ return PrepareDiagnostic(location, variableName).ToSome();
}
+ return Option.None;
}
}
}
diff --git a/Philips.CodeAnalysis.MaintainabilityAnalyzers/Maintainability/AvoidDuplicateStringsAnalyzer.cs b/Philips.CodeAnalysis.MaintainabilityAnalyzers/Maintainability/AvoidDuplicateStringsAnalyzer.cs
index 45e3c61c3..21eacc26f 100644
--- a/Philips.CodeAnalysis.MaintainabilityAnalyzers/Maintainability/AvoidDuplicateStringsAnalyzer.cs
+++ b/Philips.CodeAnalysis.MaintainabilityAnalyzers/Maintainability/AvoidDuplicateStringsAnalyzer.cs
@@ -1,6 +1,7 @@
// © 2023 Koninklijke Philips N.V. See License.md in the project root for license information.
using System.Collections.Concurrent;
+using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.CodeAnalysis;
@@ -9,6 +10,9 @@
using Microsoft.CodeAnalysis.Diagnostics;
using Philips.CodeAnalysis.Common;
+using LanguageExt;
+using LanguageExt.SomeHelp;
+
namespace Philips.CodeAnalysis.MaintainabilityAnalyzers.Maintainability
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
@@ -32,24 +36,24 @@ protected override SyntaxKind GetSyntaxKind()
public class AvoidDuplicateStringsSyntaxNodeAction : SyntaxNodeAction
{
- public override void Analyze()
+ public override IEnumerable Analyze()
{
if (Node.Ancestors().OfType().Any())
{
- return;
+ return Option.None;
}
TestHelper testHelper = new();
if (testHelper.IsInTestClass(Context))
{
- return;
+ return Option.None;
}
SyntaxToken literal = Node.Token;
var literalText = literal.Text.Trim('\\', '\"');
if (string.IsNullOrWhiteSpace(literalText) || literalText.Length <= 2)
{
- return;
+ return Option.None;
}
Location location = literal.GetLocation();
@@ -60,8 +64,9 @@ public override void Analyze()
_ = usedLiterals.TryGetValue(literalText, out Location firstLocation);
var firstFilename = Path.GetFileName(firstLocation.SourceTree.FilePath);
var firstLineNumber = firstLocation.GetLineSpan().StartLinePosition.Line + 1;
- ReportDiagnostic(location, firstFilename, firstLineNumber, literalText);
+ return PrepareDiagnostic(location, firstFilename, firstLineNumber, literalText).ToSome();
}
+ return Option.None;
}
}
}
diff --git a/Philips.CodeAnalysis.MaintainabilityAnalyzers/Maintainability/AvoidEmptyTypeInitializerAnalyzer.cs b/Philips.CodeAnalysis.MaintainabilityAnalyzers/Maintainability/AvoidEmptyTypeInitializerAnalyzer.cs
index bf90e6db0..58d03a368 100644
--- a/Philips.CodeAnalysis.MaintainabilityAnalyzers/Maintainability/AvoidEmptyTypeInitializerAnalyzer.cs
+++ b/Philips.CodeAnalysis.MaintainabilityAnalyzers/Maintainability/AvoidEmptyTypeInitializerAnalyzer.cs
@@ -1,5 +1,8 @@
// © 2019 Koninklijke Philips N.V. See License.md in the project root for license information.
+using System.Collections.Generic;
+using LanguageExt;
+using LanguageExt.SomeHelp;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
@@ -22,28 +25,28 @@ public AvoidEmptyTypeInitializerAnalyzer()
public class AvoidEmptyTypeInitializerSyntaxNodeAction : SyntaxNodeAction
{
- public override void Analyze()
+ public override IEnumerable Analyze()
{
if (!Node.Modifiers.Any(SyntaxKind.StaticKeyword))
{
//not a static constructor
- return;
+ return Option.None;
}
if (Node.Body == null)
{
//during the intellisense phase the body of a constructor can be non-existent.
- return;
+ return Option.None;
}
if (Node.Body.Statements.Any())
{
//not empty
- return;
+ return Option.None;
}
Location location = Node.GetLocation();
- ReportDiagnostic(location);
+ return PrepareDiagnostic(location).ToSome();
}
}
}
diff --git a/Philips.CodeAnalysis.MaintainabilityAnalyzers/Maintainability/AvoidInvocationAsArgumentAnalyzer.cs b/Philips.CodeAnalysis.MaintainabilityAnalyzers/Maintainability/AvoidInvocationAsArgumentAnalyzer.cs
index 6d31979e9..9091a5d27 100644
--- a/Philips.CodeAnalysis.MaintainabilityAnalyzers/Maintainability/AvoidInvocationAsArgumentAnalyzer.cs
+++ b/Philips.CodeAnalysis.MaintainabilityAnalyzers/Maintainability/AvoidInvocationAsArgumentAnalyzer.cs
@@ -1,6 +1,9 @@
// © 2022 Koninklijke Philips N.V. See License.md in the project root for license information.
+using System.Collections.Generic;
using System.Linq;
+using LanguageExt;
+using LanguageExt.SomeHelp;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
@@ -22,45 +25,45 @@ public AvoidInvocationAsArgumentAnalyzer()
public class AvoidInvocationAsArgumentSyntaxNodeAction : SyntaxNodeAction
{
- public override void Analyze()
+ public override IEnumerable Analyze()
{
// We are looking for method calls as arguments
if (Node.Expression is not InvocationExpressionSyntax invocationExpressionSyntax)
{
- return;
+ return Option.None;
}
// If it's an embedded nameof() operation, let it go.
if ((invocationExpressionSyntax.Expression as IdentifierNameSyntax)?.Identifier.Text == "nameof")
{
- return;
+ return Option.None;
}
// If it's calling ToString(), let it go. (ToStrings() cognitive load isn't excessive, and lots of violations)
var methodName = (invocationExpressionSyntax.Expression as MemberAccessExpressionSyntax)?.Name.Identifier.Text;
if (methodName is StringConstants.ToStringMethodName or StringConstants.ToArrayMethodName or StringConstants.ToListMethodName)
{
- return;
+ return Option.None;
}
// If nested calls (e.g., Foo(Bar(Meow()))), only trigger the outer violation Bar(Meow())
if (Node.Ancestors().OfType().Any(arg => !IsStaticMethod(arg.Expression)))
{
- return;
+ return Option.None;
}
// If we're within a constructor initializer (this(...) or base(...) eg), let it go
ConstructorInitializerSyntax constructorInitializerSyntax = Node.Ancestors().OfType().FirstOrDefault();
if (constructorInitializerSyntax != null)
{
- return;
+ return Option.None;
}
// If the caller is Assert, let it go. (This is debatable, and ideally warrants a configuration option.)
var caller = (Node.Parent.Parent as InvocationExpressionSyntax)?.Expression as MemberAccessExpressionSyntax;
if (caller?.Expression is IdentifierNameSyntax identifier && identifier.Identifier.ValueText.Contains(@"Assert"))
{
- return;
+ return Option.None;
}
// If the called method is static, let it go to reduce annoyances. E.g., "Times.Once", "Mock.Of<>", "Marshal.Sizeof", etc.
@@ -70,12 +73,12 @@ public override void Analyze()
var isStatic = IsStaticMethod(callee);
if (isStatic)
{
- return;
+ return Option.None;
}
}
Location location = Node.GetLocation();
- ReportDiagnostic(location, Node.ToString());
+ return PrepareDiagnostic(location, Node.ToString()).ToSome();
}
private bool IsStaticMethod(SyntaxNode node)
diff --git a/Philips.CodeAnalysis.MaintainabilityAnalyzers/Maintainability/AvoidMagicNumbersAnalyzer.cs b/Philips.CodeAnalysis.MaintainabilityAnalyzers/Maintainability/AvoidMagicNumbersAnalyzer.cs
index 69d4d2d05..fa93e77d1 100644
--- a/Philips.CodeAnalysis.MaintainabilityAnalyzers/Maintainability/AvoidMagicNumbersAnalyzer.cs
+++ b/Philips.CodeAnalysis.MaintainabilityAnalyzers/Maintainability/AvoidMagicNumbersAnalyzer.cs
@@ -1,8 +1,11 @@
// © 2023 Koninklijke Philips N.V. See License.md in the project root for license information.
using System;
+using System.Collections.Generic;
using System.Globalization;
using System.Linq;
+using LanguageExt;
+using LanguageExt.SomeHelp;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
@@ -27,45 +30,45 @@ public class AvoidMagicNumbersSyntaxNodeAction : SyntaxNodeAction Analyze()
{
TestHelper helper = new();
if (helper.IsInTestClass(Context))
{
- return;
+ return Option.None;
}
if (!Node.Token.IsKind(SyntaxKind.NumericLiteralToken))
{
- return;
+ return Option.None;
}
if (IsAllowedNumber(Node.Token.Text))
{
- return;
+ return Option.None;
}
// Magic number are allowed in enumerations, as they give meaning to the number.
if (Node.Ancestors().OfType().Any())
{
- return;
+ return Option.None;
}
// If in a field, the magic number should be defined in a static field.
FieldDeclarationSyntax field = Node.Ancestors().OfType().FirstOrDefault();
if (field != null && IsStaticOrConst(field))
{
- return;
+ return Option.None;
}
LocalDeclarationStatementSyntax local = Node.Ancestors().OfType().FirstOrDefault();
if (local != null && local.Modifiers.Any(SyntaxKind.ConstKeyword))
{
- return;
+ return Option.None;
}
Location location = Node.GetLocation();
- ReportDiagnostic(location);
+ return PrepareDiagnostic(location).ToSome();
}
private static bool IsAllowedNumber(string text)
diff --git a/Philips.CodeAnalysis.MaintainabilityAnalyzers/Maintainability/AvoidPragmaAnalyzer.cs b/Philips.CodeAnalysis.MaintainabilityAnalyzers/Maintainability/AvoidPragmaAnalyzer.cs
index b5b7fde51..e81acceff 100644
--- a/Philips.CodeAnalysis.MaintainabilityAnalyzers/Maintainability/AvoidPragmaAnalyzer.cs
+++ b/Philips.CodeAnalysis.MaintainabilityAnalyzers/Maintainability/AvoidPragmaAnalyzer.cs
@@ -1,6 +1,9 @@
// © 2019 Koninklijke Philips N.V. See License.md in the project root for license information.
+using System.Collections.Generic;
using System.Linq;
+using LanguageExt;
+using LanguageExt.SomeHelp;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
@@ -23,18 +26,18 @@ public AvoidPragmaAnalyzer()
public class AvoidPragmaSyntaxNodeAction : SyntaxNodeAction
{
- public override void Analyze()
+ public override IEnumerable Analyze()
{
var myOwnId = Helper.ToDiagnosticId(DiagnosticId.AvoidPragma);
if (Node.ErrorCodes.Where(e => e.IsKind(SyntaxKind.IdentifierName))
.Any(i => i.ToString().Contains(myOwnId)))
{
- return;
+ return Option.None;
}
CSharpSyntaxNode violation = Node;
Location location = violation.GetLocation();
- ReportDiagnostic(location);
+ return PrepareDiagnostic(location).ToSome();
}
}
}
diff --git a/Philips.CodeAnalysis.MaintainabilityAnalyzers/Maintainability/AvoidPrivateKeyPropertyAnalyzer.cs b/Philips.CodeAnalysis.MaintainabilityAnalyzers/Maintainability/AvoidPrivateKeyPropertyAnalyzer.cs
index 6936918d5..f8424938d 100644
--- a/Philips.CodeAnalysis.MaintainabilityAnalyzers/Maintainability/AvoidPrivateKeyPropertyAnalyzer.cs
+++ b/Philips.CodeAnalysis.MaintainabilityAnalyzers/Maintainability/AvoidPrivateKeyPropertyAnalyzer.cs
@@ -1,5 +1,8 @@
// © 2023 Koninklijke Philips N.V. See License.md in the project root for license information.
+using System.Collections.Generic;
+using LanguageExt;
+using LanguageExt.SomeHelp;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
@@ -27,11 +30,11 @@ public class AvoidPrivateKeyPropertySyntaxNodeAction : SyntaxNodeAction Analyze()
{
if (!Node.Name.ToString().Equals(PrivateKeyProperty, System.StringComparison.Ordinal))
{
- return;
+ return Option.None;
}
ITypeSymbol typeSymbol = Context.SemanticModel.GetTypeInfo(Node.Expression).Type;
@@ -39,8 +42,10 @@ public override void Analyze()
if (typeSymbol != null && typeSymbol.Name.Equals(ObjectType, System.StringComparison.Ordinal))
{
Location location = Node.GetLocation();
- ReportDiagnostic(location);
+ return PrepareDiagnostic(location).ToSome();
}
+
+ return Option.None;
}
}
}
diff --git a/Philips.CodeAnalysis.MaintainabilityAnalyzers/Maintainability/AvoidPublicMemberVariableAnalyzer.cs b/Philips.CodeAnalysis.MaintainabilityAnalyzers/Maintainability/AvoidPublicMemberVariableAnalyzer.cs
index a16b00c08..392a77b0c 100644
--- a/Philips.CodeAnalysis.MaintainabilityAnalyzers/Maintainability/AvoidPublicMemberVariableAnalyzer.cs
+++ b/Philips.CodeAnalysis.MaintainabilityAnalyzers/Maintainability/AvoidPublicMemberVariableAnalyzer.cs
@@ -1,6 +1,9 @@
// © 2019 Koninklijke Philips N.V. See License.md in the project root for license information.
+using System.Collections.Generic;
using System.Linq;
+using LanguageExt;
+using LanguageExt.SomeHelp;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
@@ -22,28 +25,29 @@ public AvoidPublicMemberVariableAnalyzer()
}
public class AvoidPublicMemberVariableSyntaxNodeAction : SyntaxNodeAction
{
- public override void Analyze()
+ public override IEnumerable Analyze()
{
if (Node.Parent.Kind() == SyntaxKind.StructDeclaration)
{
- return;
+ return Option.None;
}
if (Node.Modifiers.Any(SyntaxKind.PublicKeyword))
{
if (Node.Modifiers.Any(SyntaxKind.ConstKeyword))
{
- return;
+ return Option.None;
}
if (Node.Modifiers.Any(SyntaxKind.StaticKeyword))
{
- return;
+ return Option.None;
}
Location location = Node.GetLocation();
- ReportDiagnostic(location);
+ return PrepareDiagnostic(location).ToSome();
}
+ return Option.None;
}
}
}
diff --git a/Philips.CodeAnalysis.MaintainabilityAnalyzers/Maintainability/AvoidStaticMethodAnalyzer.cs b/Philips.CodeAnalysis.MaintainabilityAnalyzers/Maintainability/AvoidStaticMethodAnalyzer.cs
index c90ae507b..ab843c95f 100644
--- a/Philips.CodeAnalysis.MaintainabilityAnalyzers/Maintainability/AvoidStaticMethodAnalyzer.cs
+++ b/Philips.CodeAnalysis.MaintainabilityAnalyzers/Maintainability/AvoidStaticMethodAnalyzer.cs
@@ -1,7 +1,10 @@
// © 2019 Koninklijke Philips N.V. See License.md in the project root for license information.
using System;
+using System.Collections.Generic;
using System.Linq;
+using LanguageExt;
+using LanguageExt.SomeHelp;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
@@ -23,47 +26,47 @@ public AvoidStaticMethodAnalyzer()
}
public class AvoidStaticMethodSyntaxNodeAction : SyntaxNodeAction
{
- public override void Analyze()
+ public override IEnumerable Analyze()
{
// Only analyzing static method declarations
if (!Node.Modifiers.Any(SyntaxKind.StaticKeyword))
{
- return;
+ return Option.None;
}
// If the method is marked "extern", let it go.
if (Node.Modifiers.Any(SyntaxKind.ExternKeyword))
{
- return;
+ return Option.None;
}
// If the class is static, we need to let it go.
ClassDeclarationSyntax classDeclarationSyntax = Context.Node.FirstAncestorOrSelf();
if (classDeclarationSyntax == null)
{
- return;
+ return Option.None;
}
if (classDeclarationSyntax.Modifiers.Any(SyntaxKind.StaticKeyword))
{
- return;
+ return Option.None;
}
// The Main entrypoint to the program must be static
if (Node.Identifier.ValueText == @"Main")
{
- return;
+ return Option.None;
}
// Hunt for static members
INamedTypeSymbol us = Context.SemanticModel.GetDeclaredSymbol(classDeclarationSyntax);
if (us == null)
{
- return;
+ return Option.None;
}
if (ReferencesAnotherStatic(us, Context))
{
- return;
+ return Option.None;
}
// Hunt for evidence that this is a factory method
@@ -72,7 +75,7 @@ public override void Analyze()
ISymbol objectCreationSymbol = Context.SemanticModel.GetSymbolInfo(objectCreationExpressionSyntax).Symbol;
if (SymbolEqualityComparer.Default.Equals(objectCreationSymbol?.ContainingType, us))
{
- return;
+ return Option.None;
}
}
@@ -80,11 +83,11 @@ public override void Analyze()
var returnType = Node.ReturnType.ToString();
if (string.Equals(returnType, "IEnumerable