Parsing nameof expressions in Roslyn
Asked Answered
C

2

10

I'm trying to do something with nameof expressions in a CSharpSyntaxWalker, however, I noticed that there is no NameOfExpressionSyntax in the AST. Instead I get an InvocationExpressionSyntax for which SemanticModel.GetSymbolInfo returns no matching symbols, and the expression of the invocation is an IdentifierNameSyntax containing an identifier token "nameof".

So to recognize nameof expressions I would have added a special case to VisitInvocationExpression, looking for whether GetSymbolInfo returns anything and if not, looking for whether the identifier is nameof. However, that sounds a bit iffy to me. Is there a better way maybe which shifts that sort of detection logic to the parser?

(P.S.: I know this is probably parsed like this for backwards compatibility reasons; just wondering whether there is an API for distinguishing nameof and normal invocations.)

Caldarium answered 5/9, 2016 at 7:22 Comment(4)
Someone else has noticed this too.Remake
There still isn't a formal spec for c# 6, but this draft does seem to confirm that it's an ambiguous parse and so additional reasoning is required.Catton
@Damien_The_Unbeliever: I actually tried finding the place in Roslyn's source code where it determines that but couldn't in a cursory search. Maybe I should look again.Caldarium
Nope, do it as you're doing. Considering anyone could've had a nameof() method prior to C# 6, there is no way to guarantee that it isn't referring to a method in the class or imported via a static using. At the time of syntax parsing there is no notion of a semantic model yet and afterwards it is obviously too late. Anyone doing nameof parsing has to wire up their own solution ;-)Torso
C
2

I now indeed used the following snippet:

if (symbolInfo.Symbol == null &&
    symbolInfo.CandidateSymbols.IsEmpty &&
    symbolInfo.CandidateReason == CandidateReason.None) {
  var identifier = node.Expression as IdentifierNameSyntax;
  if (identifier != null && identifier.Identifier.Kind() == SyntaxKind.IdentifierToken && identifier.Identifier.Text == "nameof") {
    // We have a nameof expression
  }
}

I opted not to exploit the constant value for detection just in case C# 8 or so adds yet a different operator in that vein, that might also have a constant value, but is not nameof. The detection pretty much detects exactly what the specification says is used for determining an invocation being a nameof expression:

Because nameof is not a reserved keyword, a nameof expression is always syntactically ambiguous with an invocation of the simple name nameof. For compatibility reasons, if a name lookup of the name nameof succeeds, the expression is treated as an invocation_expression – regardless of whether the invocation is legal. Otherwise it is a nameof_expression.

Caldarium answered 7/9, 2016 at 7:29 Comment(0)
R
7

nameof expressions are compile-time constant. You can use that fact to distinguish it from normal invocations. You can call SematicModel.GetConstantValue() on the InvocationExpressionSyntax. In case it's a nameof, you get back the string / name inside the Optional<object>.Value (HasValue also returns true).

Reni answered 5/9, 2016 at 13:45 Comment(1)
GetConstantValue won't help me in that case, as I need to map the symbol to a custom name, but granted, that's probably a good way of distinguishing it from other invocations which usually are not constants.Caldarium
C
2

I now indeed used the following snippet:

if (symbolInfo.Symbol == null &&
    symbolInfo.CandidateSymbols.IsEmpty &&
    symbolInfo.CandidateReason == CandidateReason.None) {
  var identifier = node.Expression as IdentifierNameSyntax;
  if (identifier != null && identifier.Identifier.Kind() == SyntaxKind.IdentifierToken && identifier.Identifier.Text == "nameof") {
    // We have a nameof expression
  }
}

I opted not to exploit the constant value for detection just in case C# 8 or so adds yet a different operator in that vein, that might also have a constant value, but is not nameof. The detection pretty much detects exactly what the specification says is used for determining an invocation being a nameof expression:

Because nameof is not a reserved keyword, a nameof expression is always syntactically ambiguous with an invocation of the simple name nameof. For compatibility reasons, if a name lookup of the name nameof succeeds, the expression is treated as an invocation_expression – regardless of whether the invocation is legal. Otherwise it is a nameof_expression.

Caldarium answered 7/9, 2016 at 7:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.