【问题标题】:How to get the fully-qualified target of a MethodInvocation?如何获得 MethodInvocation 的完全限定目标?
【发布时间】:2017-01-16 21:38:25
【问题描述】:

我正在尝试查找特定方法的用法。在这种情况下,我想找到 foo.go() 的用法/调用,但不是 bar.go()。下面的代码将在任何类上找到go() 的所有调用。在下面的代码中,node.target 给了我简单的 x,这是 var 名称,但我很难弄清楚该方法属于哪个类。

test.dart

void main() {
  var x = new Foo();
  x.go();

  x = new Bar();
  x.go();
}

class Foo {
  go() {
    print('I am foo');
  }
}

class Bar {
  go() {
    print('I am bar');
  }
}

analyze.dart

import 'package:analyzer/analyzer.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/visitor.dart';

void main() {
  var targetFile = parseDartFile('test.dart');
  var visitor    = new UsageVisitor('foo.go');
  targetFile.visitChildren(visitor);
}

class UsageVisitor extends UnifyingAstVisitor {
  String _class;
  String _method;

  UsageVisitor(this._class, this._method);

  @override
  visitMethodInvocation(MethodInvocation node) {
    print({
      'target'    : node.target.toString(),    // in both cases, gives "x" -- I need "Foo" and "Bar", respectively
      'methodName': node.methodName.toString() // gives "go"
    });

    return super.visitNode(node);
  }
}

我如何区分foo.go()bar.go() 之间的区别(在分析器级别)?

【问题讨论】:

    标签: dart abstract-syntax-tree static-analysis


    【解决方案1】:

    问题在于 test.dart 正在在没有上下文的情况下解析需要解析其中包含的元素。

    analyzer package within the Dart SDK 中有一个例子

    将其分解为您拥有的步骤:

    1 - 设置解析器

    PhysicalResourceProvider resourceProvider = PhysicalResourceProvider.INSTANCE;
    DartSdk sdk = new FolderBasedDartSdk(resourceProvider, resourceProvider.getFolder(args[0]));
    
    var resolvers = [
      new DartUriResolver(sdk),
      new ResourceUriResolver(resourceProvider)
    ];
    

    2 - 使用解析器创建AnalysisContext

    AnalysisContext context = AnalysisEngine.instance.createAnalysisContext()
      ..sourceFactory = new SourceFactory(resolvers);
    

    3 - 将您的 Source 添加到 ChangeSet

    Source source = new FileSource(resourceProvider.getFile(args[1]));
    ChangeSet changeSet = new ChangeSet()..addedSource(source);
    

    3.1 - 将ChangeSet 应用到AnalysisContext

    context.applyChanges(changeSet);
    

    3.2 - 获取LibraryElement

    我不确定这是否总是需要。也许对于简单的文件可以跳过它。¯(◉◡◔)/¯

    LibraryElement libElement = context.computeLibraryElement(source);
    

    4 - 解析你的 Source!

    CompilationUnit resolvedUnit = context.resolveCompilationUnit(source, libElement);
    

    5 - 运行你的Visitor

    resolvedUnit.accept(visitor)
    

    不幸的是,这比仅仅调用 parseDartFile() 更多的代码,但好消息是你的 Visitor 无需进一步更改即可工作。

    祝你好运!

    【讨论】:

    • 这太棒了!正是我需要的!
    【解决方案2】:

    我发现了一些东西,但我不习惯分析仪,也许它可以简化,它只适用于你的情况。

    @override
    visitMethodInvocation(MethodInvocation node) {
      if (node.methodName.toString() == "go" && node.target.toString() == "x") {
        for (dynamic child in node.parent?.parent?.childEntities) {
          if (child is VariableDeclarationStatement) {
            if ((child.childEntities.first as VariableDeclarationList).type.toString() == "Foo" &&
              ((child.childEntities.first as VariableDeclarationList).childEntities.elementAt(1) as VariableDeclaration)
                      .beginToken
                      .toString() ==
                  "x") {
              print(node);
            }
          }
        }
      }
      return super.visitNode(node);
    }
    

    【讨论】:

    • 感谢您的努力,但您是对的,这仅适用于我非常简单的示例代码。一旦你进入“真实”(更复杂的)代码并引入 if/else 块、闭包、方法、类等,这种方法就变得站不住脚了。
    猜你喜欢
    • 2015-05-17
    • 2014-10-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多