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

[Proposal] Compiler warnings with unobvious assignment #2527

Open
Kir-Antipov opened this issue Jun 7, 2019 · 2 comments · May be fixed by #3694
Open

[Proposal] Compiler warnings with unobvious assignment #2527

Kir-Antipov opened this issue Jun 7, 2019 · 2 comments · May be fixed by #3694
Labels
Approved-Rule Indicates if the new proposed rule has been approved to move to implementation phase Feature Request help wanted The issue is up-for-grabs, and can be claimed by commenting Needs-Fixer
Milestone

Comments

@Kir-Antipov
Copy link

C# is a beautiful and flexible language that will not allow a beginner to "shoot off his leg"... or it will?


Issue

Let's imagine that we have the following class:

class Foo
{
    public int Value;
    public Foo Next;
}

Then we want to do something like this:

var a = new Foo();
var b = new Foo { Value = 1, Next = new Foo { Value = 2 } };
a.Next = a = b;
Console.WriteLine(a.Next == a);

What we'll see in the console's output?
Most of you're now thinking something like this:

It's kinda simple!
First we assign "b" to "a", so "a" and "b" are now the same
Then we assign "a" (or "b", cause they're the same now) to the "Next" field of object "a" (or "b")
So a.Next == a is true!

And I have some bad news for you, cause it's false :D
If you're surprised, then let me surprise you even more:

a = b;
a.Next = a; // or b
Console.WriteLine(a.Next == a); // true

Explanation

When we have construction like this:

a = b;
a.x = b;

As a result, we'll get this IL-code:

;store b into a
ldloc b
stloc a 

;store b into a.x
ldloc a
ldloc b 
stfld class Foo Foo::X

But if we want to simplify this, and we'll use the following construction (that should mean the same as the example above):

a.x = a = b; 

IL will look like this:

ldloc a ;load "a" to the top of the stack
ldloc b ;load "b" to the top of the stack
dup     ;load "b" to the top of the stack
stloc a ;store "b" to "a"
stfld class Foo Foo::X ;store "b" to an object on top of the stack that is no longer "a"!

As we see, the assignment happens to the object we didn't expected (cause we know that assignment goes from right to left).

var aClone = a;
a.x = a = b; 
Console.WriteLine(a.x == a); // false
Console.WriteLine(a.x == b); // false
Console.WriteLine(aClone.x == a); // true
Console.WriteLine(aClone.x == b); // true

Proposal

I found this behaviour kinda unobvious. Especially for those who aren't familiar with the "subtleties" of C#

So I want to suggest introducing compiler warnings for those cases where the left side of an assignment contains a variable, the object inside of which changes somewhere in the right side of the expression, since it wouldn't be very cool to replace a = b; a.x = b; with a similar a.x = a = b; and spend a few sleepless nights in search of a bug :D

@maxkoshevoi
Copy link
Contributor

maxkoshevoi commented Jun 7, 2019

Here is another code snippet that demonstrates this issue:

Annotation 2019-06-07 161419

Its also worth mentioning that Visual Studio aware of the difference between first = second; first.Next = first; and first.Next = first = second;. In the TestOne method it states that initial value in first variable is never used, but it doesn't mention that in the TestTwo method.

Annotation 2019-06-07 161055

@mavasani mavasani added 4 - In Review Approved-Rule Indicates if the new proposed rule has been approved to move to implementation phase Feature Request help wanted The issue is up-for-grabs, and can be claimed by commenting labels Sep 9, 2019
@mavasani mavasani added this to the vNext milestone Sep 9, 2019
@mavasani
Copy link
Member

mavasani commented Sep 12, 2019

Analyzer implemented with #2717

I am going to keep this issue open to track addition of a potential code fixer. I think we should offer following code fixes to the user to allow splitting their current statement into separate assignments for clarity. Let's assume original code on which the diagnostic fired: a.x = a = b;

  1. Fix 1: Splits the code into 2 statements as follows, which is the most likely original intent:
a = b;
a.x = b;
  1. Fix 2: Splits the code into 2 statements as follows, which is another possible intent:
a = b;
a.x = a;
  1. Fix 3 (may be?): Splits the code into statements as per the actual IL generated for original code:
var temp = a;
a = b;
temp.x = b;

@mavasani mavasani modified the milestones: vNext, Unknown Sep 12, 2019
@Youssef1313 Youssef1313 linked a pull request May 30, 2020 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Approved-Rule Indicates if the new proposed rule has been approved to move to implementation phase Feature Request help wanted The issue is up-for-grabs, and can be claimed by commenting Needs-Fixer
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants