forked from sharwell/NullParameterCheckRefactoring
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathNullParameterCheckRefactoringProvider.cs
More file actions
84 lines (73 loc) · 4.28 KB
/
NullParameterCheckRefactoringProvider.cs
File metadata and controls
84 lines (73 loc) · 4.28 KB
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
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Formatting;
using System;
using System.Collections.Generic;
using System.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using RoslynExts.CS;
namespace NullParameterCheckRefactoring
{
[ExportCodeRefactoringProvider(RefactoringId, LanguageNames.CSharp), Shared]
public class NullParameterCheckRefactoringProvider : CodeRefactoringProvider
{
internal const string RefactoringId = "TR0001";
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
{
SyntaxNode root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
SyntaxNode node = root.FindNode(context.Span);
ParameterSyntax parameterSyntax = node as ParameterSyntax;
if (parameterSyntax != null)
{
TypeSyntax paramTypeName = parameterSyntax.Type;
SemanticModel semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken);
ITypeSymbol type = semanticModel.GetTypeInfo(paramTypeName).ConvertedType;
BaseMethodDeclarationSyntax methodDeclaration = parameterSyntax.Parent.Parent as BaseMethodDeclarationSyntax;
IEnumerable<IfStatementSyntax> availableIfStatements = methodDeclaration.Body.ChildNodes().OfType<IfStatementSyntax>();
if (type.IsReferenceType)
{
// check if the null check already exists.
bool isNullCheckAlreadyPresent = availableIfStatements.Any(ifStatement =>
{
return ifStatement.ChildNodes()
.OfType<BinaryExpressionSyntax>()
.Where(x => x.IsKind(SyntaxKind.EqualsExpression))
.Any(expression =>
{
if (expression.Right.IsKind(SyntaxKind.NullLiteralExpression))
{
var identifierSyntaxt = expression.ChildNodes().OfType<IdentifierNameSyntax>().FirstOrDefault();
return (identifierSyntaxt != null) &&
(identifierSyntaxt.Identifier.Text.Equals(parameterSyntax.Identifier.Text, StringComparison.Ordinal));
}
return false;
});
});
if (isNullCheckAlreadyPresent == false)
{
CodeAction action = CodeAction.Create(
"Check parameter for null",
ct => AddParameterNullCheckAsync(context.Document, parameterSyntax, methodDeclaration, ct));
context.RegisterRefactoring(action);
}
}
}
}
private async Task<Document> AddParameterNullCheckAsync(Document document, ParameterSyntax parameter, BaseMethodDeclarationSyntax methodDeclaration, CancellationToken cancellationToken)
{
var nullCheckIfStatement =
"if( \{parameter.Identifier} == null ) { throw new ArgumentNullException( nameof( \{parameter.Identifier.Text} )); };".ToSExpr<IfStatementSyntax>();
SyntaxList<SyntaxNode> newStatements = methodDeclaration.Body.Statements.Insert(0, nullCheckIfStatement);
BlockSyntax newBlock = SyntaxFactory.Block(newStatements).WithAdditionalAnnotations(Formatter.Annotation);
SyntaxNode root = await document.GetSyntaxRootAsync(cancellationToken);
SyntaxNode newRoot = root.ReplaceNode(methodDeclaration.Body, newBlock);
return document.WithSyntaxRoot(newRoot);
}
}
}