-
Notifications
You must be signed in to change notification settings - Fork 229
/
SonarAnalysisContext.cs
124 lines (104 loc) · 8.24 KB
/
SonarAnalysisContext.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
/*
* SonarAnalyzer for .NET
* Copyright (C) 2015-2022 SonarSource SA
* mailto: contact AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
using Microsoft.CodeAnalysis.Text;
namespace SonarAnalyzer; // FIXME: Better namespace name or directory name
public sealed class SonarAnalysisContext : SonarAnalysisContextBase
{
private readonly AnalysisContext analysisContext;
private readonly IEnumerable<DiagnosticDescriptor> supportedDiagnostics;
/// <summary>
/// This delegate is called on all specific contexts, after the registration to the <see cref="AnalysisContext"/>, to
/// control whether or not the action should be executed.
/// </summary>
/// <remarks>
/// This delegate is set by old SonarLint (from v4.0 to v5.5) when the project has the NuGet package installed to avoid
/// duplicated analysis and issues. When both the NuGet and the VSIX are available, NuGet will take precedence and VSIX
/// will be inhibited.
/// This delegate was removed from SonarLint v6.0.
/// </remarks>
public static Func<IEnumerable<DiagnosticDescriptor>, SyntaxTree, bool> ShouldExecuteRegisteredAction { get; set; }
/// <summary>
/// This delegates control whether or not a diagnostic should be reported to Roslyn.
/// </summary>
/// <remarks>
/// Currently this delegate is set by SonarLint (older than v4.0) to provide a suppression mechanism (i.e. specific issues turned off on the bound SonarQube).
/// </remarks>
public static Func<SyntaxTree, Diagnostic, bool> ShouldDiagnosticBeReported { get; set; }
/// <summary>
/// This delegate is used to supersede the default reporting action.
/// When this delegate is set, the delegate set for <see cref="ShouldDiagnosticBeReported"/> is ignored.
/// </summary>
/// <remarks>
/// Currently this delegate is set by SonarLint (4.0+) to control how the diagnostic should be reported to Roslyn (including not being reported).
/// </remarks>
public static Action<IReportingContext> ReportDiagnostic { get; set; }
internal SonarAnalysisContext(AnalysisContext analysisContext, IEnumerable<DiagnosticDescriptor> supportedDiagnostics)
{
this.analysisContext = analysisContext ?? throw new ArgumentNullException(nameof(analysisContext));
this.supportedDiagnostics = supportedDiagnostics ?? throw new ArgumentNullException(nameof(supportedDiagnostics));
}
public override bool TryGetValue<TValue>(SourceText text, SourceTextValueProvider<TValue> valueProvider, out TValue value) =>
analysisContext.TryGetValue(text, valueProvider, out value);
/// <summary>
/// Legacy API for backward compatibility with SonarLint v4.0 - v5.5. See <see cref="ShouldExecuteRegisteredAction"/>.
/// </summary>
internal static bool LegacyIsRegisteredActionEnabled(IEnumerable<DiagnosticDescriptor> diagnostics, SyntaxTree tree) =>
ShouldExecuteRegisteredAction == null || tree == null || ShouldExecuteRegisteredAction(diagnostics, tree);
// FIXME: Better names for these pairs, this one is not used
public void RegisterCodeBlockStartAction<TSyntaxKind>(Action<SonarCodeBlockStartAnalysisContext<TSyntaxKind>> action) where TSyntaxKind : struct =>
analysisContext.RegisterCodeBlockStartAction<TSyntaxKind>(c => Execute<SonarCodeBlockStartAnalysisContext<TSyntaxKind>, CodeBlockStartAnalysisContext<TSyntaxKind>>(new(this, c), action, c.CodeBlock.SyntaxTree));
public void RegisterCodeBlockStartActionInNonGenerated<TSyntaxKind>(GeneratedCodeRecognizer generatedCodeRecognizer, Action<SonarCodeBlockStartAnalysisContext<TSyntaxKind>> action)
where TSyntaxKind : struct =>
analysisContext.RegisterCodeBlockStartAction<TSyntaxKind>(c => Execute<SonarCodeBlockStartAnalysisContext<TSyntaxKind>, CodeBlockStartAnalysisContext<TSyntaxKind>>(new(this, c), action, c.CodeBlock.SyntaxTree, generatedCodeRecognizer));
public void RegisterCompilationAction(Action<SonarCompilationAnalysisContext> action) =>
analysisContext.RegisterCompilationAction(c => Execute<SonarCompilationAnalysisContext, CompilationAnalysisContext>(new(this, c), action, null));
public void RegisterCompilationStartAction(Action<SonarCompilationStartAnalysisContext> action) =>
analysisContext.RegisterCompilationStartAction(c => Execute<SonarCompilationStartAnalysisContext, CompilationStartAnalysisContext>(new(this, c), action, null));
public void RegisterSymbolAction(Action<SonarSymbolAnalysisContext> action, params SymbolKind[] symbolKinds) =>
analysisContext.RegisterSymbolAction(c => Execute<SonarSymbolAnalysisContext, SymbolAnalysisContext>(new(this, c), action, null), symbolKinds);
// FIXME: Better names for these pairs,this one is not used (almost)
public void RegisterSyntaxNodeAction<TSyntaxKind>(Action<SonarSyntaxNodeAnalysisContext> action, params TSyntaxKind[] syntaxKinds) where TSyntaxKind : struct =>
analysisContext.RegisterSyntaxNodeAction(c => Execute<SonarSyntaxNodeAnalysisContext, SyntaxNodeAnalysisContext>(new(this, c), action, c.Node.SyntaxTree), syntaxKinds);
public void RegisterSyntaxNodeActionInNonGenerated<TSyntaxKind>(GeneratedCodeRecognizer generatedCodeRecognizer, Action<SonarSyntaxNodeAnalysisContext> action, params TSyntaxKind[] syntaxKinds)
where TSyntaxKind : struct =>
analysisContext.RegisterSyntaxNodeAction(c => Execute<SonarSyntaxNodeAnalysisContext, SyntaxNodeAnalysisContext>(new(this, c), action, c.Node.SyntaxTree, generatedCodeRecognizer), syntaxKinds);
// FIXME: Better names for these pairs,this one is not used
public void RegisterSyntaxTreeAction(Action<SonarSyntaxTreeAnalysisContext> action) =>
analysisContext.RegisterCompilationStartAction(WrapSyntaxTreeAction(action));
public void RegisterSyntaxTreeActionInNonGenerated(GeneratedCodeRecognizer generatedCodeRecognizer, Action<SonarSyntaxTreeAnalysisContext> action) =>
analysisContext.RegisterCompilationStartAction(WrapSyntaxTreeAction(action, generatedCodeRecognizer));
public Action<CompilationStartAnalysisContext> WrapSyntaxTreeAction(Action<SonarSyntaxTreeAnalysisContext> action, GeneratedCodeRecognizer generatedCodeRecognizer = null) => // FIXME: Better name
c => c.RegisterSyntaxTreeAction(treeContext => Execute<SonarSyntaxTreeAnalysisContext, SyntaxTreeAnalysisContext>(new(this, treeContext, c.Compilation), action, treeContext.Tree, generatedCodeRecognizer));
/// <param name="sourceTree">Tree that is definitely known to be analyzed. Pass 'null' if the context doesn't know a specific tree to be analyzed, like a CompilationContext.</param>
private void Execute<TSonarContext, TRoslynContext>(TSonarContext context, Action<TSonarContext> action, SyntaxTree sourceTree, GeneratedCodeRecognizer generatedCodeRecognizer = null)
where TSonarContext : SonarAnalysisContextBase<TRoslynContext>
{
// For each action registered on context we need to do some pre-processing before actually calling the rule.
// First, we need to ensure the rule does apply to the current scope (main vs test source).
// Second, we call an external delegate (set by legacy SonarLint for VS) to ensure the rule should be run (usually
// the decision is made on based on whether the project contains the analyzer as NuGet).
if (context.HasMatchingScope(supportedDiagnostics)
&& context.ShouldAnalyzeTree(sourceTree, context.Compilation, context.Options, generatedCodeRecognizer)
&& LegacyIsRegisteredActionEnabled(supportedDiagnostics, context.Tree))
{
action(context);
}
}
}