Skip to content

Commit 3187435

Browse files
committed
Almost functional version of exception analyzer
1 parent 03b4790 commit 3187435

File tree

2 files changed

+132
-13
lines changed

2 files changed

+132
-13
lines changed

Tsarev.Analyzer.Exceptions.Test/ExceptionTest.cs

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,64 @@ class Class {
2323
2424
private void WriteException()
2525
{
26-
var ex = new Exception();
26+
var ex = new System.Exception();
2727
Log.Error(ex.Message);
2828
}
2929
}
3030
";
3131

32-
VerifyCSharpDiagnostic(test, Expect(7, 15));
32+
VerifyCSharpDiagnostic(test, Expect(7, 5));
33+
}
34+
35+
[Fact]
36+
public void TestNotException()
37+
{
38+
var test = @"
39+
class Class {
40+
41+
private void WriteException()
42+
{
43+
var ex = new {Message = """"};
44+
Log.Error(ex.Message);
45+
}
46+
}
47+
";
48+
49+
VerifyCSharpDiagnostic(test);
50+
}
51+
52+
[Fact]
53+
public void ConcatExceptionToLog()
54+
{
55+
var test = @"
56+
class Class {
57+
58+
private void WriteException()
59+
{
60+
var ex = new System.Exception();
61+
Log.Error("""" + """" + ex.Message);
62+
}
63+
}
64+
";
65+
66+
VerifyCSharpDiagnostic(test, Expect(7, 5));
67+
}
68+
69+
[Fact]
70+
public void ExceptionPassedToLog()
71+
{
72+
var test = @"
73+
class Class {
74+
75+
private void WriteException()
76+
{
77+
var ex = new System.Exception();
78+
Log.Error("""" + """" + ex.Message, ex);
79+
}
80+
}
81+
";
82+
83+
VerifyCSharpDiagnostic(test);
3384
}
3485

3586
private DiagnosticResult Expect(int line, int column)

Tsarev.Analyzer.Exceptions/ExceptionsAnalyzer.cs

Lines changed: 79 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using System.Collections.Immutable;
23
using Microsoft.CodeAnalysis;
34
using Microsoft.CodeAnalysis.CSharp;
@@ -21,13 +22,58 @@ public class ExceptionsAnalyzer : DiagnosticAnalyzer
2122

2223
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(Rule, StandartRules.FailedRule);
2324

24-
public override void Initialize(AnalysisContext context) => context.RegisterSafeSyntaxNodeAction(AnalyzeInvoke, SyntaxKind.InvocationExpression);
25+
public override void Initialize(AnalysisContext context)
26+
{
27+
context.RegisterCompilationStartAction(compilationContext =>
28+
{
29+
30+
INamedTypeSymbol interfaceType = compilationContext.Compilation.GetType<Exception>();
31+
if (interfaceType == null)
32+
{
33+
return;
34+
}
35+
36+
compilationContext.RegisterSyntaxNodeAction(
37+
symbolContext => { AnalyzeInvoke(symbolContext, interfaceType); }, SyntaxKind.InvocationExpression);
38+
}
39+
);
40+
}
2541

2642
private static readonly ImmutableHashSet<string> LogMethodNames =
2743
new[] {"Error", "Warn", "Warning", "Info", "Information", "Debug", "Trace"}
2844
.ToImmutableHashSet();
2945

30-
private void AnalyzeInvoke(SyntaxNodeAnalysisContext context)
46+
private class FindExceptionMessageVisitor : CSharpSyntaxWalker
47+
{
48+
public FindExceptionMessageVisitor(SyntaxNodeAnalysisContext context,
49+
INamedTypeSymbol systemExceptionType)
50+
{
51+
Context = context;
52+
SystemExceptionType = systemExceptionType;
53+
}
54+
55+
public bool ExceptionMessagePresent { get; private set; } = false;
56+
private SyntaxNodeAnalysisContext Context { get; }
57+
private INamedTypeSymbol SystemExceptionType { get; }
58+
59+
public override void VisitMemberAccessExpression(MemberAccessExpressionSyntax member)
60+
{
61+
if (ExceptionMessagePresent)
62+
{
63+
return; // Skip
64+
}
65+
66+
if (
67+
member.Expression.IsExpressionOfTypeOrDerived(Context, SystemExceptionType) &&
68+
member.Name.Identifier.Text == "Message")
69+
{
70+
ExceptionMessagePresent = true;
71+
}
72+
}
73+
}
74+
75+
private static void AnalyzeInvoke(SyntaxNodeAnalysisContext context,
76+
INamedTypeSymbol systemExceptionType)
3177
{
3278
if (!(context.Node is InvocationExpressionSyntax invocation))
3379
{
@@ -38,18 +84,40 @@ private void AnalyzeInvoke(SyntaxNodeAnalysisContext context)
3884

3985
if (methodName != null && LogMethodNames.Contains(methodName))
4086
{
41-
foreach (var argument in invocation.ArgumentList.Arguments)
87+
if (IsExceptionMessagePassedToMethod(context, invocation, systemExceptionType) && !IsExceptionFullyPassedToMethod(context, invocation, systemExceptionType))
4288
{
43-
if (argument.Expression is MemberAccessExpressionSyntax member)
44-
{
45-
var x = member.Name.Identifier.Text;
46-
if (x == "Message")
47-
{
48-
context.ReportDiagnostic(Diagnostic.Create(Rule, member.GetLocation()));
49-
}
50-
}
89+
context.ReportDiagnostic(Diagnostic.Create(Rule, invocation.GetLocation()));
90+
}
91+
}
92+
}
93+
94+
private static bool IsExceptionFullyPassedToMethod(SyntaxNodeAnalysisContext context, InvocationExpressionSyntax invocationExpressionSyntax, INamedTypeSymbol systemExceptionType)
95+
{
96+
foreach (var argument in invocationExpressionSyntax.ArgumentList.Arguments)
97+
{
98+
if (argument.Expression.IsExpressionOfTypeOrDerived(context, systemExceptionType))
99+
{
100+
return true;
101+
}
102+
}
103+
104+
return false;
105+
}
106+
107+
private static bool IsExceptionMessagePassedToMethod(SyntaxNodeAnalysisContext context,
108+
InvocationExpressionSyntax invocationExpressionSyntax, INamedTypeSymbol systemExceptionType)
109+
{
110+
foreach (var argument in invocationExpressionSyntax.ArgumentList.Arguments)
111+
{
112+
var analyzer = new FindExceptionMessageVisitor(context, systemExceptionType);
113+
analyzer.Visit(argument.Expression);
114+
if (analyzer.ExceptionMessagePresent)
115+
{
116+
return true;
51117
}
52118
}
119+
120+
return false;
53121
}
54122
}
55123
}

0 commit comments

Comments
 (0)