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

Fix CA2016 when used on instance method with generic types #4974

Merged
merged 1 commit into from
Jul 10, 2021

Conversation

DavidBoike
Copy link
Contributor

@DavidBoike DavidBoike commented Mar 23, 2021

Fixes #4870

@DavidBoike DavidBoike requested a review from a team as a code owner March 23, 2021 18:10
@dnfadmin
Copy link

dnfadmin commented Mar 23, 2021

CLA assistant check
All CLA requirements met.

@codecov
Copy link

codecov bot commented Mar 23, 2021

Codecov Report

Merging #4974 (c2bc092) into main (8236e8b) will increase coverage by 0.05%.
The diff coverage is 100.00%.

❗ Current head c2bc092 differs from pull request most recent head e05098e. Consider uploading reports for the commit e05098e to get more accurate results

@@            Coverage Diff             @@
##             main    #4974      +/-   ##
==========================================
+ Coverage   95.52%   95.58%   +0.05%     
==========================================
  Files        1200     1190      -10     
  Lines      274867   273033    -1834     
  Branches    16622    16486     -136     
==========================================
- Hits       262571   260974    -1597     
+ Misses      10112     9921     -191     
+ Partials     2184     2138      -46     

Comment on lines -138 to -145
case IConditionalAccessInstanceOperation:
newInstance = GetConditionalOperationInvocationExpression(invocation.Syntax);
break;
default:
if (invocation.Instance.IsImplicit)
{
newInstance = invocation.GetInstanceSyntax()!;
}
Copy link
Member

Choose a reason for hiding this comment

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

Adds a test case for a static generic method, which was green before applying the fix, to guard against regressions in that behavior

The CI is still passing because apparently I forgot to add unit tests for all three cases you removed.

  • It seems we don't have a conditional operation case in the unit tests. Can you please add one?
  • I'm trying to remember what IsImplicit is referring to. It's either:
    • An invocation of a method from an explicit interface implementation, or
    • An invocation of a method passed as a callback to another method (a Func or an Action).
      Independently if it's one or the other, we don't have unit tests for any of them. Would you mind adding one to make sure that functionality is not regressed?

Thanks for adding a method for the static case.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

From the IConditionalAccessInstanceOperation Interface docs it sounds like you're referring to a call like someExpression?.Method(). Isn't that covered by CS_Diagnostic_DereferencePossibleNullReference?

IsImplicit seems to refer to the "this" object passed to the method, either the instance or null if it's a static method. In any case, when I tried adding this block to the fixer:

if (invocation.Instance != null && invocation.Instance.IsImplicit)
{
    throw new Exception("Implicit");
}

…the result was that 73 tests failed. First one was CS_Diagnostic_ActionDelegateAwait where MethodAsync() is being called without this., which seems to track. So I think it's safe to say that test case is covered.

While the removal of that code looks scary, I think it makes sense philisophically. The fixer is only trying to adjust what's going on in the ArgumentList. So trying to rebuild the part of the invocation "outside" the parens from scratch based on a number of conditions doesn't really make sense when expression is already going to contain whatever that outside part was originally highlighted by the red squiggly.

To illustrate that point further, I added 2 additional tests CS_Diagnostic_NullCoalescedDelegates and CS_Diagnostic_NullCoalescedDelegatesWithInvoke, both of which have 2 delegates and call either (f1 ?? f2)() or (f1 ?? f2).Invoke(). The current version on main will rewrite (f1 ?? f2)() to (f1 ?? f2).Invoke(c), adding the explicit Invoke where none was there before. I assume it's preferable for a fixer to do the bare minimum to implement the fix without changing any other code, and this PR respects whether or not the explicit Invoke is used in the expression.

@@ -2229,6 +2229,78 @@ void LocalMethod()
return CS8VerifyCodeFixAsync(originalCode, fixedCode);
}

[Fact]
[WorkItem(4870, "https://github.com/dotnet/roslyn-analyzers/issues/4870")]
public Task CS_Diagnostic_GenericTypeParamOnInstanceMethod()
Copy link
Member

Choose a reason for hiding this comment

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

Can you please also add VB unit tests?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added VB versions of the test. Dislcaimer: not a VB developer. I leaned on the online Telerik code converter and a bunch of guess & check.

Copy link
Member

