微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

c# – 如何在Roslyn的某个语法节点上判断变量是否在范围内?

我是Roslyn的新手.我想知道是否有一种方法可以判断变量是否在语义模型中某个位置的范围内.为了给出我正在做的事情的一些背景知识,我试图转换遍历Select的结果的foreach块,例如,形式

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");

然后,我正在检索选择器的SimpleLambdaExpressionSyntax的Body,并且(在上面的例子中)用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 / etc.蜜蜂?

谢谢.

解决方法

我可以想到两种方法.

使用这个测试代码,我可以测试不同的声明(字段,属性,变量,类名)

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,"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();
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