Chromium Code Reviews| Index: lib/src/checker/resolver.dart |
| diff --git a/lib/src/checker/resolver.dart b/lib/src/checker/resolver.dart |
| index 944c69b8e897d627daebedaa9fab74d00f992874..7344ab4f1a317aae9be9949c2eb4c6f383ed43bd 100644 |
| --- a/lib/src/checker/resolver.dart |
| +++ b/lib/src/checker/resolver.dart |
| @@ -13,11 +13,11 @@ import 'package:analyzer/src/generated/engine.dart'; |
| import 'package:analyzer/src/generated/error.dart' as analyzer; |
| import 'package:analyzer/src/generated/java_io.dart' show JavaFile; |
| import 'package:analyzer/src/generated/resolver.dart'; |
| -import 'package:analyzer/src/generated/static_type_analyzer.dart'; |
| import 'package:analyzer/src/generated/sdk_io.dart' show DirectoryBasedDartSdk; |
| import 'package:analyzer/src/generated/source.dart' show DartUriResolver; |
| import 'package:analyzer/src/generated/source.dart' show Source; |
| import 'package:analyzer/src/generated/source_io.dart'; |
| +import 'package:analyzer/src/generated/static_type_analyzer.dart'; |
| import 'package:logging/logging.dart' as logger; |
| import 'package:dev_compiler/src/options.dart'; |
| @@ -106,29 +106,253 @@ InternalAnalysisContext _initContext(ResolverOptions options) { |
| var analysisOptions = new AnalysisOptionsImpl()..cacheSize = 512; |
| AnalysisContextImpl res = AnalysisEngine.instance.createAnalysisContext(); |
| res.analysisOptions = analysisOptions; |
| - res.resolverVisitorFactory = RestrictedResolverVisitor.constructor(options); |
| - if (options.inferFromOverrides) { |
| - res.typeResolverVisitorFactory = RestrictedTypeResolverVisitor.constructor; |
| - } |
| + res.libraryResolverFactory = |
| + (context) => new LibraryResolverWithInference(context, options); |
| return res; |
| } |
| -/// Overrides the default [ResolverVisitor] to comply with DDC's restricted |
| -/// type rules. This changes how types are promoted in conditional expressions |
| -/// and statements, and how types are computed on expressions. |
| +/// A [LibraryResolver] that performs inference on top-levels and fields based |
| +/// on the value of the initializer, and on fields and methods based on |
| +/// overriden members in super classes. |
|
vsm
2015/03/17 15:55:27
nit: overriden -> overridden
Siggi Cherem (dart-lang)
2015/03/17 16:29:22
Done.
|
| +class LibraryResolverWithInference extends LibraryResolver { |
| + final ResolverOptions _options; |
| + |
| + LibraryResolverWithInference(context, this._options) : super(context); |
| + |
| + @override |
| + void resolveReferencesAndTypes() { |
| + _resolveVariableReferences(); |
| + |
| + // Skip inference in the core libraries. |
| + if (resolvedLibraries.any((l) => l.librarySource.isInSystemLibrary)) { |
|
vsm
2015/03/17 15:55:27
Is resolvedLibraries the cycle here? I.e., if one
Siggi Cherem (dart-lang)
2015/03/17 16:29:22
Yeah, exactly, I added a comment to be clear.
|
| + _resolveReferencesAndTypes(false); |
| + return; |
| + } |
| + |
| + // Run resolution in two stages, skipping method bodies first, so we can run |
| + // type-inference before we fully analyze methods. |
| + _resolveReferencesAndTypes(true); |
| + _runInference(); |
| + _resolveReferencesAndTypes(false); |
| + } |
| + |
| + // Note: this was split from _resolveReferencesAndTypesInLibrary so we do it |
| + // only once. |
| + void _resolveVariableReferences() { |
| + for (Library library in resolvedLibraries) { |
| + for (Source source in library.compilationUnitSources) { |
| + library.getAST(source).accept( |
| + new VariableResolverVisitor.con1(library, source, typeProvider)); |
| + } |
| + } |
| + } |
| + |
| + // Note: this was split from _resolveReferencesAndTypesInLibrary so we can do |
| + // resolution in pieces. |
| + void _resolveReferencesAndTypes(bool skipMethods) { |
| + for (Library library in resolvedLibraries) { |
| + for (Source source in library.compilationUnitSources) { |
| + library.getAST(source).accept(new RestrictedResolverVisitor( |
| + library, source, typeProvider, _options, skipMethods)); |
| + } |
| + } |
| + } |
| + |
| + _runInference() { |
| + var consts = []; |
| + var statics = []; |
| + var classes = []; |
| + |
| + // Extract top-level members that are const, statics, or classes. |
| + for (Library library in resolvedLibraries) { |
| + for (Source source in library.compilationUnitSources) { |
| + CompilationUnit ast = library.getAST(source); |
| + for (var declaration in ast.declarations) { |
| + if (declaration is TopLevelVariableDeclaration) { |
| + if (declaration.variables.isConst) { |
| + consts.add(declaration.variables); |
| + } else { |
| + statics.add(declaration.variables); |
| + } |
| + } else if (declaration is ClassDeclaration) { |
| + classes.add(declaration); |
| + for (var member in declaration.members) { |
| + if (member is! FieldDeclaration) continue; |
| + if (member.fields.isConst) { |
| + consts.add(member.fields); |
| + } else if (member.isStatic) { |
| + statics.add(member.fields); |
| + } |
| + } |
| + } |
| + } |
| + } |
| + } |
| + |
| + // TODO(sigmund): consider propagating const types after this layer of |
| + // inferece, so their types can be used to initialize other members below. |
|
vsm
2015/03/17 15:55:27
nit: inferece -> inference
Siggi Cherem (dart-lang)
2015/03/17 16:29:22
Done.
|
| + _inferVariableFromInitializer(consts); |
| + _inferVariableFromInitializer(statics); |
| + |
| + // Track types in this strongest connected component, ensure we visit |
|
vsm
2015/03/17 15:55:27
strongly?
Siggi Cherem (dart-lang)
2015/03/17 16:29:22
Done.
|
| + // supertypes before subtypes. |
| + var typeToDeclaration = <InterfaceType, ClassDeclaration>{}; |
| + classes.forEach((c) => typeToDeclaration[c.element.type] = c); |
| + var seen = new Set<InterfaceType>(); |
| + visit(ClassDeclaration cls) { |
| + var element = cls.element; |
| + var type = element.type; |
| + if (seen.contains(type)) return; |
| + for (var supertype in element.allSupertypes) { |
| + var supertypeClass = typeToDeclaration[supertype]; |
| + if (supertypeClass != null) visit(supertypeClass); |
| + } |
| + seen.add(type); |
| + |
| + _isInstanceField(f) => |
| + f is FieldDeclaration && !f.isStatic && !f.fields.isConst; |
| + |
| + if (_options.inferFromOverrides) { |
| + // Infer field types from overrides first, otherwise from initializers. |
| + var pending = new Set<VariableDeclarationList>(); |
| + cls.members |
| + .where(_isInstanceField) |
| + .forEach((f) => _inferFieldTypeFromOverride(f, pending)); |
| + if (pending.isNotEmpty) _inferVariableFromInitializer(pending); |
| + |
| + // Infer return-types from overrides |
| + cls.members |
| + .where((m) => m is MethodDeclaration && !m.isStatic) |
| + .forEach(_inferMethodReturnTypeFromOverride); |
| + } else { |
| + _inferVariableFromInitializer( |
| + cls.members.where(_isInstanceField).map((f) => f.fields)); |
| + } |
| + } |
| + classes.forEach(visit); |
| + } |
| + |
| + /// Attempts to infer the type on [field] from overriden fields or getters if |
| + /// a type was not specified. If no type could be inferred, but it contains an |
| + /// initializer, we add it to [pending] so we can try to infer it using the |
| + /// initializer type instead. |
| + void _inferFieldTypeFromOverride( |
| + FieldDeclaration field, Set<VariableDeclarationList> pending) { |
| + var variables = field.fields; |
| + for (var variable in variables.variables) { |
| + var varElement = variable.element; |
| + if (!varElement.type.isDynamic || variables.type != null) continue; |
| + var getter = varElement.getter; |
| + var type = searchTypeFor(varElement.enclosingElement.type, getter); |
| + if (type != null && !type.returnType.isDynamic) { |
| + var newType = type.returnType; |
| + varElement.type = newType; |
| + varElement.getter.returnType = newType; |
| + if (!varElement.isFinal) varElement.setter.parameters[0].type = newType; |
| + } else if (variable.initializer != null) { |
| + pending.add(variables); |
| + } |
| + } |
| + } |
| + |
| + void _inferMethodReturnTypeFromOverride(MethodDeclaration method) { |
| + var methodElement = method.element; |
| + if ((methodElement is MethodElement || |
| + methodElement is PropertyAccessorElement) && |
| + methodElement.returnType.isDynamic && |
| + method.returnType == null) { |
| + var type = |
| + searchTypeFor(methodElement.enclosingElement.type, methodElement); |
| + if (type != null && !type.returnType.isDynamic) { |
| + methodElement.returnType = type.returnType; |
| + } |
| + } |
| + } |
| + |
| + void _inferVariableFromInitializer( |
| + Iterable<VariableDeclarationList> variables) { |
| + for (var variableList in variables) { |
| + if (variableList.type != null) continue; |
| + if (_options.onlyInferConstsAndFinalFields && |
| + !variableList.isFinal && |
| + !variableList.isConst) { |
| + return; |
| + } |
| + for (var variable in variableList.variables) { |
| + var initializer = variable.initializer; |
| + if (initializer == null) continue; |
| + var type = initializer.staticType; |
| + if (type == null || type.isDynamic) continue; |
|
vsm
2015/03/17 15:55:27
If the initializer type is Null or Bottom, we migh
Siggi Cherem (dart-lang)
2015/03/17 16:29:22
Ah, interesting. Good point. I added a check for i
|
| + if (!_canInferFrom(initializer)) continue; |
| + var element = variable.element; |
|
vsm
2015/03/17 15:55:27
Shouldn't we only update the type if it was declar
Siggi Cherem (dart-lang)
2015/03/17 16:29:22
Correct, the check in line 275 above:
if (variab
vsm
2015/03/17 17:18:48
Ah, thanks - missed the check above.
|
| + // Note: it's ok to update the type here, since initializer.staticType |
| + // is already computed for all declarations in the library cycle. The |
| + // new types will only be propagated on a second run of the |
| + // ResolverVisitor. |
| + element.type = type; |
| + element.getter.returnType = type; |
| + if (!element.isFinal && !element.isConst) { |
| + element.setter.parameters[0].type = type; |
| + } |
| + } |
| + } |
| + } |
| + |
| + bool _canInferFrom(Expression expression) { |
| + if (expression is Literal) return true; |
| + if (expression is InstanceCreationExpression) return true; |
| + if (expression is FunctionExpression) return true; |
| + if (expression is AsExpression) return true; |
| + if (expression is CascadeExpression) { |
| + return _canInferFrom(expression.target); |
| + } |
| + if (expression is SimpleIdentifier || expression is PropertyAccess) { |
| + return _options.inferStaticsFromIdentifiers; |
| + } |
| + if (expression is PrefixedIdentifier) { |
| + if (expression.staticElement is PropertyAccessorElement) { |
| + return _options.inferStaticsFromIdentifiers; |
| + } |
| + return _canInferFrom(expression.identifier); |
| + } |
| + if (expression is MethodInvocation) { |
| + return _canInferFrom(expression.target); |
| + } |
| + if (expression is BinaryExpression) { |
| + return _canInferFrom(expression.leftOperand); |
| + } |
| + if (expression is ConditionalExpression) { |
| + return _canInferFrom(expression.thenExpression) && |
| + _canInferFrom(expression.elseExpression); |
| + } |
| + if (expression is PrefixExpression) { |
| + return _canInferFrom(expression.operand); |
| + } |
| + if (expression is PostfixExpression) { |
| + return _canInferFrom(expression.operand); |
| + } |
| + return false; |
| + } |
| +} |
| + |
| +/// Overrides the default [ResolverVisitor] to support type inference in |
| +/// [LibraryResolverWithInference] above. |
| +/// |
| +/// Before inference, this visitor is used to resolve top-levels, classes, and |
| +/// fields, but nothing withihn method bodies. After inference, this visitor is |
| +/// used again to step into method bodies and complete resolution as a second |
| +/// phase. |
| class RestrictedResolverVisitor extends ResolverVisitor { |
| final TypeProvider _typeProvider; |
| + /// Whether to skip resolution within method bodies. |
| + final bool skipMethodBodies; |
| + |
| RestrictedResolverVisitor(Library library, Source source, |
| - TypeProvider typeProvider, ResolverOptions options) |
| + TypeProvider typeProvider, ResolverOptions options, this.skipMethodBodies) |
| : _typeProvider = typeProvider, |
| super.con1(library, source, typeProvider, |
| - typeAnalyzerFactory: RestrictedStaticTypeAnalyzer |
| - .constructor(options)); |
| - |
| - static constructor(options) => |
| - (Library library, Source source, TypeProvider typeProvider) => |
| - new RestrictedResolverVisitor(library, source, typeProvider, options); |
| + typeAnalyzerFactory: RestrictedStaticTypeAnalyzer.constructor); |
| @override |
| visitCatchClause(CatchClause node) { |
| @@ -147,68 +371,51 @@ class RestrictedResolverVisitor extends ResolverVisitor { |
| } |
| @override |
| - Object visitCompilationUnit(CompilationUnit node) { |
| - // Similar to the definition in ResolverVisitor.visitCompilationUnit, but |
| - // changed to visit all top-level fields first, then static fields on all |
| - // classes, then all top-level functions, then the rest of the classes. |
| - RestrictedStaticTypeAnalyzer restrictedAnalyzer = typeAnalyzer_J2DAccessor; |
| - overrideManager.enterScope(); |
| - try { |
| - var thisLib = node.element.enclosingElement; |
| - restrictedAnalyzer._isLibraryContainedInSingleUnit.putIfAbsent(thisLib, |
| - () { |
| - if (thisLib.units.length > 1) return false; |
| - for (var lib in thisLib.visibleLibraries) { |
| - if (lib != thisLib && lib.visibleLibraries.contains(thisLib)) { |
| - return false; |
| - } |
| - } |
| - return true; |
| - }); |
| + Object visitNode(AstNode node) { |
| + if (skipMethodBodies && |
| + (node is FunctionBody || |
| + node is FunctionExpression || |
| + node is FunctionExpressionInvocation || |
| + node is SuperConstructorInvocation || |
| + node is RedirectingConstructorInvocation || |
| + node is Annotation || |
| + node is Comment)) { |
| + return null; |
| + } |
| + assert(node is! Statement || !skipMethodBodies); |
| + return super.visitNode(node); |
| + } |
| - void accept(n) { |
| - n.accept(this); |
| - } |
| - node.directives.forEach(accept); |
| - var declarations = node.declarations; |
| - |
| - declarations |
| - .where((d) => d is TopLevelVariableDeclaration) |
| - .forEach(accept); |
| - |
| - // Visit classes before top-level methods so that we can visit static |
| - // fields first. |
| - // TODO(sigmund): consider visiting static fields only at this point |
| - // (the challenge is that to visit them we first need to create the scope |
| - // for the class here, and reuse it later when visiting the class |
| - // declaration to ensure that we correctly construct the scopes and that |
| - // we visit each static field only once). |
| - declarations.where((d) => d is ClassDeclaration).forEach(accept); |
| - |
| - declarations |
| - .where((d) => |
| - d is! TopLevelVariableDeclaration && d is! ClassDeclaration) |
| - .forEach(accept); |
| - } finally { |
| - overrideManager.exitScope(); |
| + @override |
| + Object visitMethodDeclaration(MethodDeclaration node) { |
| + if (skipMethodBodies) { |
| + node.accept(elementResolver_J2DAccessor); |
| + node.accept(typeAnalyzer_J2DAccessor); |
| + return null; |
| + } else { |
| + return super.visitMethodDeclaration(node); |
| } |
| - node.accept(elementResolver_J2DAccessor); |
| - node.accept(restrictedAnalyzer); |
| - return null; |
| } |
| @override |
| - void visitClassMembersInScope(ClassDeclaration node) { |
| - safelyVisit(node.documentationComment); |
| - node.metadata.accept(this); |
| - |
| - // This overrides the default way members are visited so that fields are |
| - // visited before method declarations. |
| - for (var n in node.members) { |
| - if (n is FieldDeclaration) n.accept(this); |
| + Object visitFunctionDeclaration(FunctionDeclaration node) { |
| + if (skipMethodBodies) { |
| + node.accept(elementResolver_J2DAccessor); |
| + node.accept(typeAnalyzer_J2DAccessor); |
| + return null; |
| + } else { |
| + return super.visitFunctionDeclaration(node); |
| } |
| - for (var n in node.members) { |
| - if (n is! FieldDeclaration) n.accept(this); |
| + } |
| + |
| + @override |
| + Object visitConstructorDeclaration(ConstructorDeclaration node) { |
| + if (skipMethodBodies) { |
| + node.accept(elementResolver_J2DAccessor); |
| + node.accept(typeAnalyzer_J2DAccessor); |
| + return null; |
| + } else { |
| + return super.visitConstructorDeclaration(node); |
| } |
| } |
| } |
| @@ -218,20 +425,12 @@ class RestrictedResolverVisitor extends ResolverVisitor { |
| /// variables. |
| class RestrictedStaticTypeAnalyzer extends StaticTypeAnalyzer { |
| final TypeProvider _typeProvider; |
| - final ResolverOptions _options; |
| - |
| - // TODO(sigmund): this needs to go away. This is currently a restriction |
| - // because we are not overriding things early enough in the analyzer. This |
| - // restriction makes it safe to run the inference later, but only on libraries |
| - // that are contained in a single file and are not part of a cycle. |
| - Map<LibraryElement, bool> _isLibraryContainedInSingleUnit = {}; |
| - RestrictedStaticTypeAnalyzer(ResolverVisitor r, this._options) |
| + RestrictedStaticTypeAnalyzer(ResolverVisitor r) |
| : _typeProvider = r.typeProvider, |
| super(r); |
| - static constructor(options) => |
| - (r) => new RestrictedStaticTypeAnalyzer(r, options); |
| + static constructor(ResolverVisitor r) => new RestrictedStaticTypeAnalyzer(r); |
| @override // to infer type from initializers |
| visitVariableDeclaration(VariableDeclaration node) { |
| @@ -247,25 +446,9 @@ class RestrictedStaticTypeAnalyzer extends StaticTypeAnalyzer { |
| var declaredType = (node.parent as VariableDeclarationList).type; |
| if (declaredType != null) return; |
| var element = node.element; |
| + if (element is! LocalVariableElement) return; |
| if (element.type != _typeProvider.dynamicType) return; |
| - // Local variables can be inferred automatically, for top-levels and fields |
| - // we rule out cases that could depend on the order in which we process |
| - // them. |
| - if (element is! LocalVariableElement) { |
| - if (_options.onlyInferConstsAndFinalFields && |
| - !element.isConst && |
| - !element.isFinal) { |
| - return; |
| - } |
| - // Only infer types if the library is not in a cycle. Otherwise we can't |
| - // guarantee that we are order independent (we can't guarantee that we'll |
| - // visit all top-level declarations in all libraries, before we visit |
| - // methods in all libraries). |
| - var thisLib = enclosingLibrary(element); |
| - if (!_canBeInferredIndependently(initializer, thisLib)) return; |
| - } |
| - |
| var type = initializer.staticType; |
| if (type == null || type == _typeProvider.bottomType) return; |
| element.type = type; |
| @@ -277,99 +460,6 @@ class RestrictedStaticTypeAnalyzer extends StaticTypeAnalyzer { |
| } |
| } |
| - /// Whether we could determine the type of an [expression] in a way |
| - /// that doesn't depend on the order in which we infer types within a |
| - /// strongest connected component of libraries. |
| - /// |
| - /// This will return true if the expression consists just of literals or |
| - /// allocations, if it only uses symbols that come from libraries that are |
| - /// clearly processed before the library where this expression occurs |
| - /// ([thisLib]), or if it's composed of these subexpressions (excluding fields |
| - /// and top-levels that could've been inferred as well). |
| - /// |
| - /// The [inFieldContext] is used internally when visiting nested expressions |
| - /// recursively. It indicates that the subexpression will be used in the |
| - /// context of a field dereference. |
| - bool _canBeInferredIndependently( |
| - Expression expression, LibraryElement thisLib, |
| - {bool inFieldContext: false}) { |
| - if (_options.inferInNonStableOrder) return true; |
| - if (!_options.inferStaticsFromIdentifiers && inFieldContext) return false; |
| - if (!_isLibraryContainedInSingleUnit[thisLib]) return false; |
| - if (expression is Literal) return true; |
| - |
| - if (expression is InstanceCreationExpression) { |
| - if (!inFieldContext) return true; |
| - var element = expression.staticElement; |
| - if (element == null) { |
| - print('Unexpected `null` element for $expression'); |
| - return false; |
| - } |
| - return !_sameConnectedComponent(thisLib, element); |
| - } |
| - if (expression is FunctionExpression) return true; |
| - if (expression is CascadeExpression) { |
| - return _canBeInferredIndependently(expression.target, thisLib, |
| - inFieldContext: inFieldContext); |
| - } |
| - |
| - if (expression is MethodInvocation) { |
| - return _canBeInferredIndependently(expression.target, thisLib, |
| - inFieldContext: true); |
| - } |
| - |
| - // Binary expressions, prefix/postfix expressions are are derived from the |
| - // type of the operand, which is known at this time even for classes in the |
| - // same library. |
| - if (expression is BinaryExpression) { |
| - return _canBeInferredIndependently(expression.leftOperand, thisLib, |
| - inFieldContext: false); |
| - } |
| - if (expression is PrefixExpression) { |
| - return _canBeInferredIndependently(expression.operand, thisLib, |
| - inFieldContext: false); |
| - } |
| - if (expression is PostfixExpression) { |
| - return _canBeInferredIndependently(expression.operand, thisLib, |
| - inFieldContext: false); |
| - } |
| - |
| - // Property accesses and prefix identifiers can be resolved as fields, in |
| - // which case, we need to choose whether or not to infer based on the |
| - // target. |
| - if (expression is PropertyAccess) { |
| - return _canBeInferredIndependently(expression.target, thisLib, |
| - inFieldContext: true); |
| - } |
| - if (expression is PrefixedIdentifier) { |
| - return _canBeInferredIndependently(expression.identifier, thisLib, |
| - inFieldContext: true); |
| - } |
| - |
| - if (expression is SimpleIdentifier) { |
| - if (!_options.inferStaticsFromIdentifiers) return false; |
| - var element = expression.bestElement; |
| - if (element == null) { |
| - print('Unexpected `null` element for $expression'); |
| - return false; |
| - } |
| - return !_sameConnectedComponent(thisLib, element); |
| - } |
| - return false; |
| - } |
| - |
| - /// Whether [dependency] is in the same strongest connected component of |
| - /// libraries as [declaration]. |
| - bool _sameConnectedComponent(LibraryElement thisLib, Element dependency) { |
| - assert(dependency != null); |
| - var otherLib = enclosingLibrary(dependency); |
| - // Note: we would check here also whether |
| - // otherLib.visibleLibraries.contains(thisLib), however because we are not |
| - // inferring type on any library that belongs to a cycle or that contains |
| - // parts, we know that this cannot be true. |
| - return thisLib == otherLib; |
| - } |
| - |
| @override // to propagate types to identifiers |
| visitMethodInvocation(MethodInvocation node) { |
| // TODO(sigmund): follow up with analyzer team - why is this needed? |
| @@ -422,55 +512,3 @@ class RestrictedStaticTypeAnalyzer extends StaticTypeAnalyzer { |
| // type in a (...) => expr or just the written type? |
| } |
| - |
| -class RestrictedTypeResolverVisitor extends TypeResolverVisitor { |
| - RestrictedTypeResolverVisitor( |
| - Library library, Source source, TypeProvider typeProvider) |
| - : super.con1(library, source, typeProvider); |
| - |
| - static TypeResolverVisitor constructor( |
| - Library library, Source source, TypeProvider typeProvider) => |
| - new RestrictedTypeResolverVisitor(library, source, typeProvider); |
| - |
| - @override |
| - Object visitVariableDeclaration(VariableDeclaration node) { |
| - var res = super.visitVariableDeclaration(node); |
| - |
| - var element = node.element; |
| - VariableDeclarationList parent = node.parent; |
| - // only infer types if it was left blank |
| - if (!element.type.isDynamic || parent.type != null) return res; |
| - |
| - // const fields and top-levels will be inferred from the initializer value |
| - // somewhere else. |
| - if (parent.isConst) return res; |
| - |
| - // If the type was omitted on a field, we can infer it from a supertype. |
| - if (node.element is FieldElement) { |
| - var getter = element.getter; |
| - var type = searchTypeFor(element.enclosingElement.type, getter); |
| - if (type != null && !type.returnType.isDynamic) { |
| - var newType = type.returnType; |
| - element.type = newType; |
| - getter.returnType = newType; |
| - if (!element.isFinal) element.setter.parameters[0].type = newType; |
| - } |
| - } |
| - return res; |
| - } |
| - |
| - @override |
| - Object visitMethodDeclaration(MethodDeclaration node) { |
| - var res = super.visitMethodDeclaration(node); |
| - var element = node.element; |
| - if ((element is MethodElement || element is PropertyAccessorElement) && |
| - element.returnType.isDynamic && |
| - node.returnType == null) { |
| - var type = searchTypeFor(element.enclosingElement.type, element); |
| - if (type != null && !type.returnType.isDynamic) { |
| - element.returnType = type.returnType; |
| - } |
| - } |
| - return res; |
| - } |
| -} |