Choose a reason for hiding this comment

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

We all use Telerik 😄 .

Copy link
Member

@carlossanlop carlossanlop left a comment

Choose a reason for hiding this comment

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

Thank you for the change, @DavidBoike . I left a couple of requests.

Copy link
Contributor Author

@DavidBoike DavidBoike left a comment

Choose a reason for hiding this comment

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

@carlossanlop ready for another review.

@@ -2229,6 +2229,78 @@ void LocalMethod()
return CS8VerifyCodeFixAsync(originalCode, fixedCode);
}

[Fact]
[WorkItem(4870, "https://github.com/dotnet/roslyn-analyzers/issues/4870")]
public Task CS_Diagnostic_GenericTypeParamOnInstanceMethod()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added VB versions of the test. Dislcaimer: not a VB developer. I leaned on the online Telerik code converter and a bunch of guess & check.

Comment on lines -138 to -145
case IConditionalAccessInstanceOperation:
newInstance = GetConditionalOperationInvocationExpression(invocation.Syntax);
break;
default:
if (invocation.Instance.IsImplicit)
{
newInstance = invocation.GetInstanceSyntax()!;
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

From the IConditionalAccessInstanceOperation Interface docs it sounds like you're referring to a call like someExpression?.Method(). Isn't that covered by CS_Diagnostic_DereferencePossibleNullReference?

IsImplicit seems to refer to the "this" object passed to the method, either the instance or null if it's a static method. In any case, when I tried adding this block to the fixer:

if (invocation.Instance != null && invocation.Instance.IsImplicit)
{
    throw new Exception("Implicit");
}

…the result was that 73 tests failed. First one was CS_Diagnostic_ActionDelegateAwait where MethodAsync() is being called without this., which seems to track. So I think it's safe to say that test case is covered.

While the removal of that code looks scary, I think it makes sense philisophically. The fixer is only trying to adjust what's going on in the ArgumentList. So trying to rebuild the part of the invocation "outside" the parens from scratch based on a number of conditions doesn't really make sense when expression is already going to contain whatever that outside part was originally highlighted by the red squiggly.

To illustrate that point further, I added 2 additional tests CS_Diagnostic_NullCoalescedDelegates and CS_Diagnostic_NullCoalescedDelegatesWithInvoke, both of which have 2 delegates and call either (f1 ?? f2)() or (f1 ?? f2).Invoke(). The current version on main will rewrite (f1 ?? f2)() to (f1 ?? f2).Invoke(c), adding the explicit Invoke where none was there before. I assume it's preferable for a fixer to do the bare minimum to implement the fix without changing any other code, and this PR respects whether or not the explicit Invoke is used in the expression.

@DavidBoike
Copy link
Contributor Author

Anything else I can do here?

@mavasani
Copy link
Member

Anything else I can do here?

@carlossanlop Can you please help review the PR again? We already have multiple users reporting this bug.

@DavidBoike
Copy link
Contributor Author

I rebased the PR to make it easier to review.

@DavidBoike
Copy link
Contributor Author

Checking in again. What can I do here?

@DavidBoike
Copy link
Contributor Author

@mavasani @carlossanlop @buyaa-n Please let me know how I can help drive this forward. I'm willing to add more test cases or get on a call or whatever, just let me know.

@adamralph
Copy link
Contributor

adamralph commented Jun 30, 2021

@mavasani @carlossanlop is there anything that I, @DavidBoike, or anyone else can do to help move this forward? It would be a shame to abandon it after the non-trivial effort that's already been made by @DavidBoike and @carlossanlop. And as mentioned above, multiple users have reported this problem.

Copy link
Member

@buyaa-n buyaa-n left a comment

Choose a reason for hiding this comment

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

The fix looks good to me, I will merge it on Monday if @carlossanlop will not add any other comments/concerns

CC @mavasani

@jeffhandley
Copy link
Member

@carlossanlop won't be available to review on Monday. LGTM.

@jeffhandley jeffhandley merged commit f64768f into dotnet:main Jul 10, 2021
@adamralph
Copy link
Contributor

🎉 which SDK version is this scheduled for?

@jeffhandley
Copy link
Member

This will be included in .NET 6 Preview 7.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

CA2016: Code fix removes generic type parameter from message
7 participants