mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 15:17:07 +00:00
Record references for all expression types.
I'm reasonable confident that this covers all expression types, we can compute nodes and their dependencies for kernel, analyzer, analysis_server, front_end. R=brianwilkerson@google.com, paulberry@google.com Change-Id: Ifd47cfdc6fb5bde4b84a644501c8530e757fcf50 Reviewed-on: https://dart-review.googlesource.com/c/86620 Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
parent
3e8d87b59d
commit
d1d35db153
2 changed files with 286 additions and 45 deletions
|
@ -8,6 +8,7 @@ import 'package:analyzer/dart/element/element.dart';
|
|||
import 'package:analyzer/dart/element/type.dart';
|
||||
import 'package:analyzer/src/dart/analysis/dependency/node.dart';
|
||||
import 'package:analyzer/src/dart/ast/token.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
/// Collector of information about external nodes referenced by a node.
|
||||
///
|
||||
|
@ -146,6 +147,35 @@ class ReferenceCollector {
|
|||
}
|
||||
}
|
||||
|
||||
void _visitAssignmentExpression(AssignmentExpression node) {
|
||||
var assignmentType = node.operator.type;
|
||||
|
||||
_visitExpression(node.leftHandSide,
|
||||
get: assignmentType != TokenType.EQ, set: true);
|
||||
_visitExpression(node.rightHandSide);
|
||||
|
||||
if (assignmentType != TokenType.EQ &&
|
||||
assignmentType != TokenType.QUESTION_QUESTION_EQ) {
|
||||
var operatorType = operatorFromCompoundAssignment(assignmentType);
|
||||
_recordClassMemberReference(
|
||||
node.leftHandSide.staticType,
|
||||
operatorType.lexeme,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _visitBinaryExpression(BinaryExpression node) {
|
||||
var operatorName = node.operator.lexeme;
|
||||
var leftOperand = node.leftOperand;
|
||||
if (leftOperand is SuperExpression) {
|
||||
_superReferences.add(operatorName);
|
||||
} else {
|
||||
_visitExpression(leftOperand);
|
||||
_recordClassMemberReference(leftOperand.staticType, operatorName);
|
||||
}
|
||||
_visitExpression(node.rightOperand);
|
||||
}
|
||||
|
||||
void _visitCascadeExpression(CascadeExpression node) {
|
||||
_visitExpression(node.target);
|
||||
var sections = node.cascadeSections;
|
||||
|
@ -155,7 +185,7 @@ class ReferenceCollector {
|
|||
}
|
||||
}
|
||||
|
||||
void _visitExpression(Expression node) {
|
||||
void _visitExpression(Expression node, {bool get: true, bool set: false}) {
|
||||
if (node == null) return;
|
||||
|
||||
if (node is AdjacentStrings) {
|
||||
|
@ -164,26 +194,11 @@ class ReferenceCollector {
|
|||
_visitExpression(node.expression);
|
||||
_visitTypeAnnotation(node.type);
|
||||
} else if (node is AssignmentExpression) {
|
||||
_visitExpression(node.leftHandSide);
|
||||
_visitExpression(node.rightHandSide);
|
||||
var assignmentType = node.operator.type;
|
||||
if (assignmentType != TokenType.EQ &&
|
||||
assignmentType != TokenType.QUESTION_QUESTION_EQ) {
|
||||
var operatorType = operatorFromCompoundAssignment(assignmentType);
|
||||
_recordClassMemberReference(
|
||||
node.leftHandSide.staticType,
|
||||
operatorType.lexeme,
|
||||
);
|
||||
}
|
||||
_visitAssignmentExpression(node);
|
||||
} else if (node is AwaitExpression) {
|
||||
_visitExpression(node.expression);
|
||||
} else if (node is BinaryExpression) {
|
||||
_visitExpression(node.leftOperand);
|
||||
_visitExpression(node.rightOperand);
|
||||
_recordClassMemberReference(
|
||||
node.leftOperand.staticType,
|
||||
node.operator.lexeme,
|
||||
);
|
||||
_visitBinaryExpression(node);
|
||||
} else if (node is BooleanLiteral) {
|
||||
// no dependencies
|
||||
} else if (node is CascadeExpression) {
|
||||
|
@ -201,8 +216,7 @@ class ReferenceCollector {
|
|||
_visitTypeArguments(node.typeArguments);
|
||||
_visitArgumentList(node.argumentList);
|
||||
} else if (node is IndexExpression) {
|
||||
_visitExpression(node.target);
|
||||
_visitExpression(node.index);
|
||||
_visitIndexExpression(node, get: get, set: set);
|
||||
} else if (node is InstanceCreationExpression) {
|
||||
_visitInstanceCreationExpression(node);
|
||||
} else if (node is IntegerLiteral) {
|
||||
|
@ -229,18 +243,25 @@ class ReferenceCollector {
|
|||
} else if (node is PrefixedIdentifier) {
|
||||
_visitPrefixedIdentifier(node);
|
||||
} else if (node is PropertyAccess) {
|
||||
_visitPropertyAccess(node);
|
||||
_visitPropertyAccess(node, get: get, set: set);
|
||||
} else if (node is SetLiteral) {
|
||||
_visitSetLiteral(node);
|
||||
} else if (node is SimpleIdentifier) {
|
||||
_visitSimpleIdentifier(node);
|
||||
_visitSimpleIdentifier(node, get: get, set: set);
|
||||
} else if (node is SimpleStringLiteral) {
|
||||
// no dependencies
|
||||
} else if (node is StringInterpolation) {
|
||||
_visitStringInterpolation(node);
|
||||
} else if (node is ThisExpression) {
|
||||
} else if (node is SymbolLiteral) {
|
||||
// no dependencies
|
||||
// TODO(scheglov) not really, because "this" type depends on the hierarchy
|
||||
} else if (node is ThisExpression) {
|
||||
// Strongly speaking, "this" should add dependencies.
|
||||
// Just like any class reference, it depends on the class hierarchy.
|
||||
// For example adding a new type to the `implements` clause might make
|
||||
// it OK to pass `this` as an argument of an invocation.
|
||||
//
|
||||
// However the current plan is to resolve the whole library on a change.
|
||||
// So, we will resolve all implementations that reference `this`.
|
||||
} else if (node is ThrowExpression) {
|
||||
_visitExpression(node.expression);
|
||||
} else {
|
||||
|
@ -342,6 +363,32 @@ class ReferenceCollector {
|
|||
_localScopes.exit();
|
||||
}
|
||||
|
||||
void _visitIndexExpression(IndexExpression node,
|
||||
{@required bool get, @required bool set}) {
|
||||
var target = node.target;
|
||||
if (target == null) {
|
||||
// no dependencies
|
||||
} else if (target is SuperExpression) {
|
||||
if (get) {
|
||||
_superReferences.add('[]');
|
||||
}
|
||||
if (set) {
|
||||
_superReferences.add('[]=');
|
||||
}
|
||||
} else {
|
||||
_visitExpression(target);
|
||||
var targetType = target.staticType;
|
||||
if (get) {
|
||||
_recordClassMemberReference(targetType, '[]');
|
||||
}
|
||||
if (set) {
|
||||
_recordClassMemberReference(targetType, '[]=');
|
||||
}
|
||||
}
|
||||
|
||||
_visitExpression(node.index);
|
||||
}
|
||||
|
||||
void _visitInstanceCreationExpression(InstanceCreationExpression node) {
|
||||
var constructor = node.constructorName;
|
||||
|
||||
|
@ -429,16 +476,26 @@ class ReferenceCollector {
|
|||
_recordClassMemberReference(node.operand.staticType, operatorName);
|
||||
}
|
||||
|
||||
void _visitPropertyAccess(PropertyAccess node) {
|
||||
void _visitPropertyAccess(PropertyAccess node,
|
||||
{@required bool get, @required bool set}) {
|
||||
var realTarget = node.realTarget;
|
||||
var name = node.propertyName.name;
|
||||
|
||||
if (realTarget is SuperExpression) {
|
||||
_superReferences.add(node.propertyName.name);
|
||||
if (get) {
|
||||
_superReferences.add(name);
|
||||
}
|
||||
if (set) {
|
||||
_superReferences.add('$name=');
|
||||
}
|
||||
} else {
|
||||
_visitExpression(node.target);
|
||||
_recordClassMemberReference(
|
||||
realTarget.staticType,
|
||||
node.propertyName.name,
|
||||
);
|
||||
if (get) {
|
||||
_recordClassMemberReference(realTarget.staticType, name);
|
||||
}
|
||||
if (set) {
|
||||
_recordClassMemberReference(realTarget.staticType, '$name=');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -451,14 +508,21 @@ class ReferenceCollector {
|
|||
}
|
||||
}
|
||||
|
||||
void _visitSimpleIdentifier(SimpleIdentifier node) {
|
||||
void _visitSimpleIdentifier(SimpleIdentifier node,
|
||||
{@required bool get, @required bool set}) {
|
||||
if (node.isSynthetic) return;
|
||||
|
||||
var name = node.name;
|
||||
if (!_localScopes.contains(name) && name != 'void' && name != 'dynamic') {
|
||||
// TODO(scheglov) use `name=` if assignment context, or both
|
||||
if (_localScopes.contains(name) || name == 'void' || name == 'dynamic') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (get) {
|
||||
_recordUnprefixedReference(name);
|
||||
}
|
||||
if (set) {
|
||||
_recordUnprefixedReference('$name=');
|
||||
}
|
||||
}
|
||||
|
||||
void _visitStatement(Statement node) {
|
||||
|
|
|
@ -46,7 +46,7 @@ test() {
|
|||
x = y;
|
||||
}
|
||||
''');
|
||||
_assertImpl(library, 'test', NodeKind.FUNCTION, unprefixed: ['x', 'y']);
|
||||
_assertImpl(library, 'test', NodeKind.FUNCTION, unprefixed: ['x=', 'y']);
|
||||
}
|
||||
|
||||
test_assignmentExpression_compound() async {
|
||||
|
@ -64,7 +64,7 @@ test() {
|
|||
}
|
||||
''');
|
||||
_assertImpl(library, 'test', NodeKind.FUNCTION,
|
||||
unprefixed: ['x', 'y'],
|
||||
unprefixed: ['x', 'x=', 'y'],
|
||||
expectedMembers: [_ExpectedClassMember(aUri, 'B', '+')]);
|
||||
}
|
||||
|
||||
|
@ -74,7 +74,8 @@ test() {
|
|||
x ??= y;
|
||||
}
|
||||
''');
|
||||
_assertImpl(library, 'test', NodeKind.FUNCTION, unprefixed: ['x', 'y']);
|
||||
_assertImpl(library, 'test', NodeKind.FUNCTION,
|
||||
unprefixed: ['x', 'x=', 'y']);
|
||||
}
|
||||
|
||||
test_awaitExpression() async {
|
||||
|
@ -154,6 +155,34 @@ test() {
|
|||
);
|
||||
}
|
||||
|
||||
test_binaryExpression_super() async {
|
||||
var library = await buildTestLibrary(a, r'''
|
||||
class A {}
|
||||
|
||||
class B extends A {
|
||||
test() {
|
||||
super + x;
|
||||
}
|
||||
}
|
||||
''');
|
||||
_assertImpl(library, 'test', NodeKind.METHOD,
|
||||
memberOf: 'B', unprefixed: ['x'], superPrefixed: ['+']);
|
||||
}
|
||||
|
||||
test_binaryExpression_super2() async {
|
||||
var library = await buildTestLibrary(a, r'''
|
||||
class A {}
|
||||
|
||||
class B extends A {
|
||||
test() {
|
||||
super == x;
|
||||
}
|
||||
}
|
||||
''');
|
||||
_assertImpl(library, 'test', NodeKind.METHOD,
|
||||
memberOf: 'B', unprefixed: ['x'], superPrefixed: ['==']);
|
||||
}
|
||||
|
||||
test_binaryExpression_unique() async {
|
||||
var library = await buildTestLibrary(a, r'''
|
||||
class A {
|
||||
|
@ -221,10 +250,9 @@ test() {
|
|||
'y',
|
||||
'z'
|
||||
], expectedMembers: [
|
||||
_ExpectedClassMember(aUri, 'A', 'bar'),
|
||||
_ExpectedClassMember(aUri, 'A', 'bar='),
|
||||
_ExpectedClassMember(aUri, 'A', 'foo'),
|
||||
]);
|
||||
// TODO(scheglov) should be `bar=`
|
||||
}
|
||||
|
||||
test_conditionalExpression() async {
|
||||
|
@ -270,7 +298,92 @@ test() {
|
|||
unprefixed: ['T', 'x', 'y', 'z']);
|
||||
}
|
||||
|
||||
test_indexExpression() async {
|
||||
test_indexExpression_get() async {
|
||||
var library = await buildTestLibrary(a, r'''
|
||||
class A {}
|
||||
|
||||
A x;
|
||||
|
||||
test() {
|
||||
x[y];
|
||||
}
|
||||
''');
|
||||
_assertImpl(library, 'test', NodeKind.FUNCTION,
|
||||
unprefixed: ['x', 'y'],
|
||||
expectedMembers: [_ExpectedClassMember(aUri, 'A', '[]')]);
|
||||
}
|
||||
|
||||
test_indexExpression_getSet() async {
|
||||
var library = await buildTestLibrary(a, r'''
|
||||
class A {}
|
||||
|
||||
A x;
|
||||
|
||||
test() {
|
||||
x[y] += x;
|
||||
}
|
||||
''');
|
||||
_assertImpl(library, 'test', NodeKind.FUNCTION, unprefixed: [
|
||||
'x',
|
||||
'y'
|
||||
], expectedMembers: [
|
||||
_ExpectedClassMember(aUri, 'A', '[]'),
|
||||
_ExpectedClassMember(aUri, 'A', '[]=')
|
||||
]);
|
||||
}
|
||||
|
||||
test_indexExpression_set() async {
|
||||
var library = await buildTestLibrary(a, r'''
|
||||
class A {}
|
||||
|
||||
A x;
|
||||
|
||||
test() {
|
||||
x[y] = x;
|
||||
}
|
||||
''');
|
||||
_assertImpl(library, 'test', NodeKind.FUNCTION,
|
||||
unprefixed: ['x', 'y'],
|
||||
expectedMembers: [_ExpectedClassMember(aUri, 'A', '[]=')]);
|
||||
}
|
||||
|
||||
test_indexExpression_super_get() async {
|
||||
var library = await buildTestLibrary(a, r'''
|
||||
class C {
|
||||
test() {
|
||||
super[x];
|
||||
}
|
||||
}
|
||||
''');
|
||||
_assertImpl(library, 'test', NodeKind.METHOD,
|
||||
memberOf: 'C', unprefixed: ['x'], superPrefixed: ['[]']);
|
||||
}
|
||||
|
||||
test_indexExpression_super_getSet() async {
|
||||
var library = await buildTestLibrary(a, r'''
|
||||
class C {
|
||||
test() {
|
||||
super[x] += y;
|
||||
}
|
||||
}
|
||||
''');
|
||||
_assertImpl(library, 'test', NodeKind.METHOD,
|
||||
memberOf: 'C', unprefixed: ['x', 'y'], superPrefixed: ['[]', '[]=']);
|
||||
}
|
||||
|
||||
test_indexExpression_super_set() async {
|
||||
var library = await buildTestLibrary(a, r'''
|
||||
class C {
|
||||
test() {
|
||||
super[x] = y;
|
||||
}
|
||||
}
|
||||
''');
|
||||
_assertImpl(library, 'test', NodeKind.METHOD,
|
||||
memberOf: 'C', unprefixed: ['x', 'y'], superPrefixed: ['[]=']);
|
||||
}
|
||||
|
||||
test_indexExpression_unresolvedTarget() async {
|
||||
var library = await buildTestLibrary(a, r'''
|
||||
test() {
|
||||
x[y];
|
||||
|
@ -621,13 +734,11 @@ test() {
|
|||
_assertImpl(library, 'test', NodeKind.FUNCTION, unprefixed: ['x']);
|
||||
}
|
||||
|
||||
test_propertyAccess() async {
|
||||
test_propertyAccess_get() async {
|
||||
var library = await buildTestLibrary(a, r'''
|
||||
class A {}
|
||||
|
||||
class B extends A {}
|
||||
|
||||
B x;
|
||||
A x;
|
||||
|
||||
test() {
|
||||
(x).foo;
|
||||
|
@ -635,10 +746,43 @@ test() {
|
|||
''');
|
||||
_assertImpl(library, 'test', NodeKind.FUNCTION,
|
||||
unprefixed: ['x'],
|
||||
expectedMembers: [_ExpectedClassMember(aUri, 'B', 'foo')]);
|
||||
expectedMembers: [_ExpectedClassMember(aUri, 'A', 'foo')]);
|
||||
}
|
||||
|
||||
test_propertyAccess_super() async {
|
||||
test_propertyAccess_getSet() async {
|
||||
var library = await buildTestLibrary(a, r'''
|
||||
class A {}
|
||||
|
||||
A x;
|
||||
|
||||
test() {
|
||||
(x).foo += 1;
|
||||
}
|
||||
''');
|
||||
_assertImpl(library, 'test', NodeKind.FUNCTION, unprefixed: [
|
||||
'x'
|
||||
], expectedMembers: [
|
||||
_ExpectedClassMember(aUri, 'A', 'foo'),
|
||||
_ExpectedClassMember(aUri, 'A', 'foo='),
|
||||
]);
|
||||
}
|
||||
|
||||
test_propertyAccess_set() async {
|
||||
var library = await buildTestLibrary(a, r'''
|
||||
class A {}
|
||||
|
||||
A x;
|
||||
|
||||
test() {
|
||||
(x).foo = 1;
|
||||
}
|
||||
''');
|
||||
_assertImpl(library, 'test', NodeKind.FUNCTION,
|
||||
unprefixed: ['x'],
|
||||
expectedMembers: [_ExpectedClassMember(aUri, 'A', 'foo=')]);
|
||||
}
|
||||
|
||||
test_propertyAccess_super_get() async {
|
||||
var library = await buildTestLibrary(a, r'''
|
||||
class C {
|
||||
test() {
|
||||
|
@ -650,6 +794,30 @@ class C {
|
|||
memberOf: 'C', superPrefixed: ['foo']);
|
||||
}
|
||||
|
||||
test_propertyAccess_super_getSet() async {
|
||||
var library = await buildTestLibrary(a, r'''
|
||||
class C {
|
||||
test() {
|
||||
super.foo += 1;
|
||||
}
|
||||
}
|
||||
''');
|
||||
_assertImpl(library, 'test', NodeKind.METHOD,
|
||||
memberOf: 'C', superPrefixed: ['foo', 'foo=']);
|
||||
}
|
||||
|
||||
test_propertyAccess_super_set() async {
|
||||
var library = await buildTestLibrary(a, r'''
|
||||
class C {
|
||||
test() {
|
||||
super.foo = 1;
|
||||
}
|
||||
}
|
||||
''');
|
||||
_assertImpl(library, 'test', NodeKind.METHOD,
|
||||
memberOf: 'C', superPrefixed: ['foo=']);
|
||||
}
|
||||
|
||||
test_setLiteral() async {
|
||||
var library = await buildTestLibrary(a, r'''
|
||||
test() {
|
||||
|
@ -717,6 +885,15 @@ test() {
|
|||
_assertImpl(library, 'test', NodeKind.FUNCTION, unprefixed: ['x', 'y']);
|
||||
}
|
||||
|
||||
test_symbolLiteral() async {
|
||||
var library = await buildTestLibrary(a, r'''
|
||||
test() {
|
||||
#foo.bar;
|
||||
}
|
||||
''');
|
||||
_assertImpl(library, 'test', NodeKind.FUNCTION);
|
||||
}
|
||||
|
||||
test_thisExpression() async {
|
||||
var library = await buildTestLibrary(a, r'''
|
||||
class C {
|
||||
|
|
Loading…
Reference in a new issue