mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 00:39:49 +00:00
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:
parent
1e793c96b2
commit
bf43e4b43e
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue