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

Counterintuitive interaction of null-conditional and null-forgiving operators #43659

Closed
AntonLapounov opened this issue Apr 24, 2020 · 2 comments · Fixed by #47712
Closed

Counterintuitive interaction of null-conditional and null-forgiving operators #43659

AntonLapounov opened this issue Apr 24, 2020 · 2 comments · Fixed by #47712

Comments

@AntonLapounov
Copy link
Member

According to the documentation, the null-forgiving operator has no effect at run time. However, it has a surprising side effect of breaking a null_conditional_operations chain at compile time, which may change evaluation of the expression. Consider this example:

int[] a = null;

// s1 is null: ToString() is not called at all
string s1 = a?.First().ToString();

// s2 is the empty string: ToString() is called on default(int?)
string s2 = a?.First()!.ToString();

Adding the ! operator effectively changes parsing of the expression to (a?.First())!.ToString(). Since this behavior is surprising for many developers (e.g., dotnet/docs#17988), I am wondering whether not allowing ! in null_conditional_operations was due to omission rather than intentional design. In the former case we might consider fixing this, in the latter — I would like to learn arguments that led to this decision.

@AntonLapounov
Copy link
Member Author

According to the null-conditional and null-forgiving operator specifications, a?.b! is not even a legal expression, because ! may follow only a primary_expression and not a null_conditional_expression:

null_conditional_expression
    : primary_expression null_conditional_operations
    ;

primary_expression
    : ...
    | null_forgiving_expression
    ;
    
null_forgiving_expression
    : primary_expression '!'
    ;

I guess that could be fixed by removing the break in ParsePostFixExpression:

case SyntaxKind.QuestionToken:
if (CanStartConsequenceExpression(this.PeekToken(1).Kind))
{
var qToken = this.EatToken();
var consequence = ParseConsequenceSyntax();
expr = _syntaxFactory.ConditionalAccessExpression(expr, qToken, consequence);
expr = CheckFeatureAvailability(expr, MessageID.IDS_FeatureNullPropagatingOperator);
break;
}
goto default;

@jcouv
Copy link
Member

jcouv commented Sep 14, 2020

LDM decided to push this into C# 9 and settled on syntax/semantics (9/14/2020).

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

Successfully merging a pull request may close this issue.

4 participants