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:
Konstantin Shcheglov 2018-12-07 19:38:32 +00:00 committed by commit-bot@chromium.org
parent 3e8d87b59d
commit d1d35db153
2 changed files with 286 additions and 45 deletions

View file

@ -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) {

View file

@ -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 {