Skip to content

Commit

Permalink
refactor: SingleDiagnosticAnalyzer::Analyze returns the Diagnostics
Browse files Browse the repository at this point in the history
This ensures single responsibility of the Analyze method itself;
  • Loading branch information
kryptt committed Mar 13, 2023
1 parent d00546a commit d977172
Show file tree
Hide file tree
Showing 53 changed files with 565 additions and 416 deletions.
19 changes: 19 additions & 0 deletions Philips.CodeAnalysis.Common/ParameterPredicates.cs
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<AssemblyVersion>1.0.3.0</AssemblyVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="LanguageExt.Core" Version="4.4.2" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="3.6.0" />
<PackageReference Include="Mono.Cecil" Version="0.11.4" />
</ItemGroup>
Expand Down
33 changes: 17 additions & 16 deletions Philips.CodeAnalysis.Common/SingleDiagnosticAnalyzer{TU}.cs
Original file line number Diff line number Diff line change
@@ -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<T, TSyntaxNodeAction> : SingleDiagnosticAnalyzer where T : SyntaxNode where TSyntaxNodeAction : SyntaxNodeAction<T>, new()
Expand Down Expand Up @@ -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<GeneratedCodeDetector> { 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()
Expand Down
8 changes: 4 additions & 4 deletions Philips.CodeAnalysis.Common/SyntaxNodeAction.cs
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -14,12 +15,11 @@ public abstract class SyntaxNodeAction<T> where T : SyntaxNode

protected Helper Helper { get; init; } = new Helper();

public abstract void Analyze();
public abstract IEnumerable<Diagnostic> 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);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Philips.CodeAnalysis.Common;

using static LanguageExt.Prelude;
using LanguageExt;

namespace Philips.CodeAnalysis.MaintainabilityAnalyzers.Cardinality
{
Expand All @@ -29,25 +30,20 @@ public AvoidEnumParametersAnalyzer()

public class AvoidEnumParametersSyntaxNodeAction : SyntaxNodeAction<MethodDeclarationSyntax>
{
public override void Analyze()
public override IEnumerable<Diagnostic> Analyze()
{
_ = List(Node)
return List(Node)
.Filter((m) => !m.IsOverridden())
.SelectMany(AnalyzeMethodParameters)
.Iter(Context.ReportDiagnostic);
.SelectMany(AnalyzeMethodParameters);
}

private IEnumerable<Diagnostic> 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<Diagnostic> AnalyzeParam(MethodDeclarationSyntax m, ParameterSyntax p)
{
return Optional(Context.SemanticModel.GetDeclaredSymbol(p).Type)
.Select((t) => m.CreateDiagnostic(Rule));

}
}

}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -23,13 +24,12 @@ public AvoidVoidReturnAnalyzer()

public class AvoidVoidReturnSyntaxNodeAction : SyntaxNodeAction<MethodDeclarationSyntax>
{
public override void Analyze()
public override IEnumerable<Diagnostic> 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));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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)));
}

}
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
// © 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;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Text;
using Philips.CodeAnalysis.Common;

using static LanguageExt.Prelude;

namespace Philips.CodeAnalysis.MaintainabilityAnalyzers.Documentation
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
Expand All @@ -29,16 +32,17 @@ public class CopyrightPresentSyntaxNodeAction : SyntaxNodeAction<CompilationUnit
{
private static readonly Regex yearRegex = new(@"\d\d\d\d", RegexOptions.Singleline | RegexOptions.Compiled, TimeSpan.FromSeconds(1));

public override void Analyze()
public override IEnumerable<Diagnostic> Analyze()
{
if (Helper.IsAssemblyInfo(Context) || Helper.HasAutoGeneratedComment(Node))
{
return;
return Option<Diagnostic>.None;
}

if (Node.FindToken(0).IsKind(SyntaxKind.EndOfFileToken))
{
return;
return Option<Diagnostic>.None;
;
}

Location location = GetSquiggleLocation(Node.SyntaxTree);
Expand All @@ -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<Diagnostic>.None;
}

SyntaxTrivia syntaxTrivia = leadingTrivia.FirstOrDefault(t => t.IsKind(SyntaxKind.SingleLineCommentTrivia));
if (!CheckCopyrightStatement(syntaxTrivia))
{
ReportDiagnostic(location);
return Optional(PrepareDiagnostic(location));
}

return Option<Diagnostic>.None;
}

private Location GetSquiggleLocation(SyntaxTree tree)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
/// <summary>
Expand All @@ -27,11 +30,11 @@ public DocumentUnhandledExceptionsAnalyzer()

public class DocumentUnhandledExceptionsSyntaxNodeAction : SyntaxNodeAction<MethodDeclarationSyntax>
{
public override void Analyze()
public override IEnumerable<Diagnostic> Analyze()
{
if (Context.Compilation?.SyntaxTrees.FirstOrDefault()?.Options.DocumentationMode == DocumentationMode.None)
{
return;
return Option<Diagnostic>.None;
}

IReadOnlyDictionary<string, string> aliases = Helper.GetUsingAliases(Node);
Expand Down Expand Up @@ -62,9 +65,9 @@ public override void Analyze()
var methodName = Node.Identifier.Text;
var remainingExceptionsString = string.Join(",", remainingExceptions);
ImmutableDictionary<string, string> properties = ImmutableDictionary<string, string>.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<Diagnostic>.None;
}
}
}
Original file line number Diff line number Diff line change
@@ -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)]
Expand All @@ -21,12 +25,13 @@ public EnableDocumentationCreationAnalyzer()

public class EnableDocumentationCreationAction : SyntaxNodeAction<CompilationUnitSyntax>
{
public override void Analyze()
public override IEnumerable<Diagnostic> Analyze()
{
if (Node.SyntaxTree.Options.DocumentationMode == DocumentationMode.None)
{
ReportDiagnostic();
return Optional(PrepareDiagnostic());
}
return Option<Diagnostic>.None;
}
}
}
Loading

0 comments on commit d977172

Please sign in to comment.