Skip to content

Commit

Permalink
chor: 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 14, 2023
1 parent 6327d53 commit 0bfc1b9
Show file tree
Hide file tree
Showing 52 changed files with 537 additions and 403 deletions.
2 changes: 1 addition & 1 deletion Philips.CodeAnalysis.Common/ParameterPredicates.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// © 2023 Koninklijke Philips N.V. See License.md in the project root for license information.
// © 2023 Koninklijke Philips N.V. See License.md in the project root for license information.

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
Expand Down
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 @@ -8,6 +8,7 @@
using Microsoft.CodeAnalysis.Diagnostics;
using Philips.CodeAnalysis.Common;
using static LanguageExt.Prelude;
using LanguageExt;

namespace Philips.CodeAnalysis.MaintainabilityAnalyzers.Cardinality
{
Expand All @@ -26,12 +27,11 @@ 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)
Expand Down
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
@@ -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;
}
}
}
Original file line number Diff line number Diff line change
@@ -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)]
Expand All @@ -21,40 +26,31 @@ public OrderPropertyAccessorsAnalyzer()

public class OrderPropertyAccessorsSyntaxNodeAction : SyntaxNodeAction<PropertyDeclarationSyntax>
{
public override void Analyze()
public override IEnumerable<Diagnostic> 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<bool>.None, ReduceSetIsBeforeGet)
.Filter(setIsBeforeGet => setIsBeforeGet)
.Select((setIsBeforeGet) => PrepareDiagnostic(accsessors.GetLocation()))
);
}

for (var i = 0; i < accessors.Accessors.Count; i++)
private static Option<bool> ReduceSetIsBeforeGet(Option<bool> 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;
}
}
}
Loading

0 comments on commit 0bfc1b9

Please sign in to comment.