Validate local resolution scope, and compute initial name scope.

R=brianwilkerson@google.com, paulberry@google.com
BUG=

Review URL: https://codereview.chromium.org/2439583003 .
This commit is contained in:
Konstantin Shcheglov 2016-10-19 14:08:46 -07:00
parent 1e793c96b2
commit bf43e4b43e
3 changed files with 289 additions and 31 deletions

View file

@ -9395,6 +9395,10 @@ enum TypeResolverMode {
/**
* Resolve only type names that would be skipped during [api].
*
* Resolution must start from a unit member or a class member. For example
* it is not allowed to resolve types in a separate statement, or a function
* body.
*/
local
}
@ -9438,7 +9442,16 @@ class TypeResolverVisitor extends ScopedVisitor {
final TypeResolverMode mode;
bool _visitAllInLocalMode = false;
/**
* Is `true` when we are visiting all nodes in [TypeResolverMode.local] mode.
*/
bool _localModeVisitAll = false;
/**
* Is `true` if we are in [TypeResolverMode.local] mode, and the initial
* [nameScope] was computed.
*/
bool _localModeScopeReady = false;
/**
* Initialize a newly created visitor to resolve the nodes in an AST node.
@ -9794,21 +9807,47 @@ class TypeResolverVisitor extends ScopedVisitor {
// resolve only type names that are local.
if (mode == TypeResolverMode.local) {
// We are in the state of visiting all nodes.
if (_visitAllInLocalMode) {
if (_localModeVisitAll) {
return super.visitNode(node);
}
// Ensure that the name scope is ready.
if (!_localModeScopeReady) {
void fillNameScope(AstNode node) {
if (node is FunctionBody ||
node is FormalParameterList ||
node is VariableDeclaration) {
throw new StateError(
'Local type resolution must start from a class or unit member.');
}
// Create enclosing name scopes.
AstNode parent = node.parent;
if (parent != null) {
fillNameScope(parent);
}
// Create the name scope for the node.
if (node is ClassDeclaration) {
ClassElement classElement = node.element;
nameScope = new TypeParameterScope(nameScope, classElement);
nameScope = new ClassScope(nameScope, classElement);
}
}
fillNameScope(node);
_localModeScopeReady = true;
}
/**
* Visit the given [node] and all its children.
*/
void visitAllNodes(AstNode node) {
if (node != null) {
bool wasVisitAllInLocalMode = _visitAllInLocalMode;
bool wasVisitAllInLocalMode = _localModeVisitAll;
try {
_visitAllInLocalMode = true;
_localModeVisitAll = true;
node.accept(this);
} finally {
_visitAllInLocalMode = wasVisitAllInLocalMode;
_localModeVisitAll = wasVisitAllInLocalMode;
}
}
}

View file

@ -8,10 +8,12 @@ import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/src/dart/ast/token.dart';
import 'package:analyzer/src/dart/scanner/reader.dart';
import 'package:analyzer/src/dart/scanner/scanner.dart';
import 'package:analyzer/src/generated/parser.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/generated/testing/ast_factory.dart';
import 'package:analyzer/src/generated/testing/token_factory.dart';
import 'package:analyzer/src/generated/utilities_dart.dart';
@ -2986,6 +2988,21 @@ class ParserTestCase extends EngineTestCase {
return unit;
}
/**
* Parse the given [code] as a compilation unit.
*/
static CompilationUnit parseCompilationUnit2(String code,
{AnalysisErrorListener listener, bool parseGenericMethods: false}) {
listener ??= AnalysisErrorListener.NULL_LISTENER;
Scanner scanner = new Scanner(null, new CharSequenceReader(code), listener);
Token token = scanner.tokenize();
Parser parser = new Parser(null, listener);
parser.parseGenericMethods = parseGenericMethods;
CompilationUnit unit = parser.parseCompilationUnit(token);
unit.lineInfo = new LineInfo(scanner.lineStarts);
return unit;
}
/**
* Parse the given [source] as a statement. The [errorCodes] are the error
* codes of the errors that are expected to be found. If

View file

@ -2695,7 +2695,9 @@ A V = new A();
}
void test_modeLocal_noContext() {
CompilationUnit unit = ParserTestCase.parseCompilationUnit(r'''
CompilationUnit unit;
_resolveTypeModeLocal(
r'''
class C {
A f = new A();
A m([A p = const A()]) {
@ -2710,31 +2712,10 @@ A f([A p = const A()]) {
}
A V = new A();
A get G => new A();
''');
var unitElement = new CompilationUnitElementImpl('/test.dart');
ClassElementImpl A = ElementFactory.classElement2('A');
// Build API elements.
{
var holder = new ElementHolder();
unit.accept(new ElementBuilder(holder, unitElement));
}
// Resolve API types.
{
MemoryResourceProvider resourceProvider = new MemoryResourceProvider();
InternalAnalysisContext context = AnalysisContextFactory.contextWithCore(
resourceProvider: resourceProvider);
var source = resourceProvider.getFile('/test.dart').createSource();
var libraryElement = new LibraryElementImpl.forNode(context, null)
..definingCompilationUnit = unitElement;
var libraryScope = new LibraryScope(libraryElement);
var visitor = new TypeResolverVisitor(
libraryElement, source, _typeProvider, _listener,
nameScope: libraryScope, mode: TypeResolverMode.local);
libraryScope.define(A);
unit.accept(visitor);
}
''', (CompilationUnit u) {
unit = u;
return u;
});
// Top-level: C
{
@ -2827,6 +2808,185 @@ A get G => new A();
}
}
void test_modeLocal_withContext_bad_methodBody() {
expect(() {
_resolveTypeModeLocal(
r'''
class C<T1> {
A m<T2>() {
T1 v1;
T2 v2;
}
}
''', (CompilationUnit u) {
var c = u.declarations[0] as ClassDeclaration;
var m = c.members[0] as MethodDeclaration;
var mb = m.body as BlockFunctionBody;
return mb;
});
}, throwsStateError);
}
void test_modeLocal_withContext_bad_topLevelVariable_declaration() {
expect(() {
_resolveTypeModeLocal(
r'''
var v = new A();
''', (CompilationUnit u) {
var tlv = u.declarations[0] as TopLevelVariableDeclaration;
return tlv.variables.variables[0];
});
}, throwsStateError);
}
void test_modeLocal_withContext_bad_topLevelVariable_initializer() {
expect(() {
_resolveTypeModeLocal(
r'''
var v = new A();
''', (CompilationUnit u) {
var tlv = u.declarations[0] as TopLevelVariableDeclaration;
return tlv.variables.variables[0].initializer;
});
}, throwsStateError);
}
void test_modeLocal_withContext_class() {
ClassDeclaration c;
_resolveTypeModeLocal(
r'''
class C<T1> {
A m<T2>() {
T1 v1;
T2 v2;
}
}
''', (CompilationUnit u) {
c = u.declarations[0] as ClassDeclaration;
return c;
});
var m = c.members[0] as MethodDeclaration;
// The return type of "m" is not resolved.
expect(m.returnType.type, isNull);
var mb = m.body as BlockFunctionBody;
var ms = mb.block.statements;
// The type of "v1" is resolved.
{
var vd = ms[0] as VariableDeclarationStatement;
expect(vd.variables.type.type.toString(), 'T1');
}
// The type of "v2" is resolved.
{
var vd = ms[1] as VariableDeclarationStatement;
expect(vd.variables.type.type.toString(), 'T2');
}
}
void test_modeLocal_withContext_inClass_constructor() {
ConstructorDeclaration cc;
_resolveTypeModeLocal(
r'''
class C<T> {
C() {
T v1;
}
}
''', (CompilationUnit u) {
var c = u.declarations[0] as ClassDeclaration;
cc = c.members[0] as ConstructorDeclaration;
return cc;
});
var ccb = cc.body as BlockFunctionBody;
var ccs = ccb.block.statements;
// The type of "v" is resolved.
{
var vd = ccs[0] as VariableDeclarationStatement;
expect(vd.variables.type.type.toString(), 'T');
}
}
void test_modeLocal_withContext_inClass_method() {
MethodDeclaration m;
_resolveTypeModeLocal(
r'''
class C<T1> {
A m<T2>() {
T1 v1;
T2 v2;
}
}
''', (CompilationUnit u) {
var c = u.declarations[0] as ClassDeclaration;
m = c.members[0] as MethodDeclaration;
return m;
});
// The return type of "m" is not resolved.
expect(m.returnType.type, isNull);
var mb = m.body as BlockFunctionBody;
var ms = mb.block.statements;
// The type of "v1" is resolved.
{
var vd = ms[0] as VariableDeclarationStatement;
expect(vd.variables.type.type.toString(), 'T1');
}
// The type of "v2" is resolved.
{
var vd = ms[1] as VariableDeclarationStatement;
expect(vd.variables.type.type.toString(), 'T2');
}
}
void test_modeLocal_withContext_topLevelFunction() {
FunctionDeclaration f;
_resolveTypeModeLocal(
r'''
A m<T>() {
T v;
}
''', (CompilationUnit u) {
f = u.declarations[0] as FunctionDeclaration;
return f;
});
// The return type of "f" is not resolved.
expect(f.returnType.type, isNull);
var fb = f.functionExpression.body as BlockFunctionBody;
var fs = fb.block.statements;
// The type of "v" is resolved.
var vd = fs[0] as VariableDeclarationStatement;
expect(vd.variables.type.type.toString(), 'T');
}
void test_modeLocal_withContext_topLevelVariable() {
TopLevelVariableDeclaration v;
_resolveTypeModeLocal(
r'''
A v = new A();
''', (CompilationUnit u) {
v = u.declarations[0] as TopLevelVariableDeclaration;
return v;
});
// The type of "v" is not resolved.
expect(v.variables.type.type, isNull);
// The type of "v" initializer is resolved.
var vi = v.variables.variables[0].initializer as InstanceCreationExpression;
expect(vi.constructorName.type.type.toString(), 'A');
}
void test_visitCatchClause_exception() {
// catch (e)
CatchClause clause = AstFactory.catchClause("e");
@ -3413,6 +3573,48 @@ A get G => new A();
}
node.accept(_visitor);
}
/**
* Parse the given [code], build elements and resolve in the
* [TypeResolverMode.local] mode. The [code] is allowed to use only the type
* named `A`.
*/
void _resolveTypeModeLocal(
String code, AstNode getNodeToResolve(CompilationUnit unit)) {
CompilationUnit unit =
ParserTestCase.parseCompilationUnit2(code, parseGenericMethods: true);
var unitElement = new CompilationUnitElementImpl('/test.dart');
// Build API elements.
{
var holder = new ElementHolder();
unit.accept(new ElementBuilder(holder, unitElement));
}
// Prepare for resolution.
LibraryScope libraryScope;
TypeResolverVisitor visitor;
{
MemoryResourceProvider resourceProvider = new MemoryResourceProvider();
InternalAnalysisContext context = AnalysisContextFactory.contextWithCore(
resourceProvider: resourceProvider);
var source = resourceProvider.getFile('/test.dart').createSource();
var libraryElement = new LibraryElementImpl.forNode(context, null)
..definingCompilationUnit = unitElement;
libraryScope = new LibraryScope(libraryElement);
visitor = new TypeResolverVisitor(
libraryElement, source, _typeProvider, _listener,
nameScope: libraryScope, mode: TypeResolverMode.local);
}
// Define top-level types.
ClassElementImpl A = ElementFactory.classElement2('A');
libraryScope.define(A);
// Perform resolution.
AstNode nodeToResolve = getNodeToResolve(unit);
nodeToResolve.accept(visitor);
}
}
class _RootScope extends Scope {