diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart index fc0fb787922..e831fc09e62 100644 --- a/pkg/analyzer/lib/src/generated/resolver.dart +++ b/pkg/analyzer/lib/src/generated/resolver.dart @@ -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; } } } diff --git a/pkg/analyzer/test/generated/parser_test.dart b/pkg/analyzer/test/generated/parser_test.dart index 2e46dfeb9b9..61d65771a1e 100644 --- a/pkg/analyzer/test/generated/parser_test.dart +++ b/pkg/analyzer/test/generated/parser_test.dart @@ -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 diff --git a/pkg/analyzer/test/generated/resolver_test.dart b/pkg/analyzer/test/generated/resolver_test.dart index 23f67c85d26..bdd6b8c014a 100644 --- a/pkg/analyzer/test/generated/resolver_test.dart +++ b/pkg/analyzer/test/generated/resolver_test.dart @@ -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 { + A m() { + 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 { + A m() { + 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 { + 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 { + A m() { + 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 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 {