Skip to content

Commit

Permalink
Merge pull request #48198 from huoyaoyuan/use-is-null-constraint
Browse files Browse the repository at this point in the history
Fix generic constraint handling in IDE0041
  • Loading branch information
CyrusNajmabadi authored Oct 2, 2020
2 parents e4c91a3 + 75d8c9d commit f89e9df
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ public CSharpUseIsNullCheckForReferenceEqualsDiagnosticAnalyzer()
protected override bool IsLanguageVersionSupported(ParseOptions options)
=> ((CSharpParseOptions)options).LanguageVersion >= LanguageVersion.CSharp7;

protected override bool IsUnconstrainedGenericSupported(ParseOptions options)
=> ((CSharpParseOptions)options).LanguageVersion >= LanguageVersion.CSharp8;

protected override ISyntaxFacts GetSyntaxFacts()
=> CSharpSyntaxFacts.Instance;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public UseIsNullCheckForReferenceEqualsTests(ITestOutputHelper logger)
}

private static readonly ParseOptions CSharp7 = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp7);
private static readonly ParseOptions CSharp8 = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp8);
private static readonly ParseOptions CSharp9 = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp9);

internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace)
Expand Down Expand Up @@ -261,7 +262,7 @@ void M(string s1, string s2)

[WorkItem(23581, "https://github.com/dotnet/roslyn/issues/23581")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseIsNullCheck)]
public async Task TestValueParameterTypeIsUnconstrainedGeneric()
public async Task TestValueParameterTypeIsUnconstrainedGeneric_CSharp7()
{
await TestInRegularAndScript1Async(
@"
Expand All @@ -286,7 +287,38 @@ public static void NotNull<T>(T value)
}
}
}
");
", new TestParameters(parseOptions: CSharp7));
}

[WorkItem(23581, "https://github.com/dotnet/roslyn/issues/47972")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseIsNullCheck)]
public async Task TestValueParameterTypeIsUnconstrainedGeneric_CSharp8()
{
await TestInRegularAndScript1Async(
@"using System;
class C
{
public static void NotNull<T>(T value)
{
if ({|FixAllInDocument:ReferenceEquals|}(value, null))
{
return;
}
}
}",
@"using System;
class C
{
public static void NotNull<T>(T value)
{
if (value is null)
{
return;
}
}
}", new TestParameters(parseOptions: CSharp8));
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseIsNullCheck)]
Expand Down Expand Up @@ -500,5 +532,36 @@ void M(string s2)
}
}");
}

[WorkItem(23581, "https://github.com/dotnet/roslyn/issues/47972")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseIsNullCheck)]
public async Task TestValueParameterTypeIsBaseTypeConstraintGeneric()
{
await TestInRegularAndScript1Async(
@"using System;
class C
{
public static void NotNull<T>(T value) where T:C
{
if ({|FixAllInDocument:ReferenceEquals|}(value, null))
{
return;
}
}
}",
@"using System;
class C
{
public static void NotNull<T>(T value) where T:C
{
if (value is null)
{
return;
}
}
}", new TestParameters(parseOptions: CSharp7));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ protected override void InitializeWorker(AnalysisContext context)
});

protected abstract bool IsLanguageVersionSupported(ParseOptions options);
protected abstract bool IsUnconstrainedGenericSupported(ParseOptions options);
protected abstract ISyntaxFacts GetSyntaxFacts();

private void AnalyzeSyntax(SyntaxNodeAnalysisContext context, IMethodSymbol referenceEqualsMethod)
Expand Down Expand Up @@ -112,19 +113,21 @@ private void AnalyzeSyntax(SyntaxNodeAnalysisContext context, IMethodSymbol refe
var genericParameterSymbol = GetGenericParameterSymbol(syntaxFacts, semanticModel, arguments[0], arguments[1], cancellationToken);
if (genericParameterSymbol != null)
{
if (genericParameterSymbol.HasValueTypeConstraint)
if (genericParameterSymbol.IsValueType)
{
// 'is null' would generate error CS0403: Cannot convert null to type parameter 'T' because it could be a non-nullable value type. Consider using 'default(T)' instead.
// '== null' would generate error CS0019: Operator '==' cannot be applied to operands of type 'T' and '<null>'
// 'Is Nothing' would generate error BC30020: 'Is' operator does not accept operands of type 'T'. Operands must be reference or nullable types.
return;
}

if (!genericParameterSymbol.HasReferenceTypeConstraint)
// HasReferenceTypeConstraint returns false for base type constraint.
// IsReferenceType returns true.

if (!genericParameterSymbol.IsReferenceType && !IsUnconstrainedGenericSupported(syntaxTree.Options))
{
// Needs special casing for C# as long as
// https://github.com/dotnet/csharplang/issues/1284
// is not implemented.
// 'is null' over unconstrained generic is implemented in C# 8.
properties = properties.Add(UseIsNullConstants.UnconstrainedGeneric, "");
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UseIsNullCheck
Return True
End Function

Protected Overrides Function IsUnconstrainedGenericSupported(options As ParseOptions) As Boolean
Return True
End Function

Protected Overrides Function GetSyntaxFacts() As ISyntaxFacts
Return VisualBasicSyntaxFacts.Instance
End Function
Expand Down

0 comments on commit f89e9df

Please sign in to comment.