Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Replace all variables in C# code with methods

I need to parse fragments of user-written C# code and replace all variables that aren't defined locally with method calls. I.e.

public class Foo
{
  public dynamic Bar()
  {
     return Math.Min(x + width, maxWidth);
  }
}

has to become:

public class Foo
{
  public dynamic Bar()
  {
      return Math.Min(Resolve("x") + Resolve("width"), Resolve("maxWidth"));
  }
}

I'm using Microsoft.CodeAnalysis.CSharp and the CSharpSyntaxTree to examine the string, but it doesn't give me enough information to perform the replace. Or if it does, I don't know where to look for it. I've pasted the SyntaxTree layout below. All the variables occur as IdentifierName nodes, but I don't know how to tell different IdentifierNames apart. Where to go from here?

CompilationUnit[0..99) {
 code:  public class Foo\n{\n  public dynamic Bar()\n  {\n    return Math.Min(x + width, maxWidth);\n  }\n}
 tokens: EndOfFileToken[] 
 nodes{
  ClassDeclaration[0..99) {
   code:  public class Foo\n{\n  public dynamic Bar()\n  {\n    return Math.Min(x + width, maxWidth);\n  }\n}
   tokens: PublicKeyword[public ] ClassKeyword[class ] IdentifierToken[Foo\n] OpenBraceToken[{\n] CloseBraceToken[}] 
   nodes{
    MethodDeclaration[21..98) {
     code:    public dynamic Bar()\n  {\n    return Math.Min(x + width, maxWidth);\n  }\n
     tokens: PublicKeyword[  public ] IdentifierToken[Bar] 
     nodes{
      IdentifierName[30..38) {
       code:  dynamic 
       tokens: IdentifierToken[dynamic ] 
      }
      ParameterList[41..45) {
       code:  ()\n
       tokens: OpenParenToken[(] CloseParenToken[)\n] 
      }
      Block[45..98) {
       code:    {\n    return Math.Min(x + width, maxWidth);\n  }\n
       tokens: OpenBraceToken[  {\n] CloseBraceToken[  }\n] 
       nodes{
        ReturnStatement[50..93) {
         code:      return Math.Min(x + width, maxWidth);\n
         tokens: ReturnKeyword[    return ] SemicolonToken[;\n] 
         nodes{
          InvocationExpression[61..90) {
           code:  Math.Min(x + width, maxWidth)
           nodes{
            SimpleMemberAccessExpression[61..69) {
             code:  Math.Min
             tokens: DotToken[.] 
             nodes{
              IdentifierName[61..65) {
               code:  Math
               tokens: IdentifierToken[Math] 
              }
              IdentifierName[66..69) {
               code:  Min
               tokens: IdentifierToken[Min] 
              }
             }
            }
            ArgumentList[69..90) {
             code:  (x + width, maxWidth)
             tokens: OpenParenToken[(] CommaToken[, ] CloseParenToken[)] 
             nodes{
              Argument[70..79) {
               code:  x + width
               nodes{
                AddExpression[70..79) {
                 code:  x + width
                 tokens: PlusToken[+ ] 
                 nodes{
                  IdentifierName[70..72) {
                   code:  x 
                   tokens: IdentifierToken[x ] 
                  }
                  IdentifierName[74..79) {
                   code:  width
                   tokens: IdentifierToken[width] 
                  }
                 }
                }
               }
              }
              Argument[81..89) {
               code:  maxWidth
               nodes{
                IdentifierName[81..89) {
                 code:  maxWidth
                 tokens: IdentifierToken[maxWidth] 
                }
               }
              }
             }
            }
           }
          }
         }
        }
       }
      }
     }
    }
   }
  }
 }
}
like image 276
David Rutten Avatar asked Jul 17 '14 08:07

David Rutten


People also ask

How do you change all variables at once?

Place the cursor inside a variable you want to rename and press Ctrl + R . Then type other name and editor will change all occurrences of that variable. Save this answer.

How do I Rename all variables in VS?

Select Edit > Refactor > Rename. Right-click the code and select Rename.

Can we change variable value in C?

We can change the value of a variable in C or any other language, and we can also reuse it multiple times.


3 Answers

I think you need to use the semantic model. Here's a (very basic) example that shows how to find unresolved symbols:

var tree = CSharpSyntaxTree.ParseFile(fileName);
var root = tree.GetRoot();
var refs = new MetadataReference[]
{
    new MetadataFileReference(@"C:\Windows\Microsoft.NET\Framework\v4.0.30319\mscorlib.dll", new MetadataReferenceProperties(MetadataImageKind.Assembly))
};
var compilation = CSharpCompilation.Create("testRoslyn", new[] { tree }, refs);
var model = compilation.GetSemanticModel(tree);

var unknownSymbols =
    from node in root.DescendantNodes()
    where node.IsKind(SyntaxKind.IdentifierName)
    let symbolInfo = model.GetSymbolInfo(node)
    where symbolInfo.Symbol == null && !symbolInfo.CandidateSymbols.Any()
    select node;

From there you can replace the nodes with Resolve(name).

like image 139
Thomas Levesque Avatar answered Oct 17 '22 13:10

Thomas Levesque


This post doesn't really offer you a solution to your problem, but instead another way that may be easier to implement, but introduce a change for the user. I post here only as an idea.

The objective is not to allow the user to write x but instead Var.x or Var.maxWidth

Then, when parsing your C# code, you just need to insert code for a property Var of type CustomDynamicObject (or any name you want to give)

public (static?) CustomDynamicObject Var { get { /* create the object once and return */ }}

Then you can defined CustomDynamicObject inheriting DynamicObject, so that you can intercept all calls to undefined methods/property

DynamicObject and using dynamic feature of .NET 4 is just a way to intercept call, but you can google for other techniques.

like image 45
Fabske Avatar answered Oct 17 '22 13:10

Fabske


Perhaps you accidentally paste CSharpSyntaxTree of code without declaration "double width = 10.0;"? If so, you get this additional declarations in your CSharpSyntaxTree.

All you need to do is just scan the tree for IdentifierToken`s that not declarated in user code, all this tokens have positions that you must use for replace variables acces code to method call code.

like image 33
gabba Avatar answered Oct 17 '22 15:10

gabba