Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suppress 'IDE0041 Null check can be simplified' for unconstraint generic parameters. #24173

Conversation

MaStr11
Copy link
Contributor

@MaStr11 MaStr11 commented Jan 11, 2018

Customer scenario

IDE0041 Null check can be simplified is shown for the ReferenceEquals method and a fix is offered. The fix generates code which might have the compiler error 'Can not convert null to type parameter T because it could be a non-nullable value type.'. This fix suppresses IDE0041 if the parameter passed to ReferenceEquals is an unconstraint generic type parameter Update 01/17/2018: a value type constraint generic type parameter.

Bugs this fixes

#23581

Workarounds, if any

Suppress IDE0041 with a pragma or change the generated code from (value is null)to (value == null).

Risk

Low. Small additional check.

Performance impact

Low. Syntax and semantic analysis has already been done by other checks on the code path before. This adds some more checks.

Is this a regression from a previous update?

/

Root cause analysis

/

How was the bug found?

Customer reported #23581

Test documentation updated?

/

@MaStr11 MaStr11 requested a review from a team as a code owner January 11, 2018 12:12
@MaStr11
Copy link
Contributor Author

MaStr11 commented Jan 11, 2018

Another option might be to generate (value == null) instead of (value is null). VBs value Is Nothing seems also be fine with unconstraint parameters.

@MaStr11
Copy link
Contributor Author

MaStr11 commented Jan 12, 2018

@CyrusNajmabadi

Another option might be to generate (value == null) instead of (value is null). VBs value Is Nothing seems also be fine with unconstraint parameters.

This seems to be the better option (I just discovered this option after I finished the PR). Are there any downsides?

@CyrusNajmabadi
Copy link
Member

This seems to be the better option (I just discovered this option after I finished the PR). Are there any downsides?

Not that i can tell. Make it so!

@MaStr11
Copy link
Contributor Author

MaStr11 commented Jan 15, 2018

Oh my gosh! I just read dotnet/csharplang#505 and discovered that (v == null) fails for value type constraints:

public void TheFunction<T>(T a) where T : struct {
  var c = (a != null); // CS0019: Operator '!=' cannot be applied to operand of type 'T' and '<null>'
}
Public Sub TheFunction(Of T As Structure)(a As T)
    If a Is Nothing Then 'BC30020: 'Is' operator does not accept operands of type 'T'. Operands must be reference or nullable types.
    End If
End Sub

I will update the PR to suppress on value types now.

@@ -36,7 +36,7 @@ class C
{
void M(string s)
{
if (s is null)
if (s == null)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this seems like a regression.

@@ -17,12 +17,16 @@ protected override string GetIsNullTitle()
protected override string GetIsNotNullTitle()
=> GetIsNullTitle();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the feature is literall the "Use 'is null'" feature :) You're now using "== null".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. But I thought we agreed on that. Did I got you wrong?

@CyrusNajmabadi
Copy link
Member

Did I got you wrong?

Yes (my fault). I meant specifically using '==' only for the cases where 'is' wouldn't work.

@CyrusNajmabadi
Copy link
Member

basically:

  1. use "is null" whenever possible.
  2. fall back to "== null" when '1' isn't possible. (or possibly just suppress).
  3. if neither are possible, offer nothing.

@MaStr11
Copy link
Contributor Author

MaStr11 commented Jan 15, 2018

That's fine to me (I'm on the road tomorrow so this will not be done until Wednesday. Sorry.)

@CyrusNajmabadi
Copy link
Member

No worries :)

@MaStr11
Copy link
Contributor Author

MaStr11 commented Jan 17, 2018

I changed the logic as requested by @CyrusNajmabadi. I was wondering if it is worth to document somewhere that this fixer can be simplified once this is done:

@gafter

We should change the language to permit value is null for an unconstrained generic.

There seems to be no issue in the language repo yet to link to.

@CyrusNajmabadi
Copy link
Member

There seems to be no issue in the language repo yet to link to.

