forked from dotnet/roslyn
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathAbstractUseExpressionBodyDiagnosticAnalyzer.cs
115 lines (100 loc) · 4.87 KB
/
AbstractUseExpressionBodyDiagnosticAnalyzer.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
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Immutable;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Options;
namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody
{
internal abstract class AbstractUseExpressionBodyDiagnosticAnalyzer<TDeclaration> :
AbstractCodeStyleDiagnosticAnalyzer, IBuiltInAnalyzer
where TDeclaration : SyntaxNode
{
private readonly ImmutableArray<SyntaxKind> _syntaxKinds;
private readonly Option<CodeStyleOption<bool>> _option;
private readonly LocalizableString _expressionBodyTitle;
private readonly LocalizableString _blockBodyTitle;
public bool OpenFileOnly(Workspace workspace) => true;
protected AbstractUseExpressionBodyDiagnosticAnalyzer(
string diagnosticId,
LocalizableString expressionBodyTitle,
LocalizableString blockBodyTitle,
ImmutableArray<SyntaxKind> syntaxKinds,
Option<CodeStyleOption<bool>> option)
: base(diagnosticId, expressionBodyTitle)
{
_syntaxKinds = syntaxKinds;
_option = option;
_expressionBodyTitle = expressionBodyTitle;
_blockBodyTitle = blockBodyTitle;
}
public DiagnosticAnalyzerCategory GetAnalyzerCategory() => DiagnosticAnalyzerCategory.SemanticDocumentAnalysis;
public override void Initialize(AnalysisContext context)
=> context.RegisterSyntaxNodeAction(AnalyzeSyntax, _syntaxKinds);
private void AnalyzeSyntax(SyntaxNodeAnalysisContext context)
{
var options = context.Options;
var syntaxTree = context.Node.SyntaxTree;
var cancellationToken = context.CancellationToken;
var optionSet = options.GetDocumentOptionSetAsync(syntaxTree, cancellationToken).GetAwaiter().GetResult();
if (optionSet == null)
{
return;
}
var diagnostic = AnalyzeSyntax(optionSet, (TDeclaration)context.Node);
if (diagnostic != null)
{
context.ReportDiagnostic(diagnostic);
}
}
internal virtual Diagnostic AnalyzeSyntax(OptionSet optionSet, TDeclaration declaration)
{
var preferExpressionBodiedOption = optionSet.GetOption(_option);
var expressionBody = GetExpressionBody(declaration);
if (preferExpressionBodiedOption.Value)
{
if (expressionBody == null)
{
// They want expression bodies and they don't have one. See if we can
// convert this to have an expression body.
expressionBody = GetBody(declaration).TryConvertToExpressionBody(declaration.SyntaxTree.Options);
if (expressionBody != null)
{
var additionalLocations = ImmutableArray.Create(declaration.GetLocation());
return Diagnostic.Create(
CreateDescriptor(this.DescriptorId, _expressionBodyTitle, preferExpressionBodiedOption.Notification.Value),
GetBody(declaration).Statements[0].GetLocation(),
additionalLocations: additionalLocations);
}
}
}
else
{
// They don't want expression bodies but they have one. Offer to conver this to a normal block
if (expressionBody != null)
{
var additionalLocations = ImmutableArray.Create(declaration.GetLocation());
return Diagnostic.Create(
CreateDescriptor(this.DescriptorId, _blockBodyTitle, preferExpressionBodiedOption.Notification.Value),
expressionBody.GetLocation(),
additionalLocations: additionalLocations);
}
}
return null;
}
protected static BlockSyntax GetBodyFromSingleGetAccessor(AccessorListSyntax accessorList)
{
if (accessorList != null &&
accessorList.Accessors.Count == 1 &&
accessorList.Accessors[0].AttributeLists.Count == 0 &&
accessorList.Accessors[0].IsKind(SyntaxKind.GetAccessorDeclaration))
{
return accessorList.Accessors[0].Body;
}
return null;
}
protected abstract BlockSyntax GetBody(TDeclaration declaration);
protected abstract ArrowExpressionClauseSyntax GetExpressionBody(TDeclaration declaration);
}
}