I am attempting to use the Roslyn SDK and StackExchange.Precompilation
(thank you!) to implement aspect-oriented programming in C#6. My specific problem right now is, starting with an IdentifierNameSyntax
instance, I want to find the "member type" (method, property, field, var, etc.) that the identifier refers to. (How) can this be done?
Background:
The first proof-of-concept I am working on is some good old design-by-contract. I have a NonNullAttribute
which can be applied to parameters, properties, or method return values. Along with the attribute there is a class implementing the StackExchange.Precompilation.ICompileModule
interface, which on compilation will insert null checks on the marked parameters or return values.
This is the same idea as PostSharp's NonNullAttribute
, but the transformation is being done on one of Roslyn's syntax trees, not on an already compiled assembly. It is also similar to Code Contracts, but with a declarative attribute approach, and again operating on syntax trees not IL.
For example, this source code:
[return: NonNull]
public string Capitalize([NonNull] string text) {
return text.ToUpper();
}
will be transformed into this during precompilation:
[return: NonNull]
public string Capitalize([NonNull] string text) {
if (Object.Equals(text, null))
throw new ArgumentNullException(nameof(text));
var result = text.ToUpper();
if (Object.Equals(result, null))
throw new PostconditionFailedException("Result cannot be null.");
return result;
}
(PostconditionFailedException
is a custom exception I made to compliment ArgumentException
for return values. If there is already something like this in the framework please let me know.)
For properties with this attribute, there would be a similar transformation, but with preconditions and postconditions implemented separately in the set
and get
accessors, respectively.
The specific reason I need to find the "member type" of an identifier here is for an optimization on implementing postconditions. Note in the post-compilation sample above, the value that would have been returned is stored in a local variable, checked, and then the local is returned. This storage is necessary for transforming return
statements that evaluate a method or complex expression, but if the returned expression is just a field or local variable reference, creating that temporary storage local is wasteful.
So, when the return
statement is being scanned, I first check if the statement is of the form ReturnKeyword
-IdentifierSyntaxToken
-SemicolonToken
. If so, I then need to check what that identifier refers to, so I avoid that local variable allocation if the referent is a field or var.
Update For more context, check out the project this is in reference to on GitHub.
You'll need to use SemanticModel.GetSymbolInfo
to determine the symbol an identifier binds to.