[Edit] Solution: Problem were solved by below answers. I used this part of code:
SyntaxFactory.IdentifierName(
SyntaxFactory.Identifier(
SyntaxFactory.TriviaList(),
SyntaxKind.NameOfKeyword,
"nameof",
"nameof",
SyntaxFactory.TriviaList()))
Unfortunately there is no pattern as SyntaxFactory.TypeOfExpression.
Problem: I am writing a code generator (using Roslyn) which uses nameof. When I try to compile prepared SyntaxTree with Roslyn it fails with error "error CS0103: The name 'nameof' does not exist in the current context", whereas when I parse as SyntaxTree the full text of the same SyntaxTree compilation is done with no error.
That's my first post on StackOverflow, thank you in advance for your understanding.
I am writing a code generator (using Roslyn) which uses nameof. My compilation unit preparation stands as below:
private static CompilationUnitSyntax PrepareCompilationUnit(bool useNameof)
{
ArrowExpressionClauseSyntax arrowExpression = useNameof
? SyntaxFactory.ArrowExpressionClause(
SyntaxFactory.InvocationExpression(
SyntaxFactory.IdentifierName(
SyntaxFactory.Token(SyntaxKind.NameOfKeyword).ToString()),
SyntaxFactory.ArgumentList(
SyntaxFactory.SeparatedList(
new[] { SyntaxFactory
.Argument(SyntaxFactory
.IdentifierName(PROPERTY_NAME)) }))))
: SyntaxFactory.ArrowExpressionClause(
SyntaxFactory.LiteralExpression(
SyntaxKind.StringLiteralExpression,
SyntaxFactory.Literal(PROPERTY_NAME)));
PropertyDeclarationSyntax property = SyntaxFactory.PropertyDeclaration(
SyntaxFactory.ParseTypeName("string"),
SyntaxFactory.ParseName(PROPERTY_NAME).ToString())
.AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword))
.WithExpressionBody(arrowExpression)
.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken));
ClassDeclarationSyntax classDefinition =
SyntaxFactory
.ClassDeclaration(
SyntaxFactory
.Identifier(
SyntaxFactory
.ParseTypeName(CLASS_NAME).ToString()))
.AddModifiers(
SyntaxFactory.Token(SyntaxKind.PublicKeyword))
.AddMembers(new[] { property });
NamespaceDeclarationSyntax @namespace = SyntaxFactory
.NamespaceDeclaration(
SyntaxFactory
.ParseName(NAMESPACE_NAME))
.AddMembers(classDefinition)
.NormalizeWhitespace();
return SyntaxFactory
.CompilationUnit()
.WithMembers(
SyntaxFactory
.SingletonList<MemberDeclarationSyntax>(@namespace));
}
After compilation unit is prepared, I try to compile this and create instance of prepared class in 4 listed below ways:
using nameof keyword
get text from compilation unit SyntaxTree, then build new SyntaxTree based on this text, and compile new SyntaxTree -> It works -> I get a properly created instance
build new SyntaxTree based on compilation unit SyntaxTree, and compile new SyntaxTree -> It doesn't works -> I get a "error CS0103: The name 'nameof' does not exist in the current context" during compilation
using literal string
get text from compilation unit SyntaxTree, then build new SyntaxTree based on this text, and compile new SyntaxTree -> It works -> i get a properly created instance
build new SyntaxTree based on compilation unit SyntaxTree, and compile new SyntaxTree -> It works -> I get a properly created instance
Main function look like:
private static void Main(string[] args)
{
CompilationUnitSyntax compilationUnit = PrepareCompilationUnit(useNameof: true);
SaveToFile(compilationUnit);
object test1 = CreateInstance(
compilationUnit: compilationUnit,
compileFromParsedString: true); // return instance
object test2 = CreateInstance(
compilationUnit: compilationUnit,
compileFromParsedString: false); // return null
compilationUnit = PrepareCompilationUnit(useNameof: false);
SaveToFile(compilationUnit);
test1 = CreateInstance(
compilationUnit: compilationUnit,
compileFromParsedString: true); // return instance
test2 = CreateInstance(
compilationUnit: compilationUnit,
compileFromParsedString: false); // return instance
}
CreateInstance code:
private static object CreateInstance(
CompilationUnitSyntax? compilationUnit,
bool compileFromParsedString = false)
{
object result = null;
var options = new CSharpParseOptions(LanguageVersion.Latest);
var syntaxTree = compileFromParsedString
? SyntaxFactory.ParseSyntaxTree(compilationUnit.ToFullString(), options)
: SyntaxFactory.SyntaxTree(compilationUnit, options);
var trustedAssembliesPaths = ((string)AppContext
.GetData("TRUSTED_PLATFORM_ASSEMBLIES"))
.Split(Path.PathSeparator);
var neededAssemblies = new[]
{
typeof(object).Assembly.GetName().Name
};
List<PortableExecutableReference> references = trustedAssembliesPaths
.Where(p => neededAssemblies.Contains(Path.GetFileNameWithoutExtension(p)))
.Select(p => MetadataReference.CreateFromFile(p))
.ToList();
var compilationOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
.WithOverflowChecks(true)
.WithOptimizationLevel(OptimizationLevel.Debug);
var compilation = CSharpCompilation
.Create(Guid.NewGuid().ToString("N"), options: compilationOptions)
.AddReferences(references)
.AddSyntaxTrees(syntaxTree);
try
{
using MemoryStream dllStream = new MemoryStream();
Assembly assembly = null;
EmitResult emitResult = compilation.Emit(dllStream);
if (emitResult.Success)
{
assembly = Assembly.Load(dllStream.ToArray());
result = assembly.CreateInstance($"{NAMESPACE_NAME}.{CLASS_NAME}");
}
else
{
foreach (var el in emitResult.Diagnostics)
{
Console.WriteLine(el);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
return result;
}
I found, that the problem is on nameof expression creation. The problem is solved (create valid instance) as well when i use expression parser instead of creating invocationExpression manually:
var parsed = SyntaxFactory.ParseExpression($"nameof({PROPERTY_NAME})"))
var created = SyntaxFactory.InvocationExpression(
SyntaxFactory.IdentifierName(
SyntaxFactory.Token(SyntaxKind.NameOfKeyword).ToString()),
SyntaxFactory.ArgumentList(
SyntaxFactory.SeparatedList(
new[] { SyntaxFactory
.Argument(SyntaxFactory
.IdentifierName(PROPERTY_NAME)) }))))
I realized that the problem is caused by RawContextualkind of node for IdentifierNameSyntax. It is SyntaxKind.NameOfKeyword for parsed expression, and SyntaxKind.IdentifierToken for created one.
Can anybody tell me how to create nameof expression without parsing a string (it seems to me as too hardcoded)?
Generated code (using nameof and string literal):
namespace namespaceExample
{
public class ClsExample
{
public string TestName => nameof(TestName);
}
}
namespace namespaceExample
{
public class ClsExample
{
public string TestName => "TestName";
}
}