forked from dotnet/roslynator
-
Notifications
You must be signed in to change notification settings - Fork 0
/
SimplifyNullCheckRefactoring.cs
96 lines (78 loc) · 4.32 KB
/
SimplifyNullCheckRefactoring.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
// Copyright (c) Josef Pihrt and Contributors. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Roslynator.CSharp.Analysis;
using Roslynator.CSharp.Syntax;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
using static Roslynator.CSharp.CSharpFactory;
namespace Roslynator.CSharp.Refactorings;
internal static class SimplifyNullCheckRefactoring
{
public static async Task<Document> RefactorAsync(
Document document,
ConditionalExpressionSyntax conditionalExpression,
CancellationToken cancellationToken)
{
SemanticModel semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
ConditionalExpressionInfo conditionalExpressionInfo = SyntaxInfo.ConditionalExpressionInfo(conditionalExpression);
NullCheckExpressionInfo nullCheck = SyntaxInfo.NullCheckExpressionInfo(conditionalExpressionInfo.Condition, semanticModel: semanticModel, cancellationToken: cancellationToken);
ExpressionSyntax whenNotNull = (nullCheck.IsCheckingNotNull) ? conditionalExpressionInfo.WhenTrue : conditionalExpressionInfo.WhenFalse;
ExpressionSyntax whenNull = (nullCheck.IsCheckingNotNull) ? conditionalExpressionInfo.WhenFalse : conditionalExpressionInfo.WhenTrue;
var castExpression = whenNotNull as CastExpressionSyntax;
ExpressionSyntax expression = (castExpression is not null)
? UseConditionalAccessAnalyzer.FindExpressionThatCanBeConditionallyAccessed(nullCheck.Expression, castExpression.Expression, semanticModel, cancellationToken)
: UseConditionalAccessAnalyzer.FindExpressionThatCanBeConditionallyAccessed(nullCheck.Expression, whenNotNull, semanticModel, cancellationToken);
var coalesce = false;
ExpressionSyntax newNode = null;
if (CSharpFactory.AreEquivalent(nullCheck.Expression, whenNotNull))
{
//RCS1084 UseCoalesceExpressionInsteadOfConditionalExpression
newNode = nullCheck.Expression;
coalesce = true;
}
else if (semanticModel
.GetTypeSymbol(nullCheck.Expression, cancellationToken)
.IsNullableType())
{
if (expression.IsParentKind(SyntaxKind.SimpleMemberAccessExpression))
{
var memberAccessExpression = (MemberAccessExpressionSyntax)expression.Parent;
if (!memberAccessExpression.IsParentKind(SyntaxKind.InvocationExpression)
&& (memberAccessExpression.Name as IdentifierNameSyntax)?.Identifier.ValueText == "Value")
{
if (memberAccessExpression == whenNotNull)
{
//RCS1084 UseCoalesceExpressionInsteadOfConditionalExpression
newNode = nullCheck.Expression;
coalesce = true;
}
else
{
newNode = ParseExpression($"{expression}?{whenNotNull.ToString().Substring(memberAccessExpression.Span.End - whenNotNull.SpanStart)}");
if (castExpression is not null)
{
newNode = castExpression
.WithExpression(newNode.Parenthesize())
.WithSimplifierAnnotation();
}
}
}
}
}
if (newNode is null)
newNode = ParseExpression(whenNotNull.ToString().Insert(expression.Span.End - whenNotNull.SpanStart, "?"));
if (coalesce
|| (!semanticModel.GetTypeSymbol(whenNotNull, cancellationToken).IsReferenceTypeOrNullableType()
&& (whenNull as DefaultExpressionSyntax)?.Type.IsKind(SyntaxKind.NullableType) != true))
{
newNode = CoalesceExpression(newNode.Parenthesize(), whenNull.Parenthesize());
}
newNode = newNode
.WithTriviaFrom(conditionalExpression)
.Parenthesize();
return await document.ReplaceNodeAsync(conditionalExpression, newNode, cancellationToken).ConfigureAwait(false);
}
}