blob: 022a7c9e0a7aa5cb62152149d4450f3057e85c0e [file] [log] [blame]
// Copyright (c) 2014, 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.
library test.services.src.index.dart_index_contributor;
import 'package:analysis_server/analysis/index/index_core.dart';
import 'package:analysis_server/src/services/index/index.dart';
import 'package:analysis_server/src/services/index/index_contributor.dart';
import 'package:analysis_server/src/services/index/index_store.dart';
import 'package:analysis_server/src/services/index/indexable_element.dart';
import 'package:analyzer/src/generated/ast.dart';
import 'package:analyzer/src/generated/element.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import 'package:typed_mock/typed_mock.dart';
import 'package:unittest/unittest.dart';
import '../../abstract_single_unit.dart';
import '../../utils.dart';
main() {
initializeTestEnvironment();
defineReflectiveTests(DartUnitContributorTest);
}
/**
* Returns `true` if the [actual] location the same properties as [expected].
*/
bool _equalsLocation(LocationImpl actual, ExpectedLocation expected) {
return _equalsLocationProperties(actual, expected.element, expected.offset,
expected.length, expected.isQualified, expected.isResolved);
}
/**
* Returns `true` if the [actual] location the expected properties.
*/
bool _equalsLocationProperties(LocationImpl actual, Element expectedElement,
int expectedOffset, int expectedLength, bool isQualified, bool isResolved) {
return (expectedElement == null || expectedElement == actual.element) &&
expectedOffset == actual.offset &&
expectedLength == actual.length &&
isQualified == actual.isQualified &&
isResolved == actual.isResolved;
}
bool _equalsRecordedRelation(
RecordedRelation recordedRelation,
IndexableObject expectedIndexable,
RelationshipImpl expectedRelationship,
ExpectedLocation expectedLocation) {
return expectedIndexable == recordedRelation.indexable &&
(expectedRelationship == null ||
expectedRelationship == recordedRelation.relationship) &&
(expectedLocation == null ||
_equalsLocation(recordedRelation.location, expectedLocation));
}
@reflectiveTest
class DartUnitContributorTest extends AbstractSingleUnitTest {
InternalIndexStore store = new MockIndexStore();
List<RecordedRelation> recordedRelations = <RecordedRelation>[];
List<Element> recordedTopElements = <Element>[];
CompilationUnitElement importedUnit({int index: 0}) {
List<ImportElement> imports = testLibraryElement.imports;
return imports[index].importedLibrary.definingCompilationUnit;
}
void setUp() {
super.setUp();
when(store.aboutToIndex(context, anyObject)).thenReturn(true);
when(store.recordRelationship(anyObject, anyObject, anyObject)).thenInvoke(
(IndexableObject indexable, RelationshipImpl relationship,
LocationImpl location) {
recordedRelations
.add(new RecordedRelation(indexable, relationship, location));
});
when(store.recordTopLevelDeclaration(anyObject))
.thenInvoke((Element element) {
recordedTopElements.add(element);
});
}
void test_bad_unresolvedFieldFormalParameter() {
verifyNoTestUnitErrors = false;
_indexTestUnit('''
class Test {
final field;
Test(this.fie);
}''');
}
void test_definesClass() {
_indexTestUnit('class A {}');
// prepare elements
ClassElement classElement = findElement("A");
// verify
_assertDefinesTopLevelElement(classElement);
}
void test_definesClassAlias() {
_indexTestUnit('''
class Mix {}
class MyClass = Object with Mix;''');
// prepare elements
Element classElement = findElement("MyClass");
// verify
_assertDefinesTopLevelElement(classElement);
}
void test_definesClassEnum() {
_indexTestUnit('enum MyEnum {A, B, c}');
// prepare elements
ClassElement classElement = findElement("MyEnum");
// verify
_assertDefinesTopLevelElement(classElement);
}
void test_definesFunction() {
_indexTestUnit('myFunction() {}');
// prepare elements
FunctionElement functionElement = findElement("myFunction");
// verify
_assertDefinesTopLevelElement(functionElement);
}
void test_definesFunctionType() {
_indexTestUnit('typedef MyFunction(int p);');
// prepare elements
FunctionTypeAliasElement typeAliasElement = findElement("MyFunction");
// verify
_assertDefinesTopLevelElement(typeAliasElement);
}
void test_definesVariable() {
_indexTestUnit('var myVar = 42;');
// prepare elements
VariableElement varElement = findElement("myVar");
// verify
_assertDefinesTopLevelElement(varElement);
}
void test_forIn() {
_indexTestUnit('''
main() {
for (var v in []) {
}
}''');
// prepare elements
Element mainElement = findElement("main");
VariableElement variableElement = findElement("v");
// verify
_assertNoRecordedRelationForElement(variableElement,
IndexConstants.IS_READ_BY, _expectedLocation(mainElement, 'v in []'));
}
void test_IndexableName_field() {
_indexTestUnit('''
class A {
int field;
}
main(A a, p) {
print(a.field); // r
print(p.field); // ur
{
var field = 42;
print(field); // not a member
}
}
''');
// prepare elements
Element mainElement = findElement('main');
FieldElement fieldElement = findElement('field');
IndexableName indexable = new IndexableName('field');
// verify
_assertRecordedRelation(indexable, IndexConstants.NAME_IS_DEFINED_BY,
_expectedLocation(fieldElement, 'field;'));
_assertRecordedRelation(indexable, IndexConstants.IS_READ_BY,
_expectedLocationQ(mainElement, 'field); // r'));
_assertRecordedRelation(indexable, IndexConstants.IS_READ_BY,
_expectedLocationQU(mainElement, 'field); // ur'));
_assertNoRecordedRelation(indexable, IndexConstants.IS_READ_BY,
_expectedLocation(mainElement, 'field); // not a member'));
}
void test_IndexableName_isDefinedBy_localVariable_inForEach() {
_indexTestUnit('''
class A {
main() {
for (int test in []) {
}
}
}
''');
// prepare elements
LocalVariableElement testElement = findElement('test');
IndexableName indexable = new IndexableName('test');
// verify
_assertRecordedRelation(indexable, IndexConstants.NAME_IS_DEFINED_BY,
_expectedLocation(testElement, 'test in []'));
}
void test_IndexableName_method() {
_indexTestUnit('''
class A {
method() {}
}
main(A a, p) {
a.method(); // r
p.method(); // ur
}
''');
// prepare elements
Element mainElement = findElement('main');
MethodElement methodElement = findElement('method');
IndexableName indexable = new IndexableName('method');
// verify
_assertRecordedRelation(indexable, IndexConstants.NAME_IS_DEFINED_BY,
_expectedLocation(methodElement, 'method() {}'));
_assertRecordedRelation(indexable, IndexConstants.IS_INVOKED_BY,
_expectedLocationQ(mainElement, 'method(); // r'));
_assertRecordedRelation(indexable, IndexConstants.IS_INVOKED_BY,
_expectedLocationQU(mainElement, 'method(); // ur'));
}
void test_IndexableName_operator_resolved() {
_indexTestUnit('''
class A {
operator +(o) {}
operator -(o) {}
operator ~() {}
operator ==(o) {}
}
main(A a) {
a + 5;
a += 5;
a == 5;
++a;
--a;
~a;
a++;
a--;
}
''');
// prepare elements
Element mainElement = findElement('main');
// binary
_assertRecordedRelationForName('+', IndexConstants.IS_INVOKED_BY,
_expectedLocationQ(mainElement, '+ 5', length: 1));
_assertRecordedRelationForName('+', IndexConstants.IS_INVOKED_BY,
_expectedLocationQ(mainElement, '+= 5', length: 2));
_assertRecordedRelationForName('==', IndexConstants.IS_INVOKED_BY,
_expectedLocationQ(mainElement, '== 5', length: 2));
// prefix
_assertRecordedRelationForName('+', IndexConstants.IS_INVOKED_BY,
_expectedLocationQ(mainElement, '++a', length: 2));
_assertRecordedRelationForName('-', IndexConstants.IS_INVOKED_BY,
_expectedLocationQ(mainElement, '--a', length: 2));
_assertRecordedRelationForName('~', IndexConstants.IS_INVOKED_BY,
_expectedLocationQ(mainElement, '~a', length: 1));
// postfix
_assertRecordedRelationForName('+', IndexConstants.IS_INVOKED_BY,
_expectedLocationQ(mainElement, '++;', length: 2));
_assertRecordedRelationForName('-', IndexConstants.IS_INVOKED_BY,
_expectedLocationQ(mainElement, '--;', length: 2));
}
void test_IndexableName_operator_unresolved() {
_indexTestUnit('''
class A {
operator +(o) {}
operator -(o) {}
operator ~() {}
operator ==(o) {}
}
main(a) {
a + 5;
a += 5;
a == 5;
++a;
--a;
~a;
a++;
a--;
}
''');
// prepare elements
Element mainElement = findElement('main');
// binary
_assertRecordedRelationForName('+', IndexConstants.IS_INVOKED_BY,
_expectedLocationQU(mainElement, '+ 5', length: 1));
_assertRecordedRelationForName('+', IndexConstants.IS_INVOKED_BY,
_expectedLocationQU(mainElement, '+= 5', length: 2));
_assertRecordedRelationForName('==', IndexConstants.IS_INVOKED_BY,
_expectedLocationQU(mainElement, '== 5', length: 2));
// prefix
_assertRecordedRelationForName('+', IndexConstants.IS_INVOKED_BY,
_expectedLocationQU(mainElement, '++a', length: 2));
_assertRecordedRelationForName('-', IndexConstants.IS_INVOKED_BY,
_expectedLocationQU(mainElement, '--a', length: 2));
_assertRecordedRelationForName('~', IndexConstants.IS_INVOKED_BY,
_expectedLocationQU(mainElement, '~a', length: 1));
// postfix
_assertRecordedRelationForName('+', IndexConstants.IS_INVOKED_BY,
_expectedLocationQU(mainElement, '++;', length: 2));
_assertRecordedRelationForName('-', IndexConstants.IS_INVOKED_BY,
_expectedLocationQU(mainElement, '--;', length: 2));
}
void test_isDefinedBy_IndexableName_method() {
_indexTestUnit('''
class A {
m() {}
}''');
// prepare elements
Element methodElement = findElement("m");
IndexableName nameIndexable = new IndexableName("m");
// verify
_assertRecordedRelationForIndexable(
nameIndexable,
IndexConstants.NAME_IS_DEFINED_BY,
_expectedLocation(methodElement, 'm() {}'));
}
void test_isDefinedBy_IndexableName_operator() {
_indexTestUnit('''
class A {
operator +(o) {}
}''');
// prepare elements
Element methodElement = findElement("+");
IndexableName nameIndexable = new IndexableName("+");
// verify
_assertRecordedRelationForIndexable(
nameIndexable,
IndexConstants.NAME_IS_DEFINED_BY,
_expectedLocation(methodElement, '+(o) {}', length: 1));
}
void test_isExtendedBy_ClassDeclaration() {
_indexTestUnit('''
class A {} // 1
class B extends A {} // 2
''');
// prepare elements
ClassElement classElementA = findElement("A");
ClassElement classElementB = findElement("B");
// verify
_assertRecordedRelationForElement(
classElementA,
IndexConstants.IS_EXTENDED_BY,
_expectedLocation(classElementB, 'A {} // 2'));
}
void test_isExtendedBy_ClassDeclaration_Object() {
_indexTestUnit('''
class A {} // 1
''');
// prepare elements
ClassElement classElementA = findElement("A");
ClassElement classElementObject = classElementA.supertype.element;
// verify
_assertRecordedRelationForElement(
classElementObject,
IndexConstants.IS_EXTENDED_BY,
_expectedLocation(classElementA, 'A {}', length: 0));
}
void test_isExtendedBy_ClassTypeAlias() {
_indexTestUnit('''
class A {} // 1
class B {} // 2
class C = A with B; // 3
''');
// prepare elements
ClassElement classElementA = findElement("A");
ClassElement classElementC = findElement("C");
// verify
_assertRecordedRelationForElement(
classElementA,
IndexConstants.IS_EXTENDED_BY,
_expectedLocation(classElementC, 'A with'));
}
void test_isImplementedBy_ClassDeclaration() {
_indexTestUnit('''
class A {} // 1
class B implements A {} // 2
''');
// prepare elements
ClassElement classElementA = findElement("A");
ClassElement classElementB = findElement("B");
// verify
_assertRecordedRelationForElement(
classElementA,
IndexConstants.IS_IMPLEMENTED_BY,
_expectedLocation(classElementB, 'A {} // 2'));
}
void test_isImplementedBy_ClassTypeAlias() {
_indexTestUnit('''
class A {} // 1
class B {} // 2
class C = Object with A implements B; // 3
''');
// prepare elements
ClassElement classElementB = findElement("B");
ClassElement classElementC = findElement("C");
// verify
_assertRecordedRelationForElement(
classElementB,
IndexConstants.IS_IMPLEMENTED_BY,
_expectedLocation(classElementC, 'B; // 3'));
}
void test_isInvokedBy_FieldElement() {
_indexTestUnit('''
class A {
var field;
main() {
this.field(); // q
field(); // nq
}
}''');
// prepare elements
Element mainElement = findElement("main");
FieldElement fieldElement = findElement("field");
PropertyAccessorElement getterElement = fieldElement.getter;
IndexableElement indexable = new IndexableElement(getterElement);
// verify
_assertRecordedRelation(indexable, IndexConstants.IS_INVOKED_BY,
_expectedLocationQ(mainElement, 'field(); // q'));
_assertRecordedRelation(indexable, IndexConstants.IS_INVOKED_BY,
_expectedLocation(mainElement, 'field(); // nq'));
}
void test_isInvokedBy_FunctionElement() {
addSource(
'/lib.dart',
'''
library lib;
foo() {}
''');
_indexTestUnit('''
import 'lib.dart';
import 'lib.dart' as pref;
main() {
pref.foo(); // q
foo(); // nq
}''');
// prepare elements
Element mainElement = findElement("main");
FunctionElement functionElement = importedUnit().functions[0];
IndexableElement indexable = new IndexableElement(functionElement);
// verify
_assertRecordedRelation(indexable, IndexConstants.IS_INVOKED_BY,
_expectedLocation(mainElement, 'foo(); // q'));
_assertRecordedRelation(indexable, IndexConstants.IS_INVOKED_BY,
_expectedLocation(mainElement, 'foo(); // nq'));
}
void test_isInvokedBy_LocalVariableElement() {
_indexTestUnit('''
main() {
var v;
v();
}''');
// prepare elements
Element mainElement = findElement("main");
Element element = findElement("v");
// verify
_assertRecordedRelationForElement(element, IndexConstants.IS_INVOKED_BY,
_expectedLocation(mainElement, 'v();'));
}
void test_isInvokedBy_MethodElement() {
_indexTestUnit('''
class A {
foo() {}
main() {
this.foo(); // q
foo(); // nq
}
}''');
// prepare elements
Element mainElement = findElement("main");
Element methodElement = findElement("foo");
IndexableElement indexable = new IndexableElement(methodElement);
// verify
_assertRecordedRelation(indexable, IndexConstants.IS_INVOKED_BY,
_expectedLocationQ(mainElement, 'foo(); // q'));
_assertRecordedRelation(indexable, IndexConstants.IS_INVOKED_BY,
_expectedLocation(mainElement, 'foo(); // nq'));
}
void test_isInvokedBy_MethodElement_propagatedType() {
_indexTestUnit('''
class A {
foo() {}
}
main() {
var a = new A();
a.foo();
}
''');
// prepare elements
Element mainElement = findElement("main");
Element methodElement = findElement("foo");
// verify
_assertRecordedRelationForElement(
methodElement,
IndexConstants.IS_INVOKED_BY,
_expectedLocationQ(mainElement, 'foo();'));
}
void test_isInvokedBy_operator_binary() {
_indexTestUnit('''
class A {
operator +(other) => this;
}
main(A a) {
print(a + 1);
a += 2;
++a;
a++;
}
''');
// prepare elements
MethodElement element = findElement('+');
Element mainElement = findElement('main');
IndexableElement indexable = new IndexableElement(element);
// verify
_assertRecordedRelation(indexable, IndexConstants.IS_INVOKED_BY,
_expectedLocationQ(mainElement, '+ 1', length: 1));
_assertRecordedRelation(indexable, IndexConstants.IS_INVOKED_BY,
_expectedLocationQ(mainElement, '+= 2', length: 2));
_assertRecordedRelation(indexable, IndexConstants.IS_INVOKED_BY,
_expectedLocationQ(mainElement, '++a;', length: 2));
_assertRecordedRelation(indexable, IndexConstants.IS_INVOKED_BY,
_expectedLocationQ(mainElement, '++;', length: 2));
}
void test_isInvokedBy_operator_index() {
_indexTestUnit('''
class A {
operator [](i) => null;
operator []=(i, v) {}
}
main(A a) {
print(a[0]);
a[1] = 42;
}
''');
// prepare elements
MethodElement readElement = findElement("[]");
MethodElement writeElement = findElement("[]=");
Element mainElement = findElement('main');
// verify
_assertRecordedRelationForElement(readElement, IndexConstants.IS_INVOKED_BY,
_expectedLocationQ(mainElement, '[0]', length: 1));
_assertRecordedRelationForElement(
writeElement,
IndexConstants.IS_INVOKED_BY,
_expectedLocationQ(mainElement, '[1] =', length: 1));
}
void test_isInvokedBy_operator_prefix() {
_indexTestUnit('''
class A {
A operator ~() => this;
}
main(A a) {
print(~a);
}
''');
// prepare elements
MethodElement element = findElement("~");
Element mainElement = findElement('main');
// verify
_assertRecordedRelationForElement(element, IndexConstants.IS_INVOKED_BY,
_expectedLocationQ(mainElement, '~a', length: 1));
}
void test_isInvokedBy_ParameterElement() {
_indexTestUnit('''
main(p()) {
p();
}''');
// prepare elements
Element mainElement = findElement("main");
Element element = findElement("p");
// verify
_assertRecordedRelationForElement(element, IndexConstants.IS_INVOKED_BY,
_expectedLocation(mainElement, 'p();'));
}
void test_isMixedInBy_ClassDeclaration() {
_indexTestUnit('''
class A {} // 1
class B extends Object with A {} // 2
''');
// prepare elements
ClassElement classElementA = findElement("A");
ClassElement classElementB = findElement("B");
// verify
_assertRecordedRelationForElement(
classElementA,
IndexConstants.IS_MIXED_IN_BY,
_expectedLocation(classElementB, 'A {} // 2'));
}
void test_isMixedInBy_ClassTypeAlias() {
_indexTestUnit('''
class A {} // 1
class B = Object with A; // 2
''');
// prepare elements
ClassElement classElementA = findElement("A");
ClassElement classElementB = findElement("B");
// verify
_assertRecordedRelationForElement(
classElementA,
IndexConstants.IS_MIXED_IN_BY,
_expectedLocation(classElementB, 'A; // 2'));
}
void test_isReadBy_ParameterElement() {
_indexTestUnit('''
main(var p) {
print(p);
}
''');
// prepare elements
Element mainElement = findElement("main");
Element parameterElement = findElement("p");
// verify
_assertRecordedRelationForElement(parameterElement,
IndexConstants.IS_READ_BY, _expectedLocation(mainElement, 'p);'));
}
void test_isReadBy_VariableElement() {
_indexTestUnit('''
main() {
var v = 0;
print(v);
}
''');
// prepare elements
Element mainElement = findElement("main");
Element variableElement = findElement("v");
// verify
_assertRecordedRelationForElement(variableElement,
IndexConstants.IS_READ_BY, _expectedLocation(mainElement, 'v);'));
}
void test_isReadWrittenBy_ParameterElement() {
_indexTestUnit('''
main(int p) {
p += 1;
}
''');
// prepare elements
Element mainElement = findElement("main");
Element parameterElement = findElement("p");
// verify
_assertRecordedRelationForElement(
parameterElement,
IndexConstants.IS_READ_WRITTEN_BY,
_expectedLocation(mainElement, 'p += 1'));
}
void test_isReadWrittenBy_VariableElement() {
_indexTestUnit('''
main() {
var v = 0;
v += 1;
}
''');
// prepare elements
Element mainElement = findElement("main");
Element variableElement = findElement("v");
// verify
_assertRecordedRelationForElement(
variableElement,
IndexConstants.IS_READ_WRITTEN_BY,
_expectedLocation(mainElement, 'v += 1'));
}
void test_isReferencedBy_ClassElement() {
_indexTestUnit('''
class A {
static var field;
}
main(A p) {
A v;
new A(); // 2
A.field = 1;
print(A.field); // 3
}
''');
// prepare elements
ClassElement aElement = findElement("A");
Element mainElement = findElement("main");
ParameterElement pElement = findElement("p");
VariableElement vElement = findElement("v");
IndexableElement indexable = new IndexableElement(aElement);
// verify
_assertRecordedRelation(indexable, IndexConstants.IS_REFERENCED_BY,
_expectedLocation(pElement, 'A p) {'));
_assertRecordedRelation(indexable, IndexConstants.IS_REFERENCED_BY,
_expectedLocation(vElement, 'A v;'));
_assertRecordedRelation(indexable, IndexConstants.IS_REFERENCED_BY,
_expectedLocation(mainElement, 'A(); // 2'));
_assertRecordedRelation(indexable, IndexConstants.IS_REFERENCED_BY,
_expectedLocation(mainElement, 'A.field = 1;'));
_assertRecordedRelation(indexable, IndexConstants.IS_REFERENCED_BY,
_expectedLocation(mainElement, 'A.field); // 3'));
}
void test_isReferencedBy_ClassTypeAlias() {
_indexTestUnit('''
class A {}
class B = Object with A;
main(B p) {
B v;
}
''');
// prepare elements
ClassElement bElement = findElement("B");
ParameterElement pElement = findElement("p");
VariableElement vElement = findElement("v");
IndexableElement indexable = new IndexableElement(bElement);
// verify
_assertRecordedRelation(indexable, IndexConstants.IS_REFERENCED_BY,
_expectedLocation(pElement, 'B p) {'));
_assertRecordedRelation(indexable, IndexConstants.IS_REFERENCED_BY,
_expectedLocation(vElement, 'B v;'));
}
void test_isReferencedBy_CompilationUnitElement_export() {
addSource(
'/lib.dart',
'''
library lib;
''');
_indexTestUnit('''
export 'lib.dart';
''');
// prepare elements
LibraryElement libElement = testLibraryElement.exportedLibraries[0];
CompilationUnitElement libUnitElement = libElement.definingCompilationUnit;
// verify
_assertRecordedRelationForElement(
libUnitElement,
IndexConstants.IS_REFERENCED_BY,
_expectedLocation(testUnitElement, "'lib.dart'", length: 10));
}
void test_isReferencedBy_CompilationUnitElement_import() {
addSource(
'/lib.dart',
'''
library lib;
''');
_indexTestUnit('''
import 'lib.dart';
''');
// prepare elements
LibraryElement libElement = testLibraryElement.imports[0].importedLibrary;
CompilationUnitElement libUnitElement = libElement.definingCompilationUnit;
// verify
_assertRecordedRelationForElement(
libUnitElement,
IndexConstants.IS_REFERENCED_BY,
_expectedLocation(testUnitElement, "'lib.dart'", length: 10));
}
void test_isReferencedBy_CompilationUnitElement_part() {
addSource('/my_unit.dart', 'part of my_lib;');
_indexTestUnit('''
library my_lib;
part 'my_unit.dart';
''');
// prepare elements
CompilationUnitElement myUnitElement = testLibraryElement.parts[0];
// verify
_assertRecordedRelationForElement(
myUnitElement,
IndexConstants.IS_REFERENCED_BY,
_expectedLocation(testUnitElement, "'my_unit.dart';", length: 14));
}
void test_isReferencedBy_ConstructorElement() {
_indexTestUnit('''
class A implements B {
A() {}
A.foo() {}
}
class B extends A {
B() : super(); // marker-1
B.foo() : super.foo(); // marker-2
factory B.bar() = A.foo; // marker-3
}
main() {
new A(); // marker-main-1
new A.foo(); // marker-main-2
}
''');
// prepare elements
Element mainElement = findElement('main');
var isConstructor = (node) => node is ConstructorDeclaration;
ConstructorElement consA = findNodeElementAtString("A()", isConstructor);
ConstructorElement consA_foo =
findNodeElementAtString("A.foo()", isConstructor);
ConstructorElement consB = findNodeElementAtString("B()", isConstructor);
ConstructorElement consB_foo =
findNodeElementAtString("B.foo()", isConstructor);
ConstructorElement consB_bar =
findNodeElementAtString("B.bar()", isConstructor);
IndexableElement indexableA = new IndexableElement(consA);
IndexableElement indexableA_foo = new IndexableElement(consA_foo);
// A()
_assertRecordedRelation(indexableA, IndexConstants.IS_REFERENCED_BY,
_expectedLocation(consB, '(); // marker-1', length: 0));
_assertRecordedRelation(indexableA, IndexConstants.IS_REFERENCED_BY,
_expectedLocation(mainElement, '(); // marker-main-1', length: 0));
// A.foo()
_assertRecordedRelation(indexableA_foo, IndexConstants.IS_REFERENCED_BY,
_expectedLocation(consB_foo, '.foo(); // marker-2', length: 4));
_assertRecordedRelation(indexableA_foo, IndexConstants.IS_REFERENCED_BY,
_expectedLocation(consB_bar, '.foo; // marker-3', length: 4));
_assertRecordedRelation(indexableA_foo, IndexConstants.IS_REFERENCED_BY,
_expectedLocation(mainElement, '.foo(); // marker-main-2', length: 4));
}
void test_isReferencedBy_ConstructorElement_classTypeAlias() {
_indexTestUnit('''
class M {}
class A implements B {
A() {}
A.named() {}
}
class B = A with M;
main() {
new B(); // marker-main-1
new B.named(); // marker-main-2
}
''');
// prepare elements
Element mainElement = findElement('main');
var isConstructor = (node) => node is ConstructorDeclaration;
ConstructorElement consA = findNodeElementAtString("A()", isConstructor);
ConstructorElement consA_named =
findNodeElementAtString("A.named()", isConstructor);
// verify
_assertRecordedRelationForElement(consA, IndexConstants.IS_REFERENCED_BY,
_expectedLocation(mainElement, '(); // marker-main-1', length: 0));
_assertRecordedRelationForElement(
consA_named,
IndexConstants.IS_REFERENCED_BY,
_expectedLocation(mainElement, '.named(); // marker-main-2',
length: 6));
}
void test_isReferencedBy_ConstructorElement_redirection() {
_indexTestUnit('''
class A {
A() : this.bar();
A.foo() : this(); // marker
A.bar();
}
''');
// prepare elements
var isConstructor = (node) => node is ConstructorDeclaration;
ConstructorElement constructorA =
findNodeElementAtString("A()", isConstructor);
ConstructorElement constructorA_foo =
findNodeElementAtString("A.foo()", isConstructor);
ConstructorElement constructorA_bar =
findNodeElementAtString("A.bar()", isConstructor);
// A()
_assertRecordedRelationForElement(
constructorA,
IndexConstants.IS_REFERENCED_BY,
_expectedLocation(constructorA_foo, '(); // marker', length: 0));
// A.foo()
_assertRecordedRelationForElement(
constructorA_bar,
IndexConstants.IS_REFERENCED_BY,
_expectedLocation(constructorA, '.bar();', length: 4));
}
void test_isReferencedBy_FieldElement() {
_indexTestUnit('''
class A {
var field;
A({this.field});
m() {
field = 1; // nq
print(field); // nq
}
}
main(A a) {
a.field = 2; // q
print(a.field); // q
new A(field: 3);
}
''');
// prepare elements
Element mElement = findElement("m");
Element mainElement = findElement("main");
FieldElement fieldElement = findElement("field");
PropertyAccessorElement getter = fieldElement.getter;
PropertyAccessorElement setter = fieldElement.setter;
IndexableElement indexableGetter = new IndexableElement(getter);
IndexableElement indexableSetter = new IndexableElement(setter);
// m()
_assertRecordedRelation(indexableSetter, IndexConstants.IS_REFERENCED_BY,
_expectedLocation(mElement, 'field = 1; // nq'));
_assertRecordedRelation(indexableGetter, IndexConstants.IS_REFERENCED_BY,
_expectedLocation(mElement, 'field); // nq'));
// main()
_assertRecordedRelation(indexableSetter, IndexConstants.IS_REFERENCED_BY,
_expectedLocationQ(mainElement, 'field = 2; // q'));
_assertRecordedRelation(indexableGetter, IndexConstants.IS_REFERENCED_BY,
_expectedLocationQ(mainElement, 'field); // q'));
_assertRecordedRelationForElement(
fieldElement,
IndexConstants.IS_REFERENCED_BY,
_expectedLocation(mainElement, 'field: 3'));
}
void test_isReferencedBy_FunctionElement() {
_indexTestUnit('''
foo() {}
main() {
print(foo);
print(foo());
}
''');
// prepare elements
FunctionElement element = findElement("foo");
Element mainElement = findElement("main");
IndexableElement indexable = new IndexableElement(element);
// "referenced" here
_assertRecordedRelation(indexable, IndexConstants.IS_REFERENCED_BY,
_expectedLocation(mainElement, 'foo);'));
// only "invoked", but not "referenced"
{
_assertRecordedRelation(indexable, IndexConstants.IS_INVOKED_BY,
_expectedLocation(mainElement, 'foo());'));
_assertNoRecordedRelation(indexable, IndexConstants.IS_REFERENCED_BY,
_expectedLocation(mainElement, 'foo());'));
}
}
void test_isReferencedBy_FunctionTypeAliasElement() {
_indexTestUnit('''
typedef A();
main(A p) {
}
''');
// prepare elements
Element aElement = findElement('A');
Element pElement = findElement('p');
// verify
_assertRecordedRelationForElement(aElement, IndexConstants.IS_REFERENCED_BY,
_expectedLocation(pElement, 'A p) {'));
}
/**
* 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.
*/
void test_isReferencedBy_identifierInComment() {
_indexTestUnit('''
class A {}
/// [A] text
var myVariable = null;
''');
// prepare elements
Element aElement = findElement('A');
Element variableElement = findElement('myVariable');
IndexableElement indexable = new IndexableElement(aElement);
// verify
_assertRecordedRelation(indexable, IndexConstants.IS_REFERENCED_BY,
_expectedLocation(testUnitElement, 'A] text'));
_assertNoRecordedRelation(indexable, IndexConstants.IS_REFERENCED_BY,
_expectedLocation(variableElement, 'A] text'));
}
void test_isReferencedBy_ImportElement_noPrefix() {
addSource(
'/lib.dart',
'''
library lib;
var myVar;
myFunction() {}
myToHide() {}
''');
_indexTestUnit('''
import 'lib.dart' show myVar, myFunction hide myToHide;
main() {
myVar = 1;
myFunction();
print(0);
}
''');
// prepare elements
ImportElement importElement = testLibraryElement.imports[0];
Element mainElement = findElement('main');
IndexableElement indexable = new IndexableElement(importElement);
// verify
_assertRecordedRelation(indexable, IndexConstants.IS_REFERENCED_BY,
_expectedLocation(mainElement, 'myVar = 1;', length: 0));
_assertRecordedRelation(indexable, IndexConstants.IS_REFERENCED_BY,
_expectedLocation(mainElement, 'myFunction();', length: 0));
_assertNoRecordedRelation(indexable, IndexConstants.IS_REFERENCED_BY,
_expectedLocation(mainElement, 'print(0);', length: 0));
// no references from import combinators
_assertNoRecordedRelation(indexable, IndexConstants.IS_REFERENCED_BY,
_expectedLocation(testUnitElement, 'myVar, ', length: 0));
_assertNoRecordedRelation(indexable, IndexConstants.IS_REFERENCED_BY,
_expectedLocation(testUnitElement, 'myFunction hide', length: 0));
_assertNoRecordedRelation(indexable, IndexConstants.IS_REFERENCED_BY,
_expectedLocation(testUnitElement, 'myToHide;', length: 0));
}
void test_isReferencedBy_ImportElement_withPrefix() {
addSource(
'/libA.dart',
'''
library libA;
var myVar;
''');
addSource(
'/libB.dart',
'''
library libB;
class MyClass {}
''');
_indexTestUnit('''
import 'libA.dart' as pref;
import 'libB.dart' as pref;
main() {
pref.myVar = 1;
new pref.MyClass();
}
''');
// prepare elements
ImportElement importElementA = testLibraryElement.imports[0];
ImportElement importElementB = testLibraryElement.imports[1];
Element mainElement = findElement('main');
// verify
_assertRecordedRelationForElement(
importElementA,
IndexConstants.IS_REFERENCED_BY,
_expectedLocation(mainElement, 'pref.myVar = 1;', length: 5));
_assertRecordedRelationForElement(
importElementB,
IndexConstants.IS_REFERENCED_BY,
_expectedLocation(mainElement, 'pref.MyClass();', length: 5));
}
void test_isReferencedBy_ImportElement_withPrefix_combinators() {
addSource(
'/lib.dart',
'''
library lib;
class A {}
class B {}
''');
_indexTestUnit('''
import 'lib.dart' as pref show A;
import 'lib.dart' as pref show B;
import 'lib.dart';
import 'lib.dart' as otherPrefix;
main() {
new pref.A();
new pref.B();
}
''');
// prepare elements
ImportElement importElementA = testLibraryElement.imports[0];
ImportElement importElementB = testLibraryElement.imports[1];
Element mainElement = findElement('main');
// verify
_assertRecordedRelationForElement(
importElementA,
IndexConstants.IS_REFERENCED_BY,
_expectedLocation(mainElement, 'pref.A();', length: 5));
_assertRecordedRelationForElement(
importElementB,
IndexConstants.IS_REFERENCED_BY,
_expectedLocation(mainElement, 'pref.B();', length: 5));
}
void test_isReferencedBy_ImportElement_withPrefix_invocation() {
addSource(
'/lib.dart',
'''
library lib;
myFunc() {}
''');
_indexTestUnit('''
import 'lib.dart' as pref;
main() {
pref.myFunc();
}
''');
// prepare elements
ImportElement importElement = testLibraryElement.imports[0];
Element mainElement = findElement('main');
// verify
_assertRecordedRelationForElement(
importElement,
IndexConstants.IS_REFERENCED_BY,
_expectedLocation(mainElement, 'pref.myFunc();', length: 5));
}
void test_isReferencedBy_ImportElement_withPrefix_oneCandidate() {
addSource(
'/lib.dart',
'''
library lib;
class A {}
class B {}
''');
_indexTestUnit('''
import 'lib.dart' as pref show A;
main() {
new pref.A();
}
''');
// prepare elements
ImportElement importElement = testLibraryElement.imports[0];
Element mainElement = findElement('main');
// verify
_assertRecordedRelationForElement(
importElement,
IndexConstants.IS_REFERENCED_BY,
_expectedLocation(mainElement, 'pref.A();', length: 5));
}
void test_isReferencedBy_ImportElement_withPrefix_unresolvedElement() {
verifyNoTestUnitErrors = false;
addSource(
'/lib.dart',
'''
library lib;
''');
_indexTestUnit('''
import 'lib.dart' as pref;
main() {
pref.myVar = 1;
}
''');
}
void test_isReferencedBy_ImportElement_withPrefix_wrongInvocation() {
verifyNoTestUnitErrors = false;
_indexTestUnit('''
import 'dart:math' as m;
main() {
m();
}''');
}
void test_isReferencedBy_ImportElement_withPrefix_wrongPrefixedIdentifier() {
verifyNoTestUnitErrors = false;
_indexTestUnit('''
import 'dart:math' as m;
main() {
x.m;
}
''');
}
void test_isReferencedBy_LabelElement() {
_indexTestUnit('''
main() {
L: while (true) {
break L;
}
}
''');
// prepare elements
Element mainElement = findElement('main');
Element element = findElement('L');
// verify
_assertRecordedRelationForElement(element, IndexConstants.IS_REFERENCED_BY,
_expectedLocation(mainElement, 'L;'));
}
void test_isReferencedBy_libraryName() {
Source libSource = addSource(
'/lib.dart',
'''
library lib;
part 'test.dart';
''');
testCode = 'part of lib;';
testSource = addSource('/test.dart', testCode);
testUnit = resolveDartUnit(testSource, libSource);
testUnitElement = testUnit.element;
testLibraryElement = testUnitElement.library;
indexDartUnit(store, context, testUnit);
// verify
_assertRecordedRelationForElement(
testLibraryElement,
IndexConstants.IS_REFERENCED_BY,
_expectedLocation(testUnitElement, "lib;"));
}
void test_isReferencedBy_MethodElement() {
_indexTestUnit('''
class A {
method() {}
main() {
print(this.method); // q
print(method); // nq
}
}''');
// prepare elements
Element mainElement = findElement("main");
MethodElement methodElement = findElement("method");
IndexableElement indexable = new IndexableElement(methodElement);
// verify
_assertRecordedRelation(indexable, IndexConstants.IS_REFERENCED_BY,
_expectedLocationQ(mainElement, 'method); // q'));
_assertRecordedRelation(indexable, IndexConstants.IS_REFERENCED_BY,
_expectedLocation(mainElement, 'method); // nq'));
}
void test_isReferencedBy_ParameterElement() {
_indexTestUnit('''
foo({var p}) {}
main() {
foo(p: 1);
}
''');
// prepare elements
Element mainElement = findElement('main');
Element element = findElement('p');
// verify
_assertRecordedRelationForElement(element, IndexConstants.IS_REFERENCED_BY,
_expectedLocation(mainElement, 'p: 1'));
}
void test_isReferencedBy_PrefixElement() {
_indexTestUnit('''
import 'dart:async' as ppp;
main() {
ppp.Future a;
ppp.Stream b;
}
''');
// prepare elements
PrefixElement element = findNodeElementAtString('ppp;');
Element elementA = findElement('a');
Element elementB = findElement('b');
IndexableElement indexable = new IndexableElement(element);
// verify
_assertRecordedRelation(indexable, IndexConstants.IS_REFERENCED_BY,
_expectedLocation(elementA, 'ppp.Future'));
_assertRecordedRelation(indexable, IndexConstants.IS_REFERENCED_BY,
_expectedLocation(elementB, 'ppp.Stream'));
_assertNoRecordedRelation(indexable, null, _expectedLocation(null, 'ppp;'));
}
void test_isReferencedBy_TopLevelVariableElement() {
addSource(
'/lib.dart',
'''
library lib;
var V;
''');
_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
}''');
// prepare elements
TopLevelVariableElement variable = importedUnit().topLevelVariables[0];
Element mainElement = findElement("main");
IndexableElement indexableGetter = new IndexableElement(variable.getter);
IndexableElement indexableSetter = new IndexableElement(variable.setter);
// verify
_assertRecordedRelationForElement(variable, IndexConstants.IS_REFERENCED_BY,
_expectedLocation(testUnitElement, 'V; // imp'));
_assertRecordedRelation(indexableSetter, IndexConstants.IS_REFERENCED_BY,
_expectedLocation(mainElement, 'V = 5; // q'));
_assertRecordedRelation(indexableGetter, IndexConstants.IS_REFERENCED_BY,
_expectedLocation(mainElement, 'V); // q'));
_assertRecordedRelation(indexableSetter, IndexConstants.IS_REFERENCED_BY,
_expectedLocation(mainElement, 'V = 5; // nq'));
_assertRecordedRelation(indexableGetter, IndexConstants.IS_REFERENCED_BY,
_expectedLocation(mainElement, 'V); // nq'));
}
void test_isReferencedBy_typeInVariableList() {
_indexTestUnit('''
class A {}
A myVariable = null;
''');
// prepare elements
Element classElementA = findElement('A');
Element variableElement = findElement('myVariable');
// verify
_assertRecordedRelationForElement(
classElementA,
IndexConstants.IS_REFERENCED_BY,
_expectedLocation(variableElement, 'A myVariable'));
}
void test_isReferencedBy_TypeParameterElement() {
_indexTestUnit('''
class A<T> {
T f;
foo(T p) {
T v;
}
}
''');
// prepare elements
Element typeParameterElement = findElement('T');
Element fieldElement = findElement('f');
Element parameterElement = findElement('p');
Element variableElement = findElement('v');
IndexableElement indexable = new IndexableElement(typeParameterElement);
// verify
_assertRecordedRelation(indexable, IndexConstants.IS_REFERENCED_BY,
_expectedLocation(fieldElement, 'T f'));
_assertRecordedRelation(indexable, IndexConstants.IS_REFERENCED_BY,
_expectedLocation(parameterElement, 'T p'));
_assertRecordedRelation(indexable, IndexConstants.IS_REFERENCED_BY,
_expectedLocation(variableElement, 'T v'));
}
void test_isWrittenBy_ConstructorFieldInitializer() {
_indexTestUnit('''
class A {
int field;
A() : field = 5;
}
''');
// prepare elements
ClassElement classElement = findElement('A');
ConstructorElement constructorElement = classElement.constructors[0];
FieldElement fieldElement = findElement("field");
// verify
_assertRecordedRelationForElement(
fieldElement,
IndexConstants.IS_WRITTEN_BY,
_expectedLocation(constructorElement, 'field = 5'));
}
void test_isWrittenBy_FieldElement_fieldFormalParameter() {
_indexTestUnit('''
class A {
int field;
A(this.field);
}
''');
// prepare elements
FieldElement fieldElement = findElement("field");
Element fieldParameterElement = findNodeElementAtString("field);");
// verify
_assertRecordedRelationForElement(
fieldElement,
IndexConstants.IS_WRITTEN_BY,
_expectedLocation(fieldParameterElement, 'field);'));
}
void test_isWrittenBy_ParameterElement() {
_indexTestUnit('''
main(var p) {
p = 1;
}''');
// prepare elements
Element mainElement = findElement("main");
ParameterElement pElement = findElement("p");
// verify
_assertRecordedRelationForElement(pElement, IndexConstants.IS_WRITTEN_BY,
_expectedLocation(mainElement, 'p = 1'));
}
void test_isWrittenBy_VariableElement() {
_indexTestUnit('''
main() {
var v = 0;
v = 1;
}''');
// prepare elements
Element mainElement = findElement("main");
LocalVariableElement vElement = findElement("v");
// verify
_assertRecordedRelationForElement(vElement, IndexConstants.IS_WRITTEN_BY,
_expectedLocation(mainElement, 'v = 1'));
}
void test_nameIsInvokedBy() {
_indexTestUnit('''
class A {
test(x) {}
}
main(A a, p) {
a.test(1);
p.test(2);
}''');
// prepare elements
Element mainElement = findElement("main");
IndexableName indexable = new IndexableName('test');
// verify
_assertRecordedRelation(indexable, IndexConstants.IS_INVOKED_BY,
_expectedLocationQ(mainElement, 'test(1)'));
_assertRecordedRelation(indexable, IndexConstants.IS_INVOKED_BY,
_expectedLocationQU(mainElement, 'test(2)'));
_assertNoRecordedRelation(indexable, IndexConstants.IS_READ_BY,
_expectedLocationQU(mainElement, 'test(2)'));
}
void test_nameIsReadBy() {
_indexTestUnit('''
class A {
var test;
}
main(A a, p) {
print(a.test); // a
print(p.test); // p
}''');
// prepare elements
Element mainElement = findElement("main");
IndexableName indexable = new IndexableName('test');
// verify
_assertRecordedRelation(indexable, IndexConstants.IS_READ_BY,
_expectedLocationQ(mainElement, 'test); // a'));
_assertRecordedRelation(indexable, IndexConstants.IS_READ_BY,
_expectedLocationQU(mainElement, 'test); // p'));
}
void test_nameIsReadWrittenBy() {
_indexTestUnit('''
class A {
var test;
}
main(A a, p) {
a.test += 1;
p.test += 2;
}''');
// prepare elements
Element mainElement = findElement("main");
IndexableName indexable = new IndexableName('test');
// verify
_assertRecordedRelation(indexable, IndexConstants.IS_READ_WRITTEN_BY,
_expectedLocationQ(mainElement, 'test += 1'));
_assertRecordedRelation(indexable, IndexConstants.IS_READ_WRITTEN_BY,
_expectedLocationQU(mainElement, 'test += 2'));
}
void test_nameIsWrittenBy() {
_indexTestUnit('''
class A {
var test;
}
main(A a, p) {
a.test = 1;
p.test = 2;
}''');
// prepare elements
Element mainElement = findElement("main");
IndexableName indexable = new IndexableName('test');
// verify
_assertRecordedRelation(indexable, IndexConstants.IS_WRITTEN_BY,
_expectedLocationQ(mainElement, 'test = 1'));
_assertRecordedRelation(indexable, IndexConstants.IS_WRITTEN_BY,
_expectedLocationQU(mainElement, 'test = 2'));
}
void test_nullUnit() {
indexDartUnit(store, context, null);
}
void test_nullUnitElement() {
CompilationUnit unit = new CompilationUnit(null, null, [], [], null);
indexDartUnit(store, context, unit);
}
void _assertDefinesTopLevelElement(Element element) {
ExpectedLocation location = new ExpectedLocation(
element, element.nameOffset, element.name.length, false, true);
_assertRecordedRelationForElement(
testLibraryElement, IndexConstants.DEFINES, location);
expect(recordedTopElements, contains(element));
}
/**
* Asserts that [recordedRelations] has no item with the specified properties.
*/
void _assertNoRecordedRelation(IndexableObject expectedIndexable,
RelationshipImpl relationship, ExpectedLocation location) {
for (RecordedRelation recordedRelation in recordedRelations) {
if (_equalsRecordedRelation(
recordedRelation, expectedIndexable, relationship, location)) {
fail('not expected: ${recordedRelation} in\n' +
recordedRelations.join('\n'));
}
}
}
/**
* Asserts that [recordedRelations] has no item with the specified properties.
*/
void _assertNoRecordedRelationForElement(Element expectedElement,
RelationshipImpl relationship, ExpectedLocation location) {
_assertNoRecordedRelation(
new IndexableElement(expectedElement), relationship, location);
}
/**
* Asserts that [recordedRelations] has an item with the expected properties.
*/
LocationImpl _assertRecordedRelation(
IndexableObject expectedIndexable,
RelationshipImpl expectedRelationship,
ExpectedLocation expectedLocation) {
for (RecordedRelation recordedRelation in recordedRelations) {
if (_equalsRecordedRelation(recordedRelation, expectedIndexable,
expectedRelationship, expectedLocation)) {
return recordedRelation.location;
}
}
fail("not found\n$expectedIndexable $expectedRelationship "
"in $expectedLocation in\n" +
recordedRelations.join('\n'));
return null;
}
/**
* Asserts that [recordedRelations] has an item with the expected properties.
*/
LocationImpl _assertRecordedRelationForElement(
Element expectedElement,
RelationshipImpl expectedRelationship,
ExpectedLocation expectedLocation) {
return _assertRecordedRelationForIndexable(
new IndexableElement(expectedElement),
expectedRelationship,
expectedLocation);
}
/**
* Asserts that [recordedRelations] has an item with the expected properties.
*/
LocationImpl _assertRecordedRelationForIndexable(
IndexableObject expectedIndexable,
RelationshipImpl expectedRelationship,
ExpectedLocation expectedLocation) {
return _assertRecordedRelation(
expectedIndexable, expectedRelationship, expectedLocation);
}
/**
* Asserts that [recordedRelations] has an item with the expected properties.
*/
LocationImpl _assertRecordedRelationForName(
String expectedName,
RelationshipImpl expectedRelationship,
ExpectedLocation expectedLocation) {
return _assertRecordedRelationForIndexable(new IndexableName(expectedName),
expectedRelationship, expectedLocation);
}
ExpectedLocation _expectedLocation(Element element, String search,
{int length: -1, bool isQualified: false, bool isResolved: true}) {
int offset = findOffset(search);
if (length == -1) {
length = getLeadingIdentifierLength(search);
}
return new ExpectedLocation(
element, offset, length, isQualified, isResolved);
}
ExpectedLocation _expectedLocationQ(Element element, String search,
{int length: -1}) {
return _expectedLocation(element, search,
length: length, isQualified: true);
}
ExpectedLocation _expectedLocationQU(Element element, String search,
{int length: -1}) {
return _expectedLocation(element, search,
length: length, isQualified: true, isResolved: false);
}
void _indexTestUnit(String code) {
resolveTestUnit(code);
indexDartUnit(store, context, testUnit);
}
}
class ExpectedLocation {
Element element;
int offset;
int length;
bool isQualified;
bool isResolved;
ExpectedLocation(this.element, this.offset, this.length, this.isQualified,
this.isResolved);
@override
String toString() {
return 'ExpectedLocation(element=$element; offset=$offset; length=$length;'
' isQualified=$isQualified isResolved=$isResolved)';
}
}
class MockIndexStore extends TypedMock implements InternalIndexStore {
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
/**
* Information about a relation recorded into {@link IndexStore}.
*/
class RecordedRelation {
final IndexableObject indexable;
final RelationshipImpl relationship;
final LocationImpl location;
RecordedRelation(this.indexable, this.relationship, this.location);
@override
String toString() {
return 'RecordedRelation(indexable=$indexable; relationship=$relationship; '
'location=$location; flags='
'${location.isQualified ? "Q" : ""}'
'${location.isResolved ? "R" : ""})';
}
}