In a C# incremental generator I am authoring, beginning from an ArgumentSyntax
, I am trying to inspect the inline initializer for a readonly
field referenced as a method argument. Understanding that value inspection is impossible, I thought that I could traverse the syntax tree to find the definition of the field, and if it had an inline initializer, then I could at least use that value (with the understanding that the field could be overwritten in a constructor).
var argumentOperation = (IArgumentOperation)context.SemanticModel.GetOperation(argumentSyntax, cancellationToken)!;
switch (argumentOperation.Parameter?.Name)
{
case "argumentToInspect":
break;
default:
return;
}
if (argumentOperation.Value is not IFieldReferenceOperation fieldReferenceOperation ||
!fieldReferenceOperation.Field.IsReadOnly)
{
return;
}
var fieldSyntaxNode = fieldReferenceOperation.Field.DeclaringSyntaxReferences[0].GetSyntax(cancellationToken);
Debug.WriteLine($"fieldSyntaxNode kind: {fieldSyntaxNode.Kind()}"); // VariableDeclarator
Debug.WriteLine($"parent kind: {fieldSyntaxNode.Parent.Kind()}"); // VariableDeclaration
Debug.WriteLine($"grandparent kind: {fieldSyntaxNode.Parent.Parent.Kind()}"); // FieldDeclaration
var nodeOperation = context.SemanticModel.GetOperation(fieldSyntaxNode, cancellationToken); // null
var parentOperation = context.SemanticModel.GetOperation(fieldSyntaxNode.Parent, cancellationToken); // null
var grandparentOperation = context.SemanticModel.GetOperation(fieldSyntaxNode.Parent.Parent, cancellationToken); // null
The field in question is declared such as:
private static readonly MyClass instance = new();
Which is then referenced as:
MyMethod(instance);
Given that I am able to find the FieldDeclarationSyntax
(fieldSyntaxNode.Parent.Parent
), I'm not sure why I'm unable to find any relevant IOperation
s (e.g., IVariableDeclaratorOperation
, IVariableDeclarationOperation
, or IFieldInitializerOperation
). The field declaration and field reference passed as an argument (to MyMethod
) are in the same file, so my understanding is that the same semantic model (context.SemanticModel
) should be able to produce the relevant operations, but I am only getting null
from GetOperation
on the field declaration.
Am I approaching this the wrong way? Given that I can find the syntax of the field declaration, I could try to manually parse the text for the information that I need, but I don't understand why I can't use the IOperation
API to inspect the field initializer.
While I am still unclear as to which nodes might generate either IVariableDeclaratorOperation
or IVariableDeclarationOperation
, I was able to discover the IFieldInitializerOperation
(which is ultimately the one I was actually looking for).
In the case of an inline field initializer, GetOperation
will yield the IFieldInitializerOperation
for the EqualsValueClauseSyntax
node, which is a descendant node of the FieldDeclarationSyntax
.
Tangentially related to what I originally asked, I can discover an initializing assignment in the constructor by finding an AssignmentExpressionSyntax
with Kind
of SimpleAssignmentExpression
which has a ConstructorDeclarationSyntax
ancestor node. From this node I can receive an ISimpleAssignmentOperation
with a Target
of IFieldReferenceOperation
.
Given the Value
of the IFieldInitializationOperation
and/or ISimpleAssignmentOperation
, I am able to use this information to more powerfully (and correctly) parse the initialization of readonly
fields.