Suppose I have "".GetType() which is ExpressionStatementSyntax (or maybe InvocationExpressionSyntax) in the syntax tree and I want to turn it into ("".GetType()), that is I want to turn my ExpressionStatementSyntax node into ParenthesizedExpressionSyntax node. How would I do that?
using System;
using System.Linq;
using Roslyn.Compilers;
using Roslyn.Compilers.CSharp;
class Program
{
static void Main(string[] args)
{
var oldRootNode = Syntax.ParseCompilationUnit(
"class C { void M() { \"\".GetType(); } }");
var oldStatementNode = oldRootNode.DescendantNodes().OfType<ExpressionStatementSyntax>().First();
var oldExpressionNode = oldStatementNode.Expression;
var newExpressionNode = Syntax.ParenthesizedExpression(oldExpressionNode);
var newRootNode = oldRootNode.ReplaceNode(oldExpressionNode, newExpressionNode);
Console.WriteLine(oldRootNode.ToString());
Console.WriteLine(newRootNode.ToString());
}
}
Related
How can I fix this code? I take an exception. This exception is System.InvalidOperationException: 'The item specified is not the element of a list.'
using System;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
var tree = SyntaxFactory.ParseExpression("(x + y) * z").SyntaxTree;
var root = (BinaryExpressionSyntax)tree.GetRoot();
foreach (var i in root.DescendantNodes())
{
if (i.Kind() == SyntaxKind.IdentifierName)
{
string str = "64";
var subTree = SyntaxFactory.ParseExpression(str).SyntaxTree;
var subRoot = (LiteralExpressionSyntax)subTree.GetRoot();
var subNode = subRoot.DescendantNodes().OfType<LiteralExpressionSyntax>();
var newRoot = root.ReplaceNode(root.FindNode(i.Span), subNode);
Console.WriteLine(newRoot);
}
}
Console.ReadLine();
}
}
}
I solve this problem like this
using System;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
var tree = SyntaxFactory.ParseExpression("(x + y) * z").SyntaxTree;
var root = (BinaryExpressionSyntax)tree.GetRoot();
foreach (var i in root.DescendantNodes())
{
if (i.Kind() == SyntaxKind.IdentifierName)
{
string str = "64";
var subTree = SyntaxFactory.ParseExpression(str).SyntaxTree;
var subRoot = (LiteralExpressionSyntax)subTree.GetRoot();
var newRoot = SyntaxNodeExtensions.ReplaceNode(root, i, subRoot);
Console.WriteLine(newRoot);
}
}
Console.ReadLine();
}
}
}
Using the Roslyn API with Visual Studio 2015, can I convert an object instance to source code? Can I create an extension method like ".ToSourceCode()" as shown below?
class Foo { }
class Program
{
static string classSourceCode = "class Foo { }";
static void Main()
{
var instance = new Foo();
var instanceSourceCode = instance.GetType().ToSourceCode();
System.Diagnostics.Debug.Assert(instanceSourceCode == classSourceCode);
}
}
No. However, ILSpy can.
Based on the comments on the question and what I understand about Roslyn, decompilation is not supported. However, thanks to #Bradley's ILSpy tip, there is a solution:
Download the ILSpy binaries from http://ilspy.net/
Reference the following assemblies: ICSharpCode.Decompiler.dll, ILSpy.exe, Mono.Cecil.dll, ILSpy.BamlDecompiler.Plugin.dll
Implement the ".ToSourceCode()" extension method as shown below:
using System;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using ICSharpCode.Decompiler;
using ICSharpCode.ILSpy;
using Mono.Cecil;
class Foo { }
class Program
{
static string classSourceCode = "using System; internal class Foo { } ";
static void Main()
{
var instance = new Foo();
var instanceSourceCode = instance.GetType().ToSourceCode();
System.Diagnostics.Debug.Assert(instanceSourceCode == classSourceCode);
}
}
static class TypeExtensions
{
public static string ToSourceCode(this Type source)
{
var assembly = AssemblyDefinition.ReadAssembly(Assembly.GetExecutingAssembly().Location);
var type = assembly.MainModule.Types.FirstOrDefault(t => t.FullName == source.FullName);
if (type == null) return string.Empty;
var plainTextOutput = new PlainTextOutput();
var decompiler = new CSharpLanguage();
decompiler.DecompileType(type, plainTextOutput, new DecompilationOptions());
return Regex.Replace(Regex.Replace(plainTextOutput.ToString(), #"\n|\r", " "), #"\s+", " ");
}
}
When I'm getting IInstanceReferenceExpression operation for type Instance() => this; instance kind is Explicit, but I expect that kind would be This. Am I missing something or this is a bug in Roslyn?
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Semantics;
using System;
using System.Linq;
internal static class so39495857
{
private static void Main()
{
var tree = CSharpSyntaxTree.ParseText(#"
class c
{
c Instance() => this;
static void Main() {}
}
");
var mscorlib = MetadataReference.CreateFromFile(typeof(object).Assembly.Location);
var compilation = CSharpCompilation.Create(null, new[] { tree }, new[] { mscorlib });
var model = compilation.GetSemanticModel(tree);
var node = tree.GetRoot().DescendantNodes().OfType<ArrowExpressionClauseSyntax>().First();
var operation = model.GetOperation(node);
var block = (IBlockStatement)operation;
var #return = (IReturnStatement)block.Statements.First();
var #this = (IInstanceReferenceExpression)#return.ReturnedValue;
Console.WriteLine(#this.InstanceReferenceKind);
}
}
Project on github - https://github.com/isanych/so-39495857
That's what Explicit means.
ThisClass is only for VB's MyClass keyword.
How can I get all the classes from a Roslyn compilation?
var sln = Path.Combine(path, "xxx.sln");
var workspace = MSBuildWorkspace.Create();
var solution = await workspace.OpenSolutionAsync(sln);
Project project = solution.Projects.First(x => x.Name == "bbb");
var compilation = await project.GetCompilationAsync();
This is how I visit all the classes in my solution.
class ClassVirtualizationVisitor : CSharpSyntaxRewriter
{
List<string> classes = new List<String>();
public override SyntaxNode VisitClassDeclaration(ClassDeclarationSyntax node)
{
node = (ClassDeclarationSyntax) base.VisitClassDeclaration(node);
string className = node.Identifier.ValueText;
classes.Add(className); // save your visited classes
return node;
}
}
Now use the visited classes:
var classVisitor = new ClassVirtualizationVisitor();
classVisitor.Visit(semanticModel.SyntaxTree.GetRoot());
var classes = classVisitor.classes; // list of classes in your solution
This now is my working Code:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.MSBuild;
using System.Threading.Tasks;
namespace Kardex.LC3xx.CreateApiDokumentation
{
//Patch to work with VS2013
// https://support.microsoft.com/en-us/kb/2971005
class Program
{
private static void Main(string[] args)
{
Run(args).Wait();
Console.ReadLine();
}
private async static Task Run(string[] args)
{
var path = Path.GetDirectoryName(typeof (Program).Assembly.Location);
var sln = Path.Combine(path, "xxx.sln");
var workspace = MSBuildWorkspace.Create();
var solution = await workspace.OpenSolutionAsync(sln);
Project project = solution.Projects.First(x => x.Name == "bbbb");
var compilation = await project.GetCompilationAsync();
foreach (var #class in compilation.GlobalNamespace.GetNamespaceMembers().SelectMany(x=>x.GetMembers()))
{
Console.WriteLine(#class.Name);
Console.WriteLine(#class.ContainingNamespace.Name);
}
var classVisitor = new ClassVirtualizationVisitor();
foreach (var syntaxTree in compilation.SyntaxTrees)
{
classVisitor.Visit(syntaxTree.GetRoot());
}
var classes = classVisitor.Classes;
}
class ClassVirtualizationVisitor : CSharpSyntaxRewriter
{
public ClassVirtualizationVisitor()
{
Classes = new List<ClassDeclarationSyntax>();
}
public List<ClassDeclarationSyntax> Classes { get; set; }
public override SyntaxNode VisitClassDeclaration(ClassDeclarationSyntax node)
{
node = (ClassDeclarationSyntax)base.VisitClassDeclaration(node);
Classes.Add(node); // save your visited classes
return node;
}
}
}
}
Create a SymbolVisitor that overrides VisitNamedType to process each type (which may not be a class).
Then, pass it to compilation.Assembly.Accept().
I have a promblem with run-time compiled classes. I have something like this 2 classes:
first class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Program.Bullet {
public class Class1{
private int i;
public Class1(int j){
i=j;
}
}
}
and second class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Program.Bullet;
namespace Program.Object {
public class Class2{
public Class2(){
Class1 c1 = new Class1(5);
}
}
}
This two classes I would like to compile in run-time and use them in my project. So I have function to compile it (XmlNode has data about fullPath etc):
private ModuleBuilder moduleBuilder;
private List<string> isCompiled;
private void Compile(XmlNode compileingClasses) {
foreach (XmlNode compileingClass in compileingClasses) {
string fullPath = compileingClass.Attributes["path"].InnerText;
string type = compileingClass.Attributes["name"].InnerText; ;
if (!isCompiled.Contains(type)) {
isCompiled.Add(type);
var syntaxTree = SyntaxTree.ParseFile("../../File1/File2/" + fullPath);
var comp = Compilation.Create("Test.dll"
, syntaxTrees: new[] { syntaxTree }
, references: metadataRef
, options: comilationOption
);
// Runtime compilation and check errors
var result = comp.Emit(moduleBuilder);
if (!result.Success) {
foreach (var d in result.Diagnostics) {
Console.WriteLine(d);
}
throw new XmlLoadException("Class not found " + fullPath);
}
}
}
}
Is it possible to get the reference on Class1 to Class2?
Edit: Better question
Is it possible to create MetadataReference on compiled Class1?
Something like:
string fullName = bullet.Attributes["fullName"].InnerText;
var o = moduleBuilder.GetType(fullName);
metadataRef.Add(new MetadataFileReference(o.Assembly.Location));
This throw NotSupportedException
You're trying to reference the assembly which is currently being built and I don't think Roslyn can do that.
What you can do instead is to create a single Compilation from all your classes (probably having a separate SyntaxTree for each class). If you do that, you won't need any references.