Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(67)

Unified Diff: pkg/analyzer/test/src/dart/analysis/index_test.dart

Issue 2519733002: Add CompilationUnit indexer implementation. (Closed)
Patch Set: Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: pkg/analyzer/test/src/dart/analysis/index_test.dart
diff --git a/pkg/analyzer/test/src/dart/analysis/index_test.dart b/pkg/analyzer/test/src/dart/analysis/index_test.dart
new file mode 100644
index 0000000000000000000000000000000000000000..fd6738e80f8b68a7743b34d6a58bd9a79303f2b8
--- /dev/null
+++ b/pkg/analyzer/test/src/dart/analysis/index_test.dart
@@ -0,0 +1,1353 @@
+// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:convert';
+
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/dart/element/visitor.dart';
+import 'package:analyzer/file_system/file_system.dart';
+import 'package:analyzer/file_system/memory_file_system.dart';
+import 'package:analyzer/source/package_map_resolver.dart';
+import 'package:analyzer/src/dart/analysis/byte_store.dart';
+import 'package:analyzer/src/dart/analysis/driver.dart';
+import 'package:analyzer/src/dart/analysis/file_state.dart';
+import 'package:analyzer/src/dart/analysis/index.dart';
+import 'package:analyzer/src/generated/engine.dart' show AnalysisOptionsImpl;
+import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer/src/summary/format.dart';
+import 'package:analyzer/src/summary/idl.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import '../../context/mock_sdk.dart';
+
+main() {
+ defineReflectiveSuite(() {
+ defineReflectiveTests(IndexTest);
+ });
+}
+
+/**
+ * Finds an [Element] with the given [name].
+ */
+Element findChildElement(Element root, String name, [ElementKind kind]) {
+ Element result = null;
+ root.accept(new _ElementVisitorFunctionWrapper((Element element) {
+ if (element.name != name) {
+ return;
+ }
+ if (kind != null && element.kind != kind) {
+ return;
+ }
+ result = element;
+ }));
+ return result;
+}
+
+/**
+ * A function to be called for every [Element].
+ */
+typedef void _ElementVisitorFunction(Element element);
+
+class ExpectedLocation {
+ final CompilationUnitElement unitElement;
+ final int offset;
+ final int length;
+ final bool isQualified;
+
+ ExpectedLocation(
+ this.unitElement, this.offset, this.length, this.isQualified);
+
+ @override
+ String toString() {
+ return '(unit=$unitElement; offset=$offset; length=$length;'
+ ' isQualified=$isQualified)';
+ }
+}
+
+@reflectiveTest
+class IndexTest {
+ static final MockSdk sdk = new MockSdk();
+
+ final MemoryResourceProvider provider = new MemoryResourceProvider();
+ final ByteStore byteStore = new MemoryByteStore();
+ final FileContentOverlay contentOverlay = new FileContentOverlay();
+
+ final StringBuffer logBuffer = new StringBuffer();
+ PerformanceLog logger;
+
+ AnalysisDriverScheduler scheduler;
+ AnalysisDriver driver;
+
+ String testProject;
+ String testFile;
+
+ String testCode;
+ CompilationUnit testUnit;
+ CompilationUnitElement testUnitElement;
+ LibraryElement testLibraryElement;
+
+ AnalysisDriverUnitIndex index;
+
+ _ElementIndexAssert assertThat(Element element) {
+ List<_Relation> relations = _getElementRelations(element);
+ return new _ElementIndexAssert(this, element, relations);
+ }
+
+ _NameIndexAssert assertThatName(String name) {
+ return new _NameIndexAssert(this, name);
+ }
+
+ Element findElement(String name, [ElementKind kind]) {
+ return findChildElement(testUnitElement, name, kind);
+ }
+
+ int findOffset(String search) {
+ int offset = testCode.indexOf(search);
+ expect(offset, isNonNegative, reason: "Not found '$search' in\n$testCode");
+ return offset;
+ }
+
+ int getLeadingIdentifierLength(String search) {
+ int length = 0;
+ while (length < search.length) {
+ int c = search.codeUnitAt(length);
+ if (c >= 'a'.codeUnitAt(0) && c <= 'z'.codeUnitAt(0)) {
+ length++;
+ continue;
+ }
+ if (c >= 'A'.codeUnitAt(0) && c <= 'Z'.codeUnitAt(0)) {
+ length++;
+ continue;
+ }
+ if (c >= '0'.codeUnitAt(0) && c <= '9'.codeUnitAt(0)) {
+ length++;
+ continue;
+ }
+ break;
+ }
+ return length;
+ }
+
+ CompilationUnitElement importedUnit({int index: 0}) {
+ List<ImportElement> imports = testLibraryElement.imports;
+ return imports[index].importedLibrary.definingCompilationUnit;
+ }
+
+ void setUp() {
+ new MockSdk();
+ testProject = _p('/test/lib');
+ testFile = _p('/test/lib/test.dart');
+ logger = new PerformanceLog(logBuffer);
+ scheduler = new AnalysisDriverScheduler(logger);
+ driver = new AnalysisDriver(
+ scheduler,
+ logger,
+ provider,
+ byteStore,
+ contentOverlay,
+ new SourceFactory([
+ new DartUriResolver(sdk),
+ new PackageMapUriResolver(provider, <String, List<Folder>>{
+ 'test': [provider.getFolder(testProject)]
+ }),
+ new ResourceUriResolver(provider)
+ ], null, provider),
+ new AnalysisOptionsImpl()..strongMode = true);
+ scheduler.start();
+// driver.status.lastWhere((status) {
Paul Berry 2016/11/21 13:46:57 Was this temporary debugging code? Should it be r
scheglov 2016/11/21 17:27:30 Done.
+// allStatuses.add(status);
+// if (status.isIdle) {
+// idleStatusMonitor.notify();
+// }
+// });
+// driver.results.listen(allResults.add);
+ }
+
+ test_hasAncestor_ClassDeclaration() async {
+ await _indexTestUnit('''
+class A {}
+class B1 extends A {}
+class B2 implements A {}
+class C1 extends B1 {}
+class C2 extends B2 {}
+class C3 implements B1 {}
+class C4 implements B2 {}
+class M extends Object with A {}
+''');
+ ClassElement classElementA = findElement("A");
+ assertThat(classElementA)
+ ..isAncestorOf('B1 extends A')
+ ..isAncestorOf('B2 implements A')
+ ..isAncestorOf('C1 extends B1')
+ ..isAncestorOf('C2 extends B2')
+ ..isAncestorOf('C3 implements B1')
+ ..isAncestorOf('C4 implements B2')
+ ..isAncestorOf('M extends Object with A');
+ }
+
+ test_hasAncestor_ClassTypeAlias() async {
+ await _indexTestUnit('''
+class A {}
+class B extends A {}
+class C1 = Object with A;
+class C2 = Object with B;
+''');
+ ClassElement classElementA = findElement('A');
+ ClassElement classElementB = findElement('B');
+ assertThat(classElementA)
+ ..isAncestorOf('C1 = Object with A')
+ ..isAncestorOf('C2 = Object with B');
+ assertThat(classElementB)..isAncestorOf('C2 = Object with B');
+ }
+
+ test_isExtendedBy_ClassDeclaration() async {
+ await _indexTestUnit('''
+class A {} // 1
+class B extends A {} // 2
+''');
+ ClassElement elementA = findElement('A');
+ assertThat(elementA)
+ ..isExtendedAt('A {} // 2', false)
+ ..isReferencedAt('A {} // 2', false);
+ }
+
+ test_isExtendedBy_ClassDeclaration_isQualified() async {
+ provider.newFile(
+ _p('$testProject/lib.dart'),
+ '''
+class A {}
+''');
+ await _indexTestUnit('''
+import 'lib.dart' as p;
+class B extends p.A {} // 2
+''');
+ ClassElement elementA = importedUnit().getType('A');
+ assertThat(elementA).isExtendedAt('A {} // 2', true);
+ }
+
+ test_isExtendedBy_ClassDeclaration_Object() async {
+ await _indexTestUnit('''
+class A {}
+''');
+ ClassElement elementA = findElement('A');
+ ClassElement elementObject = elementA.supertype.element;
+ assertThat(elementObject).isExtendedAt('A {}', true, length: 0);
+ }
+
+ test_isExtendedBy_ClassTypeAlias() async {
+ await _indexTestUnit('''
+class A {}
+class B {}
+class C = A with B;
+''');
+ ClassElement elementA = findElement('A');
+ assertThat(elementA)
+ ..isExtendedAt('A with', false)
+ ..isReferencedAt('A with', false);
+ }
+
+ test_isExtendedBy_ClassTypeAlias_isQualified() async {
+ provider.newFile(
+ _p('$testProject/lib.dart'),
+ '''
+class A {}
+''');
+ await _indexTestUnit('''
+import 'lib.dart' as p;
+class B {}
+class C = p.A with B;
+''');
+ ClassElement elementA = importedUnit().getType('A');
+ assertThat(elementA)
+ ..isExtendedAt('A with', true)
+ ..isReferencedAt('A with', true);
+ }
+
+ test_isImplementedBy_ClassDeclaration() async {
+ await _indexTestUnit('''
+class A {} // 1
+class B implements A {} // 2
+''');
+ ClassElement elementA = findElement('A');
+ assertThat(elementA)
+ ..isImplementedAt('A {} // 2', false)
+ ..isReferencedAt('A {} // 2', false);
+ }
+
+ test_isImplementedBy_ClassDeclaration_isQualified() async {
+ provider.newFile(
+ _p('$testProject/lib.dart'),
+ '''
+class A {}
+''');
+ await _indexTestUnit('''
+import 'lib.dart' as p;
+class B implements p.A {} // 2
+''');
+ ClassElement elementA = importedUnit().getType('A');
+ assertThat(elementA)
+ ..isImplementedAt('A {} // 2', true)
+ ..isReferencedAt('A {} // 2', true);
+ }
+
+ test_isImplementedBy_ClassTypeAlias() async {
+ await _indexTestUnit('''
+class A {} // 1
+class B {} // 2
+class C = Object with A implements B; // 3
+''');
+ ClassElement elementB = findElement('B');
+ assertThat(elementB)
+ ..isImplementedAt('B; // 3', false)
+ ..isReferencedAt('B; // 3', false);
+ }
+
+ test_isInvokedBy_FieldElement() async {
+ await _indexTestUnit('''
+class A {
+ var field;
+ main() {
+ this.field(); // q
+ field(); // nq
+ }
+}''');
+ FieldElement field = findElement('field');
+ assertThat(field.getter)
+ ..isInvokedAt('field(); // q', true)
+ ..isInvokedAt('field(); // nq', false);
+ }
+
+ test_isInvokedBy_FunctionElement() async {
+ provider.newFile(
+ _p('$testProject/lib.dart'),
+ '''
+library lib;
+foo() {}
+''');
+ await _indexTestUnit('''
+import 'lib.dart';
+import 'lib.dart' as pref;
+main() {
+ pref.foo(); // q
+ foo(); // nq
+}''');
+ FunctionElement element = importedUnit().functions[0];
+ assertThat(element)
+ ..isInvokedAt('foo(); // q', true)
+ ..isInvokedAt('foo(); // nq', false);
+ }
+
+ test_isInvokedBy_FunctionElement_synthetic_loadLibrary() async {
+ await _indexTestUnit('''
+import 'dart:math' deferred as math;
+main() {
+ math.loadLibrary(); // 1
+ math.loadLibrary(); // 2
+}
+''');
+ LibraryElement mathLib = testLibraryElement.imports[0].importedLibrary;
+ FunctionElement element = mathLib.loadLibraryFunction;
+ assertThat(element).isInvokedAt('loadLibrary(); // 1', true);
+ assertThat(element).isInvokedAt('loadLibrary(); // 2', true);
+ }
+
+ test_isInvokedBy_MethodElement() async {
+ await _indexTestUnit('''
+class A {
+ foo() {}
+ main() {
+ this.foo(); // q
+ foo(); // nq
+ }
+}''');
+ Element element = findElement('foo');
+ assertThat(element)
+ ..isInvokedAt('foo(); // q', true)
+ ..isInvokedAt('foo(); // nq', false);
+ }
+
+ test_isInvokedBy_MethodElement_propagatedType() async {
+ await _indexTestUnit('''
+class A {
+ foo() {}
+}
+main() {
+ var a = new A();
+ a.foo();
+}
+''');
+ Element element = findElement('foo');
+ assertThat(element).isInvokedAt('foo();', true);
+ }
+
+ test_isInvokedBy_operator_binary() async {
+ await _indexTestUnit('''
+class A {
+ operator +(other) => this;
+}
+main(A a) {
+ print(a + 1);
+ a += 2;
+ ++a;
+ a++;
+}
+''');
+ MethodElement element = findElement('+');
+ assertThat(element)
+ ..isInvokedAt('+ 1', true, length: 1)
+ ..isInvokedAt('+= 2', true, length: 2)
+ ..isInvokedAt('++a', true, length: 2)
+ ..isInvokedAt('++;', true, length: 2);
+ }
+
+ test_isInvokedBy_operator_index() async {
+ await _indexTestUnit('''
+class A {
+ operator [](i) => null;
+ operator []=(i, v) {}
+}
+main(A a) {
+ print(a[0]);
+ a[1] = 42;
+}
+''');
+ MethodElement readElement = findElement('[]');
+ MethodElement writeElement = findElement('[]=');
+ assertThat(readElement).isInvokedAt('[0]', true, length: 1);
+ assertThat(writeElement).isInvokedAt('[1]', true, length: 1);
+ }
+
+ test_isInvokedBy_operator_prefix() async {
+ await _indexTestUnit('''
+class A {
+ A operator ~() => this;
+}
+main(A a) {
+ print(~a);
+}
+''');
+ MethodElement element = findElement('~');
+ assertThat(element).isInvokedAt('~a', true, length: 1);
+ }
+
+ test_isInvokedBy_PropertyAccessorElement_getter() async {
+ await _indexTestUnit('''
+class A {
+ get ggg => null;
+ main() {
+ this.ggg(); // q
+ ggg(); // nq
+ }
+}''');
+ PropertyAccessorElement element = findElement('ggg', ElementKind.GETTER);
+ assertThat(element)
+ ..isInvokedAt('ggg(); // q', true)
+ ..isInvokedAt('ggg(); // nq', false);
+ }
+
+ test_isMixedInBy_ClassDeclaration() async {
+ await _indexTestUnit('''
+class A {} // 1
+class B extends Object with A {} // 2
+''');
+ ClassElement elementA = findElement('A');
+ assertThat(elementA)
+ ..isMixedInAt('A {} // 2', false)
+ ..isReferencedAt('A {} // 2', false);
+ }
+
+ test_isMixedInBy_ClassDeclaration_isQualified() async {
+ provider.newFile(
+ _p('$testProject/lib.dart'),
+ '''
+class A {}
+''');
+ await _indexTestUnit('''
+import 'lib.dart' as p;
+class B extends Object with p.A {} // 2
+''');
+ ClassElement elementA = importedUnit().getType('A');
+ assertThat(elementA).isMixedInAt('A {} // 2', true);
+ }
+
+ test_isMixedInBy_ClassTypeAlias() async {
+ await _indexTestUnit('''
+class A {} // 1
+class B = Object with A; // 2
+''');
+ ClassElement elementA = findElement('A');
+ assertThat(elementA).isMixedInAt('A; // 2', false);
+ }
+
+ test_isReferencedBy_ClassElement() async {
+ await _indexTestUnit('''
+class A {
+ static var field;
+}
+main(A p) {
+ A v;
+ new A(); // 2
+ A.field = 1;
+ print(A.field); // 3
+}
+''');
+ ClassElement element = findElement('A');
+ assertThat(element)
+ ..isReferencedAt('A p) {', false)
+ ..isReferencedAt('A v;', false)
+ ..isReferencedAt('A(); // 2', false)
+ ..isReferencedAt('A.field = 1;', false)
+ ..isReferencedAt('A.field); // 3', false);
+ }
+
+ test_isReferencedBy_ClassElement_invocation() async {
+ await _indexTestUnit('''
+class A {}
+main() {
+ A(); // invalid code, but still a reference
+}''');
+ Element element = findElement('A');
+ assertThat(element).isReferencedAt('A();', false);
+ }
+
+ test_isReferencedBy_ClassElement_invocation_isQualified() async {
+ provider.newFile(
+ _p('$testProject/lib.dart'),
+ '''
+class A {}
+''');
+ await _indexTestUnit('''
+import 'lib.dart' as p;
+main() {
+ p.A(); // invalid code, but still a reference
+}''');
+ Element element = importedUnit().getType('A');
+ assertThat(element).isReferencedAt('A();', true);
+ }
+
+ test_isReferencedBy_ClassTypeAlias() async {
+ await _indexTestUnit('''
+class A {}
+class B = Object with A;
+main(B p) {
+ B v;
+}
+''');
+ ClassElement element = findElement('B');
+ assertThat(element)
+ ..isReferencedAt('B p) {', false)
+ ..isReferencedAt('B v;', false);
+ }
+
+ test_isReferencedBy_CompilationUnitElement_export() async {
+ provider.newFile(
+ _p('$testProject/lib.dart'),
+ '''
+library lib;
+''');
+ await _indexTestUnit('''
+export 'lib.dart';
+''');
+ LibraryElement element = testLibraryElement.exports[0].exportedLibrary;
+ assertThat(element)..isReferencedAt("'lib.dart'", true, length: 10);
+ }
+
+ test_isReferencedBy_CompilationUnitElement_import() async {
+ provider.newFile(
+ _p('$testProject/lib.dart'),
+ '''
+library lib;
+''');
+ await _indexTestUnit('''
+import 'lib.dart';
+''');
+ LibraryElement element = testLibraryElement.imports[0].importedLibrary;
+ assertThat(element)..isReferencedAt("'lib.dart'", true, length: 10);
+ }
+
+ test_isReferencedBy_CompilationUnitElement_part() async {
+ provider.newFile(_p('$testProject/my_unit.dart'), 'part of my_lib;');
+ await _indexTestUnit('''
+library my_lib;
+part 'my_unit.dart';
+''');
+ CompilationUnitElement element = testLibraryElement.parts[0];
+ assertThat(element)..isReferencedAt("'my_unit.dart';", true, length: 14);
+ }
+
+ test_isReferencedBy_ConstructorElement() async {
+ await _indexTestUnit('''
+class A implements B {
+ A() {}
+ A.foo() {}
+}
+class B extends A {
+ B() : super(); // 1
+ B.foo() : super.foo(); // 2
+ factory B.bar() = A.foo; // 3
+}
+main() {
+ new A(); // 4
+ new A.foo(); // 5
+}
+''');
+ ClassElement classA = findElement('A');
+ ConstructorElement constA = classA.constructors[0];
+ ConstructorElement constA_foo = classA.constructors[1];
+ // A()
+ assertThat(constA)
+ ..hasRelationCount(2)
+ ..isReferencedAt('(); // 1', true, length: 0)
+ ..isReferencedAt('(); // 4', true, length: 0);
+ // A.foo()
+ assertThat(constA_foo)
+ ..hasRelationCount(3)
+ ..isReferencedAt('.foo(); // 2', true, length: 4)
+ ..isReferencedAt('.foo; // 3', true, length: 4)
+ ..isReferencedAt('.foo(); // 5', true, length: 4);
+ }
+
+ test_isReferencedBy_ConstructorElement_classTypeAlias() async {
+ await _indexTestUnit('''
+class M {}
+class A implements B {
+ A() {}
+ A.named() {}
+}
+class B = A with M;
+class C = B with M;
+main() {
+ new B(); // B1
+ new B.named(); // B2
+ new C(); // C1
+ new C.named(); // C2
+}
+''');
+ ClassElement classA = findElement('A');
+ ConstructorElement constA = classA.constructors[0];
+ ConstructorElement constA_named = classA.constructors[1];
+ assertThat(constA)
+ ..isReferencedAt('(); // B1', true, length: 0)
+ ..isReferencedAt('(); // C1', true, length: 0);
+ assertThat(constA_named)
+ ..isReferencedAt('.named(); // B2', true, length: 6)
+ ..isReferencedAt('.named(); // C2', true, length: 6);
+ }
+
+ test_isReferencedBy_ConstructorElement_classTypeAlias_cycle() async {
+ await _indexTestUnit('''
+class M {}
+class A = B with M;
+class B = A with M;
+main() {
+ new A();
+ new B();
+}
+''');
+ // No additional validation, but it should not fail with stack overflow.
+ }
+
+ test_isReferencedBy_ConstructorElement_namedOnlyWithDot() async {
+ await _indexTestUnit('''
+class A {
+ A.named() {}
+}
+main() {
+ new A.named();
+}
+''');
+ // has ".named()", but does not have "named()"
+ int offsetWithoutDot = findOffset('named();');
+ int offsetWithDot = findOffset('.named();');
+ expect(index.usedElementOffsets, isNot(contains(offsetWithoutDot)));
+ expect(index.usedElementOffsets, contains(offsetWithDot));
+ }
+
+ test_isReferencedBy_ConstructorElement_redirection() async {
+ await _indexTestUnit('''
+class A {
+ A() : this.bar(); // 1
+ A.foo() : this(); // 2
+ A.bar();
+}
+''');
+ ClassElement classA = findElement('A');
+ ConstructorElement constA = classA.constructors[0];
+ ConstructorElement constA_bar = classA.constructors[2];
+ assertThat(constA).isReferencedAt('(); // 2', true, length: 0);
+ assertThat(constA_bar).isReferencedAt('.bar(); // 1', true, length: 4);
+ }
+
+ test_isReferencedBy_ConstructorElement_synthetic() async {
+ await _indexTestUnit('''
+class A {}
+main() {
+ new A(); // 1
+}
+''');
+ ClassElement classA = findElement('A');
+ ConstructorElement constA = classA.constructors[0];
+ // A()
+ assertThat(constA)..isReferencedAt('(); // 1', true, length: 0);
+ }
+
+ test_isReferencedBy_DynamicElement() async {
+ await _indexTestUnit('''
+dynamic f() {
+}''');
+ expect(index.usedElementOffsets, isEmpty);
+ }
+
+ test_isReferencedBy_FieldElement() async {
+ await _indexTestUnit('''
+class A {
+ var field;
+ A({this.field});
+ m() {
+ field = 2; // nq
+ print(field); // nq
+ }
+}
+main(A a) {
+ a.field = 3; // q
+ print(a.field); // q
+ new A(field: 4);
+}
+''');
+ FieldElement field = findElement('field', ElementKind.FIELD);
+ PropertyAccessorElement getter = field.getter;
+ PropertyAccessorElement setter = field.setter;
+ // A()
+ assertThat(field)..isWrittenAt('field});', true);
+ // m()
+ assertThat(setter)..isReferencedAt('field = 2; // nq', false);
+ assertThat(getter)..isReferencedAt('field); // nq', false);
+ // main()
+ assertThat(setter)..isReferencedAt('field = 3; // q', true);
+ assertThat(getter)..isReferencedAt('field); // q', true);
+ assertThat(field)..isReferencedAt('field: 4', true);
+ }
+
+ test_isReferencedBy_FieldElement_multiple() async {
+ await _indexTestUnit('''
+class A {
+ var aaa;
+ var bbb;
+ A(this.aaa, this.bbb) {}
+ m() {
+ print(aaa);
+ aaa = 1;
+ print(bbb);
+ bbb = 2;
+ }
+}
+''');
+ // aaa
+ {
+ FieldElement field = findElement('aaa', ElementKind.FIELD);
+ PropertyAccessorElement getter = field.getter;
+ PropertyAccessorElement setter = field.setter;
+ assertThat(field)..isWrittenAt('aaa, ', true);
+ assertThat(getter)..isReferencedAt('aaa);', false);
+ assertThat(setter)..isReferencedAt('aaa = 1;', false);
+ }
+ // bbb
+ {
+ FieldElement field = findElement('bbb', ElementKind.FIELD);
+ PropertyAccessorElement getter = field.getter;
+ PropertyAccessorElement setter = field.setter;
+ assertThat(field)..isWrittenAt('bbb) {}', true);
+ assertThat(getter)..isReferencedAt('bbb);', false);
+ assertThat(setter)..isReferencedAt('bbb = 2;', false);
+ }
+ }
+
+ test_isReferencedBy_FieldElement_ofEnum() async {
+ await _indexTestUnit('''
+enum MyEnum {
+ A, B, C
+}
+main() {
+ print(MyEnum.values);
+ print(MyEnum.A.index);
+ print(MyEnum.A);
+ print(MyEnum.B);
+}
+''');
+ ClassElement enumElement = findElement('MyEnum');
+ assertThat(enumElement.getGetter('values'))
+ ..isReferencedAt('values);', true);
+ assertThat(enumElement.getGetter('index'))..isReferencedAt('index);', true);
+ assertThat(enumElement.getGetter('A'))..isReferencedAt('A);', true);
+ assertThat(enumElement.getGetter('B'))..isReferencedAt('B);', true);
+ }
+
+ test_isReferencedBy_FieldElement_synthetic_hasGetter() async {
+ await _indexTestUnit('''
+class A {
+ A() : f = 42;
+ int get f => 0;
+}
+''');
+ ClassElement element2 = findElement('A');
+ assertThat(element2.getField('f')).isWrittenAt('f = 42', true);
+ }
+
+ test_isReferencedBy_FieldElement_synthetic_hasGetterSetter() async {
+ await _indexTestUnit('''
+class A {
+ A() : f = 42;
+ int get f => 0;
+ set f(_) {}
+}
+''');
+ ClassElement element2 = findElement('A');
+ assertThat(element2.getField('f')).isWrittenAt('f = 42', true);
+ }
+
+ test_isReferencedBy_FieldElement_synthetic_hasSetter() async {
+ await _indexTestUnit('''
+class A {
+ A() : f = 42;
+ set f(_) {}
+}
+''');
+ ClassElement element2 = findElement('A');
+ assertThat(element2.getField('f')).isWrittenAt('f = 42', true);
+ }
+
+ test_isReferencedBy_FunctionElement() async {
+ await _indexTestUnit('''
+foo() {}
+main() {
+ print(foo);
+ print(foo());
+}
+''');
+ FunctionElement element = findElement('foo');
+ assertThat(element)
+ ..isReferencedAt('foo);', false)
+ ..isInvokedAt('foo());', false);
+ }
+
+ test_isReferencedBy_FunctionElement_with_LibraryElement() async {
+ provider.newFile(
+ _p('$testProject/foo.dart'),
+ r'''
+bar() {}
+''');
+ await _indexTestUnit('''
+import "foo.dart";
+main() {
+ bar();
+}
+''');
+ LibraryElement fooLibrary = testLibraryElement.imports[0].importedLibrary;
+ assertThat(fooLibrary)..isReferencedAt('"foo.dart";', true, length: 10);
+ {
+ FunctionElement bar = fooLibrary.definingCompilationUnit.functions[0];
+ assertThat(bar)..isInvokedAt('bar();', false);
+ }
+ }
+
+ test_isReferencedBy_FunctionTypeAliasElement() async {
+ await _indexTestUnit('''
+typedef A();
+main(A p) {
+}
+''');
+ Element element = findElement('A');
+ assertThat(element)..isReferencedAt('A p) {', false);
+ }
+
+ /**
+ * There was a bug in the AST structure, when single [Comment] was cloned and
+ * assigned to both [FieldDeclaration] and [VariableDeclaration].
+ *
+ * This caused duplicate indexing.
+ * Here we test that the problem is fixed one way or another.
+ */
+ test_isReferencedBy_identifierInComment() async {
+ await _indexTestUnit('''
+class A {}
+/// [A] text
+var myVariable = null;
+''');
+ Element element = findElement('A');
+ assertThat(element)..isReferencedAt('A] text', false);
+ }
+
+ test_isReferencedBy_MethodElement() async {
+ await _indexTestUnit('''
+class A {
+ method() {}
+ main() {
+ print(this.method); // q
+ print(method); // nq
+ }
+}''');
+ MethodElement element = findElement('method');
+ assertThat(element)
+ ..isReferencedAt('method); // q', true)
+ ..isReferencedAt('method); // nq', false);
+ }
+
+ test_isReferencedBy_ParameterElement() async {
+ await _indexTestUnit('''
+foo({var p}) {}
+main() {
+ foo(p: 1);
+}
+''');
+ Element element = findElement('p');
+ assertThat(element)..isReferencedAt('p: 1', true);
+ }
+
+ test_isReferencedBy_TopLevelVariableElement() async {
+ provider.newFile(
+ _p('$testProject/lib.dart'),
+ '''
+library lib;
+var V;
+''');
+ await _indexTestUnit('''
+import 'lib.dart' show V; // imp
+import 'lib.dart' as pref;
+main() {
+ pref.V = 5; // q
+ print(pref.V); // q
+ V = 5; // nq
+ print(V); // nq
+}''');
+ TopLevelVariableElement variable = importedUnit().topLevelVariables[0];
+ assertThat(variable)..isReferencedAt('V; // imp', true);
+ assertThat(variable.getter)
+ ..isReferencedAt('V); // q', true)
+ ..isReferencedAt('V); // nq', false);
+ assertThat(variable.setter)
+ ..isReferencedAt('V = 5; // q', true)
+ ..isReferencedAt('V = 5; // nq', false);
+ }
+
+ test_isReferencedBy_TopLevelVariableElement_synthetic_hasGetterSetter() async {
+ provider.newFile(
+ _p('$testProject/lib.dart'),
+ '''
+int get V => 0;
+void set V(_) {}
+''');
+ await _indexTestUnit('''
+import 'lib.dart' show V;
+''');
+ TopLevelVariableElement element = importedUnit().topLevelVariables[0];
+ assertThat(element).isReferencedAt('V;', true);
+ }
+
+ test_isReferencedBy_TopLevelVariableElement_synthetic_hasSetter() async {
+ provider.newFile(
+ _p('$testProject/lib.dart'),
+ '''
+void set V(_) {}
+''');
+ await _indexTestUnit('''
+import 'lib.dart' show V;
+''');
+ TopLevelVariableElement element = importedUnit().topLevelVariables[0];
+ assertThat(element).isReferencedAt('V;', true);
+ }
+
+ test_isReferencedBy_typeInVariableList() async {
+ await _indexTestUnit('''
+class A {}
+A myVariable = null;
+''');
+ Element element = findElement('A');
+ assertThat(element).isReferencedAt('A myVariable', false);
+ }
+
+ test_isWrittenBy_FieldElement() async {
+ await _indexTestUnit('''
+class A {
+ int field;
+ A.foo({this.field});
+ A.bar() : field = 5;
+}
+''');
+ FieldElement element = findElement('field', ElementKind.FIELD);
+ assertThat(element)
+ ..isWrittenAt('field})', true)
+ ..isWrittenAt('field = 5', true);
+ }
+
+ test_usedName_inLibraryIdentifier() async {
+ await _indexTestUnit('''
+library aaa.bbb.ccc;
+class C {
+ var bbb;
+}
+main(p) {
+ p.bbb = 1;
+}
+''');
+ assertThatName('bbb')
+ ..isNotUsed('bbb.ccc', IndexRelationKind.IS_READ_BY)
+ ..isUsedQ('bbb = 1;', IndexRelationKind.IS_WRITTEN_BY);
+ }
+
+ test_usedName_qualified_resolved() async {
+ await _indexTestUnit('''
+class C {
+ var x;
+}
+main(C c) {
+ c.x;
+ c.x = 1;
+ c.x += 2;
+ c.x();
+}
+''');
+ assertThatName('x')
+ ..isNotUsedQ('x;', IndexRelationKind.IS_READ_BY)
+ ..isNotUsedQ('x = 1;', IndexRelationKind.IS_WRITTEN_BY)
+ ..isNotUsedQ('x += 2;', IndexRelationKind.IS_READ_WRITTEN_BY)
+ ..isNotUsedQ('x();', IndexRelationKind.IS_INVOKED_BY);
+ }
+
+ test_usedName_qualified_unresolved() async {
+ await _indexTestUnit('''
+main(p) {
+ p.x;
+ p.x = 1;
+ p.x += 2;
+ p.x();
+}
+''');
+ assertThatName('x')
+ ..isUsedQ('x;', IndexRelationKind.IS_READ_BY)
+ ..isUsedQ('x = 1;', IndexRelationKind.IS_WRITTEN_BY)
+ ..isUsedQ('x += 2;', IndexRelationKind.IS_READ_WRITTEN_BY)
+ ..isUsedQ('x();', IndexRelationKind.IS_INVOKED_BY);
+ }
+
+ test_usedName_unqualified_resolved() async {
+ await _indexTestUnit('''
+class C {
+ var x;
+ m() {
+ x;
+ x = 1;
+ x += 2;
+ x();
+ }
+}
+''');
+ assertThatName('x')
+ ..isNotUsedQ('x;', IndexRelationKind.IS_READ_BY)
+ ..isNotUsedQ('x = 1;', IndexRelationKind.IS_WRITTEN_BY)
+ ..isNotUsedQ('x += 2;', IndexRelationKind.IS_READ_WRITTEN_BY)
+ ..isNotUsedQ('x();', IndexRelationKind.IS_INVOKED_BY);
+ }
+
+ test_usedName_unqualified_unresolved() async {
+ await _indexTestUnit('''
+main() {
+ x;
+ x = 1;
+ x += 2;
+ x();
+}
+''');
+ assertThatName('x')
+ ..isUsed('x;', IndexRelationKind.IS_READ_BY)
+ ..isUsed('x = 1;', IndexRelationKind.IS_WRITTEN_BY)
+ ..isUsed('x += 2;', IndexRelationKind.IS_READ_WRITTEN_BY)
+ ..isUsed('x();', IndexRelationKind.IS_INVOKED_BY);
+ }
+
+ void _addTestFile(String content) {
+ provider.newFile(testFile, content);
+ driver.addFile(testFile);
+ }
+
+ /**
+ * Asserts that [index] has an item with the expected properties.
+ */
+ void _assertHasRelation(
+ Element element,
+ List<_Relation> relations,
+ IndexRelationKind expectedRelationKind,
+ ExpectedLocation expectedLocation) {
+ for (_Relation relation in relations) {
+ if (relation.kind == expectedRelationKind &&
+ relation.offset == expectedLocation.offset &&
+ relation.length == expectedLocation.length &&
+ relation.isQualified == expectedLocation.isQualified) {
+ return;
+ }
+ }
+ _failWithIndexDump(
+ 'not found\n$element $expectedRelationKind at $expectedLocation');
+ }
+
+ void _assertUsedName(String name, IndexRelationKind kind,
+ ExpectedLocation expectedLocation, bool isNot) {
+ int nameId = _getStringId(name);
+ for (int i = 0; i < index.usedNames.length; i++) {
+ if (index.usedNames[i] == nameId &&
+ index.usedNameKinds[i] == kind &&
+ index.usedNameOffsets[i] == expectedLocation.offset &&
+ index.usedNameIsQualifiedFlags[i] == expectedLocation.isQualified) {
+ if (isNot) {
+ _failWithIndexDump('Unexpected $name $kind at $expectedLocation');
+ }
+ return;
+ }
+ }
+ if (isNot) {
+ return;
+ }
+ _failWithIndexDump('Not found $name $kind at $expectedLocation');
+ }
+
+ ExpectedLocation _expectedLocation(String search, bool isQualified,
+ {int length}) {
+ int offset = findOffset(search);
+ if (length == null) {
+ length = getLeadingIdentifierLength(search);
+ }
+ return new ExpectedLocation(testUnitElement, offset, length, isQualified);
+ }
+
+ void _failWithIndexDump(String msg) {
+ String packageIndexJsonString =
+ new JsonEncoder.withIndent(' ').convert(index.toJson());
+ fail('$msg in\n' + packageIndexJsonString);
+ }
+
+ /**
+ * Return the [element] identifier in [index] or fail.
+ */
+ int _findElementId(Element element) {
+ int unitId = _getUnitId(element);
+ // Prepare the element that was put into the index.
+ IndexElementInfo info = new IndexElementInfo(element);
+ element = info.element;
+ // Prepare element's name components.
+ int unitMemberId = index.nullStringId;
+ int classMemberId = index.nullStringId;
+ int parameterId = index.nullStringId;
+ for (Element e = element; e != null; e = e.enclosingElement) {
+ if (e.enclosingElement is CompilationUnitElement) {
+ unitMemberId = _getStringId(e.name);
+ break;
+ }
+ }
+ for (Element e = element; e != null; e = e.enclosingElement) {
+ if (e.enclosingElement is ClassElement) {
+ classMemberId = _getStringId(e.name);
+ break;
+ }
+ }
+ if (element is ParameterElement) {
+ parameterId = _getStringId(element.name);
+ }
+ // Find the element's id.
+ for (int elementId = 0;
+ elementId < index.elementUnits.length;
+ elementId++) {
+ if (index.elementUnits[elementId] == unitId &&
+ index.elementNameUnitMemberIds[elementId] == unitMemberId &&
+ index.elementNameClassMemberIds[elementId] == classMemberId &&
+ index.elementNameParameterIds[elementId] == parameterId &&
+ index.elementKinds[elementId] == info.kind) {
+ return elementId;
+ }
+ }
+ _failWithIndexDump('Element $element is not referenced');
+ return 0;
+ }
+
+ /**
+ * Return all relations with [element] in [index].
+ */
+ List<_Relation> _getElementRelations(Element element) {
+ int elementId = _findElementId(element);
+ List<_Relation> relations = <_Relation>[];
+ for (int i = 0; i < index.usedElementOffsets.length; i++) {
+ if (index.usedElements[i] == elementId) {
+ relations.add(new _Relation(
+ index.usedElementKinds[i],
+ index.usedElementOffsets[i],
+ index.usedElementLengths[i],
+ index.usedElementIsQualifiedFlags[i]));
+ }
+ }
+ return relations;
+ }
+
+ int _getStringId(String str) {
+ int id = index.strings.indexOf(str);
+ if (id < 0) {
+ _failWithIndexDump('String "$str" is not referenced');
+ }
+ return id;
+ }
+
+ int _getUnitId(Element element) {
+ CompilationUnitElement unitElement = getUnitElement(element);
+ int libraryUriId = _getUriId(unitElement.library.source.uri);
+ int unitUriId = _getUriId(unitElement.source.uri);
+ expect(index.unitLibraryUris, hasLength(index.unitUnitUris.length));
+ for (int i = 0; i < index.unitLibraryUris.length; i++) {
+ if (index.unitLibraryUris[i] == libraryUriId &&
+ index.unitUnitUris[i] == unitUriId) {
+ return i;
+ }
+ }
+ _failWithIndexDump('Unit $unitElement of $element is not referenced');
+ return -1;
+ }
+
+ int _getUriId(Uri uri) {
+ String str = uri.toString();
+ return _getStringId(str);
+ }
+
+ Future<Null> _indexTestUnit(String code) async {
+ testCode = code;
+ _addTestFile(testCode);
+
+ AnalysisResult result = await driver.getResult(testFile);
+ testUnit = result.unit;
+ testUnitElement = testUnit.element;
+ testLibraryElement = testUnitElement.library;
+
+ AnalysisDriverUnitIndexBuilder indexBuilder = indexUnit(testUnit);
+ List<int> indexBytes = indexBuilder.toBuffer();
+ index = new AnalysisDriverUnitIndex.fromBuffer(indexBytes);
+ }
+
+ String _p(String path) => provider.convertPath(path);
+}
+
+class _ElementIndexAssert {
+ final IndexTest test;
+ final Element element;
+ final List<_Relation> relations;
+
+ _ElementIndexAssert(this.test, this.element, this.relations);
+
+ void hasRelationCount(int expectedCount) {
+ expect(relations, hasLength(expectedCount));
+ }
+
+ void isAncestorOf(String search, {int length}) {
+ test._assertHasRelation(
+ element,
+ relations,
+ IndexRelationKind.IS_ANCESTOR_OF,
+ test._expectedLocation(search, false, length: length));
+ }
+
+ void isExtendedAt(String search, bool isQualified, {int length}) {
+ test._assertHasRelation(
+ element,
+ relations,
+ IndexRelationKind.IS_EXTENDED_BY,
+ test._expectedLocation(search, isQualified, length: length));
+ }
+
+ void isImplementedAt(String search, bool isQualified, {int length}) {
+ test._assertHasRelation(
+ element,
+ relations,
+ IndexRelationKind.IS_IMPLEMENTED_BY,
+ test._expectedLocation(search, isQualified, length: length));
+ }
+
+ void isInvokedAt(String search, bool isQualified, {int length}) {
+ test._assertHasRelation(element, relations, IndexRelationKind.IS_INVOKED_BY,
+ test._expectedLocation(search, isQualified, length: length));
+ }
+
+ void isMixedInAt(String search, bool isQualified, {int length}) {
+ test._assertHasRelation(
+ element,
+ relations,
+ IndexRelationKind.IS_MIXED_IN_BY,
+ test._expectedLocation(search, isQualified, length: length));
+ }
+
+ void isReferencedAt(String search, bool isQualified, {int length}) {
+ test._assertHasRelation(
+ element,
+ relations,
+ IndexRelationKind.IS_REFERENCED_BY,
+ test._expectedLocation(search, isQualified, length: length));
+ }
+
+ void isWrittenAt(String search, bool isQualified, {int length}) {
+ test._assertHasRelation(element, relations, IndexRelationKind.IS_WRITTEN_BY,
+ test._expectedLocation(search, isQualified, length: length));
+ }
+}
+
+/**
+ * Wraps an [_ElementVisitorFunction] into a [GeneralizingElementVisitor].
+ */
+class _ElementVisitorFunctionWrapper extends GeneralizingElementVisitor {
+ final _ElementVisitorFunction function;
+
+ _ElementVisitorFunctionWrapper(this.function);
+
+ visitElement(Element element) {
+ function(element);
+ super.visitElement(element);
+ }
+}
+
+class _NameIndexAssert {
+ final IndexTest test;
+ final String name;
+
+ _NameIndexAssert(this.test, this.name);
+
+ void isNotUsed(String search, IndexRelationKind kind) {
+ test._assertUsedName(
+ name, kind, test._expectedLocation(search, false), true);
+ }
+
+ void isNotUsedQ(String search, IndexRelationKind kind) {
+ test._assertUsedName(
+ name, kind, test._expectedLocation(search, true), true);
+ }
+
+ void isUsed(String search, IndexRelationKind kind) {
+ test._assertUsedName(
+ name, kind, test._expectedLocation(search, false), false);
+ }
+
+ void isUsedQ(String search, IndexRelationKind kind) {
+ test._assertUsedName(
+ name, kind, test._expectedLocation(search, true), false);
+ }
+}
+
+class _Relation {
+ final IndexRelationKind kind;
+ final int offset;
+ final int length;
+ final bool isQualified;
+
+ _Relation(this.kind, this.offset, this.length, this.isQualified);
+
+ @override
+ String toString() {
+ return '_Relation{kind: $kind, offset: $offset, length: $length, '
+ 'isQualified: $isQualified}lified)';
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698