Skip to content

Commit

Permalink
Improve analyzer UseConditionalAccessInsteadOfConditionalExpression (…
Browse files Browse the repository at this point in the history
…RCS1206)
  • Loading branch information
josefpihrt committed Feb 1, 2020
1 parent e0fdc38 commit 88dd4ce
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,12 @@ internal static class SimplifyNullCheckRefactoring
if (newNode == null)
newNode = ParseExpression(whenNotNull.ToString().Insert(expression.Span.End - whenNotNull.SpanStart, "?"));

if (coalesce || !semanticModel.GetTypeSymbol(whenNotNull, cancellationToken).IsReferenceTypeOrNullableType())
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)
Expand Down
27 changes: 26 additions & 1 deletion src/Analyzers/CSharp/Analysis/SimplifyNullCheckAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,8 @@ public static void AnalyzeConditionalExpression(SyntaxNodeAnalysisContext contex

if (typeSymbol?.IsErrorType() == false
&& (typeSymbol.IsReferenceType || typeSymbol.IsValueType)
&& semanticModel.IsDefaultValue(typeSymbol, whenNull, cancellationToken)
&& (semanticModel.IsDefaultValue(typeSymbol, whenNull, cancellationToken)
|| IsDefaultOfNullableStruct(typeSymbol, whenNull, semanticModel, cancellationToken))
&& !CSharpUtility.ContainsOutArgumentWithLocal(whenNotNull, semanticModel, cancellationToken)
&& !conditionalExpressionInfo.ConditionalExpression.IsInExpressionTree(semanticModel, cancellationToken))
{
Expand All @@ -179,5 +180,29 @@ public static void AnalyzeConditionalExpression(SyntaxNodeAnalysisContext contex
}
}
}

private static bool IsDefaultOfNullableStruct(
ITypeSymbol typeSymbol,
ExpressionSyntax whenNull,
SemanticModel semanticModel,
CancellationToken cancellationToken)
{
if (typeSymbol.IsValueType
&& !typeSymbol.IsNullableType()
&& whenNull.IsKind(SyntaxKind.DefaultExpression))
{
var defaultExpression = (DefaultExpressionSyntax)whenNull;

TypeSyntax type = defaultExpression.Type;

if (type.IsKind(SyntaxKind.NullableType)
&& semanticModel.GetTypeSymbol(type, cancellationToken)?.IsNullableOf(typeSymbol) == true)
{
return true;
}
}

return false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,74 @@ void M()
");
}

[Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.UseConditionalAccessInsteadOfConditionalExpression)]
public async Task Test_StructAndDefaultOfNullable()
{
await VerifyDiagnosticAndFixAsync(@"
using System;
class C
{
void M()
{
C x = null;
int? y = [|(x != null) ? x.M2() : default(int?)|];
}
int M2() => default;
}
", @"
using System;
class C
{
void M()
{
C x = null;
int? y = x?.M2();
}
int M2() => default;
}
");
}

[Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.UseConditionalAccessInsteadOfConditionalExpression)]
public async Task Test_DefaultOfNullableAndStruct()
{
await VerifyDiagnosticAndFixAsync(@"
using System;
class C
{
void M()
{
C x = null;
int? y = [|(x == null) ? default(int?) : x.M2()|];
}
int M2() => default;
}
", @"
using System;
class C
{
void M()
{
C x = null;
int? y = x?.M2();
}
int M2() => default;
}
");
}

[Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.UseConditionalAccessInsteadOfConditionalExpression)]
public async Task TestNoDiagnostic()
{
Expand Down

0 comments on commit 88dd4ce

Please sign in to comment.