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].
|
* 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
|
local
|
||||||
}
|
}
|
||||||
|
@ -9438,7 +9442,16 @@ class TypeResolverVisitor extends ScopedVisitor {
|
||||||
|
|
||||||
final TypeResolverMode mode;
|
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.
|
* 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.
|
// resolve only type names that are local.
|
||||||
if (mode == TypeResolverMode.local) {
|
if (mode == TypeResolverMode.local) {
|
||||||
// We are in the state of visiting all nodes.
|
// We are in the state of visiting all nodes.
|
||||||
if (_visitAllInLocalMode) {
|
if (_localModeVisitAll) {
|
||||||
return super.visitNode(node);
|
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.
|
* Visit the given [node] and all its children.
|
||||||
*/
|
*/
|
||||||
void visitAllNodes(AstNode node) {
|
void visitAllNodes(AstNode node) {
|
||||||
if (node != null) {
|
if (node != null) {
|
||||||
bool wasVisitAllInLocalMode = _visitAllInLocalMode;
|
bool wasVisitAllInLocalMode = _localModeVisitAll;
|
||||||
try {
|
try {
|
||||||
_visitAllInLocalMode = true;
|
_localModeVisitAll = true;
|
||||||
node.accept(this);
|
node.accept(this);
|
||||||
} finally {
|
} 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/token.dart';
|
||||||
import 'package:analyzer/dart/ast/visitor.dart';
|
import 'package:analyzer/dart/ast/visitor.dart';
|
||||||
import 'package:analyzer/error/error.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/ast/token.dart';
|
||||||
import 'package:analyzer/src/dart/scanner/reader.dart';
|
import 'package:analyzer/src/dart/scanner/reader.dart';
|
||||||
import 'package:analyzer/src/dart/scanner/scanner.dart';
|
import 'package:analyzer/src/dart/scanner/scanner.dart';
|
||||||
import 'package:analyzer/src/generated/parser.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/ast_factory.dart';
|
||||||
import 'package:analyzer/src/generated/testing/token_factory.dart';
|
import 'package:analyzer/src/generated/testing/token_factory.dart';
|
||||||
import 'package:analyzer/src/generated/utilities_dart.dart';
|
import 'package:analyzer/src/generated/utilities_dart.dart';
|
||||||
|
@ -2986,6 +2988,21 @@ class ParserTestCase extends EngineTestCase {
|
||||||
return unit;
|
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
|
* Parse the given [source] as a statement. The [errorCodes] are the error
|
||||||
* codes of the errors that are expected to be found. If
|
* codes of the errors that are expected to be found. If
|
||||||
|
|
|
@ -2695,7 +2695,9 @@ A V = new A();
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_modeLocal_noContext() {
|
void test_modeLocal_noContext() {
|
||||||
CompilationUnit unit = ParserTestCase.parseCompilationUnit(r'''
|
CompilationUnit unit;
|
||||||
|
_resolveTypeModeLocal(
|
||||||
|
r'''
|
||||||
class C {
|
class C {
|
||||||
A f = new A();
|
A f = new A();
|
||||||
A m([A p = const A()]) {
|
A m([A p = const A()]) {
|
||||||
|
@ -2710,31 +2712,10 @@ A f([A p = const A()]) {
|
||||||
}
|
}
|
||||||
A V = new A();
|
A V = new A();
|
||||||
A get G => new A();
|
A get G => new A();
|
||||||
''');
|
''', (CompilationUnit u) {
|
||||||
var unitElement = new CompilationUnitElementImpl('/test.dart');
|
unit = u;
|
||||||
ClassElementImpl A = ElementFactory.classElement2('A');
|
return u;
|
||||||
|
});
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Top-level: C
|
// 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() {
|
void test_visitCatchClause_exception() {
|
||||||
// catch (e)
|
// catch (e)
|
||||||
CatchClause clause = AstFactory.catchClause("e");
|
CatchClause clause = AstFactory.catchClause("e");
|
||||||
|
@ -3413,6 +3573,48 @@ A get G => new A();
|
||||||
}
|
}
|
||||||
node.accept(_visitor);
|
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 {
|
class _RootScope extends Scope {
|
||||||
|
|
Loading…
Reference in a new issue