you could make one :) Then link to that in the code with a note.

=> ((PrefixUnaryExpressionSyntax)notExpression).WithOperand((ExpressionSyntax)CreateIsNullCheck(argument));

protected override SyntaxNode CreateNullCheck(SyntaxNode argument, bool isUnconstraintGeneric)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unconstrained (though Unconstraint is cute :))

@@ -108,9 +119,18 @@ private void AnalyzeSyntax(SyntaxNodeAnalysisContext context, IMethodSymbol refe
return;
}

var properties = ImmutableDictionary<string, string>.Empty;

switch (GetGenericParameterConstraintType(syntaxFacts, semanticModel, arguments[0], arguments[1], cancellationToken))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a simpler approach seems like it would be if you just had this method return the type parameter if there is one.

then you could do:

if (typeParameter?.HasValueConstraint == true)
{
  return;
}
else if (...?.HasReferenceConstraint == true)
{
    ...
}

Then there's no need for the enum to shuttle data across that's already in the ITypeParameter.

Copy link
Member

@CyrusNajmabadi CyrusNajmabadi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall logic LGTM. I think there are some things that can be simplified though.

@sharwell sharwell changed the base branch from master to dev15.7.x February 14, 2018 16:26
@sharwell sharwell changed the base branch from dev15.7.x to master February 14, 2018 16:27
@sharwell
Copy link
Member

@Pilchie Requesting approval for 15.7 before we do the work to rebase this 😄

@Pilchie
Copy link
Member

Pilchie commented Feb 14, 2018

I'm in favor of fixing false positive diagnostic results as early as possible, so I would be supportive of this for 15.7.

@sharwell
Copy link
Member

@MaStr11 This pull request needs to be rebased onto the dev15.7.x branch so we can get it to users earlier. Let me know if you'd like to do this yourself, or if you'd like me to go ahead and do it for you. 👍

@MaStr11 MaStr11 changed the base branch from master to dev15.7.x February 14, 2018 20:34
@MaStr11
Copy link
Contributor Author

MaStr11 commented Feb 14, 2018

@sharwell I changed the base branch but it seems there are some unrelated commits are also part of the PR. I'm not able to fix (because I don't know how to do it and because I'm currently on vacation and don't have my laptop with me). If there is anything that needs to be done it would be great if you could do it.

@sharwell
Copy link
Member

@MaStr11 Thanks I'm on it!

@sharwell sharwell changed the base branch from dev15.7.x to master February 14, 2018 20:50
@sharwell sharwell force-pushed the SuppressIDE0041_UseIsNullcheck_ForUnconstraintGenericParameters branch from 1f1a266 to bffe221 Compare February 14, 2018 20:53
@MaStr11 MaStr11 requested review from a team as code owners February 14, 2018 20:53
@sharwell sharwell changed the base branch from master to dev15.7.x February 14, 2018 20:53
@sharwell sharwell removed request for a team February 14, 2018 23:28
@sharwell sharwell added this to the 15.7 milestone Feb 14, 2018
@sharwell
Copy link
Member

@Pilchie for approval

@sharwell sharwell added the Community The pull request was submitted by a contributor who is not a Microsoft employee. label Feb 14, 2018
@sharwell sharwell merged commit 367b725 into dotnet:dev15.7.x Feb 14, 2018
@MaStr11 MaStr11 deleted the SuppressIDE0041_UseIsNullcheck_ForUnconstraintGenericParameters branch March 2, 2018 15:17
@Zenexer
Copy link

Zenexer commented Aug 2, 2019

Another option might be to generate (value == null) instead of (value is null). VBs value Is Nothing seems also be fine with unconstraint parameters.

I know I'm late to the game here, but this needs to be documented: do not do this! It's is not the same thing. A ReferenceEquals comparison to null often occurs in the Equals override of a type, and that is in turn often referenced from operator == and operator !=. value == null will create an infinite loop.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Approved to merge Community The pull request was submitted by a contributor who is not a Microsoft employee.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants