2017-02-28 26 views
4

我是Roslyn的新手。我想知道是否有一種方法可以告訴一個變量是否在語義模型中某個位置的範圍內。爲了對我正在做什麼有一些背景知識,我試圖對foreach塊進行轉換,以遍歷Select的結果,例如,形式的如何判斷某個變量是否在Roslyn的某個語法節點的範圍內?

foreach (string str in new int[0].Select(i => i.ToString())) 
{ 
} 

foreach (int item in new int[0]) 
{ 
    string str = item.ToString(); 
} 

這裏是我的代碼修復提供商的相關部分。目前,我硬編碼的迭代變量是item

var ident = SyntaxFactory.Identifier("item"); 

然後,我檢索選擇的SimpleLambdaExpressionSyntaxBody,並且(在上述情況下)與item替換參數i獲得item.ToString()

var paramTokens = from token in selectorBody.DescendantTokens() 
        where token.Text == selectorParam.Identifier.Text 
        select token; 
selectorBody = selectorBody.ReplaceTokens(paramTokens, (_, __) => ident); 

我想知道是否有辦法告訴了一個名爲item變量是否已經在範圍在foreach塊的位置,所以我的代碼修復提供商不基因評估衝突的變量聲明。這可能以某種方式實現使用SemanticModel/Symbol /等。蜜蜂?

謝謝。

回答

1

我可以想到兩種方法來做到這一點。

使用此測試代碼,所以我可以測試不同的聲明(字段,屬性,變量,類名)

const string code = @" 
    public class AClass{ 
     private int MyFld = 5; 
     protected double MyProp{get;set;} 
     public void AMethod(){ 
      string myVar = null; 
      for (int myIterator=0; myIterator<10;myIterator++) 
       foreach (string str in new int[0].Select(i => i.ToString())){ } 
     } 
     public void AnotherMethod() 
     { 
      string anotherVar = null; 
     } 
    }"; 

-

void Main() 
{ 
    var tree = CSharpSyntaxTree.ParseText(code); 
    var root = tree.GetRoot(); 

    var startNode = root 
     .DescendantNodes() 
     .OfType<SimpleLambdaExpressionSyntax>() // start at the Select() lambda 
     .FirstOrDefault(); 

    FindSymbolDeclarationsInAncestors(startNode, "myVar").Dump(); // True 
    FindSymbolDeclarationsInAncestors(startNode, "anotherVar").Dump(); // False 

    CompilationLookUpSymbols(tree, startNode, "myVar").Dump(); // True 
    CompilationLookUpSymbols(tree, startNode, "anotherVar").Dump(); // False 
} 

// You could manually traverse the ancestor nodes, and find the different DeclarationSyntax-es. 
// I may have missed some, like CatchDeclarationSyntax.. 
// Error-prone but more fun. 
public bool FindSymbolDeclarationsInAncestors(CSharpSyntaxNode currentNode, string symbolToFind) 
{ 
    return currentNode 
     .Ancestors().SelectMany(a => a.ChildNodes()) // get direct siblings 
     .SelectMany(node => // find different declarations 
      (node as VariableDeclarationSyntax)?.Variables.Select(v => v.Identifier.ValueText) 
      ?? (node as FieldDeclarationSyntax)?.Declaration?.Variables.Select(v => v.Identifier.ValueText) 
      ?? (node as LocalDeclarationStatementSyntax)?.Declaration?.Variables.Select(v => v.Identifier.ValueText) 
      ?? new[] { 
       (node as PropertyDeclarationSyntax)?.Identifier.ValueText, 
       (node as MethodDeclarationSyntax)?.Identifier.ValueText, 
       (node as ClassDeclarationSyntax)?.Identifier.ValueText, 
       }) 
     .Any(member => string.Equals(member, symbolToFind)); 
} 

// Or use the SemanticModel from the CSharpCompilation. 
// Possibly slower? Also, not as much fun as manually traversing trees. 
public bool CompilationLookUpSymbols(SyntaxTree tree, CSharpSyntaxNode currentNode, string symbolToFind) 
{ 
    var compilation = CSharpCompilation.Create("dummy", new[] { tree }); 
    var model = compilation.GetSemanticModel(tree); 
    return model.LookupSymbols(currentNode.SpanStart, name: symbolToFind).Any(); 
} 
相關問題