如何获得 MethodInvocation 的完全限定目标?

How to get the fully-qualified target of a MethodInvocation?

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

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() 之间的差异(在分析器级别)?

我找到了一些东西,但我不习惯分析器,也许它可以简化并且只适用于你的情况。

@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);
}

问题是 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 无需进一步更改即可正常工作。

祝你好运!