| // Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| import 'package:analyzer/dart/ast/ast.dart'; |
| import 'package:analyzer/dart/ast/visitor.dart'; |
| import 'package:analyzer/dart/element/element.dart'; |
| import 'package:analyzer/dart/element/type.dart'; |
| import 'package:analyzer/src/dart/element/builder.dart'; |
| import 'package:analyzer/src/dart/element/element.dart'; |
| import 'package:analyzer/src/dart/element/type.dart'; |
| import 'package:analyzer/src/dart/resolver/scope.dart'; |
| import 'package:analyzer/src/generated/resolver.dart'; |
| import 'package:analyzer/src/summary/format.dart'; |
| import 'package:analyzer/src/summary/idl.dart'; |
| import 'package:analyzer/src/summary/link.dart' as graph |
| show DependencyWalker, Node; |
| import 'package:analyzer/src/summary2/ast_resolver.dart'; |
| import 'package:analyzer/src/summary2/lazy_ast.dart'; |
| import 'package:analyzer/src/summary2/link.dart'; |
| import 'package:analyzer/src/summary2/linking_node_scope.dart'; |
| import 'package:analyzer/src/task/strong_mode.dart'; |
| |
| DartType _dynamicIfNull(DartType type) { |
| if (type == null || type.isBottom || type.isDartCoreNull) { |
| return DynamicTypeImpl.instance; |
| } |
| return type; |
| } |
| |
| AstNode _getLinkedNode(Element element) { |
| return (element as ElementImpl).linkedNode; |
| } |
| |
| class TopLevelInference { |
| final Linker linker; |
| |
| TopLevelInference(this.linker); |
| |
| DynamicTypeImpl get _dynamicType => DynamicTypeImpl.instance; |
| |
| void infer() { |
| var initializerInference = _InitializerInference(linker); |
| initializerInference.createNodes(); |
| |
| _performOverrideInference(); |
| initializerInference.perform(); |
| _inferConstructorFieldFormals(); |
| } |
| |
| void _inferConstructorFieldFormals() { |
| for (var builder in linker.builders.values) { |
| for (var unit in builder.element.units) { |
| for (var class_ in unit.types) { |
| var fields = <String, DartType>{}; |
| for (var field in class_.fields) { |
| if (field.isSynthetic) continue; |
| |
| var name = field.name; |
| var type = field.type; |
| if (type == null) { |
| throw StateError('Field $name should have a type.'); |
| } |
| fields[name] ??= type; |
| } |
| |
| for (var constructor in class_.constructors) { |
| for (var parameter in constructor.parameters) { |
| if (parameter is FieldFormalParameterElement) { |
| var node = _getLinkedNode(parameter); |
| if (node is DefaultFormalParameter) { |
| var defaultParameter = node as DefaultFormalParameter; |
| node = defaultParameter.parameter; |
| } |
| |
| if (node is FieldFormalParameter && |
| node.type == null && |
| node.parameters == null) { |
| var name = parameter.name; |
| var type = fields[name] ?? _dynamicType; |
| LazyAst.setType(node, type); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| void _performOverrideInference() { |
| var inferrer = new InstanceMemberInferrer( |
| linker.typeProvider, |
| linker.inheritance, |
| ); |
| for (var builder in linker.builders.values) { |
| for (var unit in builder.element.units) { |
| inferrer.inferCompilationUnit(unit); |
| } |
| } |
| } |
| } |
| |
| class _FunctionElementForLink_Initializer implements FunctionElementImpl { |
| final _InferenceNode _node; |
| |
| @override |
| Element enclosingElement; |
| |
| _FunctionElementForLink_Initializer(this._node); |
| |
| @override |
| DartType get returnType { |
| if (!_node.isEvaluated) { |
| _node._walker.walk(_node); |
| } |
| return LazyAst.getType(_node._node); |
| } |
| |
| noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); |
| } |
| |
| class _InferenceDependenciesCollector extends RecursiveAstVisitor<void> { |
| final Set<PropertyInducingElement> _set = Set.identity(); |
| |
| @override |
| void visitSimpleIdentifier(SimpleIdentifier node) { |
| var element = node.staticElement; |
| if (element is PropertyAccessorElement && element.isGetter) { |
| _set.add(element.variable); |
| } |
| } |
| } |
| |
| class _InferenceNode extends graph.Node<_InferenceNode> { |
| final _InferenceWalker _walker; |
| final LibraryElement _library; |
| final Scope _scope; |
| final VariableDeclaration _node; |
| |
| @override |
| bool isEvaluated = false; |
| |
| _InferenceNode(this._walker, this._library, this._scope, this._node); |
| |
| @override |
| List<_InferenceNode> computeDependencies() { |
| _node.initializer.accept(LocalElementBuilder(ElementHolder(), null)); |
| |
| _resolveInitializer(); |
| |
| var collector = _InferenceDependenciesCollector(); |
| _node.initializer.accept(collector); |
| |
| if (collector._set.isEmpty) { |
| return const <_InferenceNode>[]; |
| } |
| |
| return collector._set |
| .map(_walker.getNode) |
| .where((node) => node != null) |
| .toList(); |
| } |
| |
| void evaluate() { |
| _resolveInitializer(); |
| |
| if (LazyAst.getType(_node) == null) { |
| var initializerType = _node.initializer.staticType; |
| initializerType = _dynamicIfNull(initializerType); |
| LazyAst.setType(_node, initializerType); |
| } |
| |
| isEvaluated = true; |
| } |
| |
| void markCircular(List<_InferenceNode> cycle) { |
| LazyAst.setType(_node, DynamicTypeImpl.instance); |
| |
| var cycleNames = Set<String>(); |
| for (var inferenceNode in cycle) { |
| var node = inferenceNode._node; |
| if (node is VariableDeclaration) { |
| cycleNames.add(node.name.name); |
| } else { |
| cycleNames.add('<unknown>'); |
| } |
| } |
| |
| LazyAst.setTypeInferenceError( |
| _node, |
| TopLevelInferenceErrorBuilder( |
| kind: TopLevelInferenceErrorKind.dependencyCycle, |
| arguments: cycleNames.toList(), |
| ), |
| ); |
| |
| isEvaluated = true; |
| } |
| |
| void _resolveInitializer() { |
| var astResolver = AstResolver(_walker._linker, _library, _scope); |
| astResolver.resolve(_node.initializer, doAstRewrite: true); |
| } |
| } |
| |
| class _InferenceWalker extends graph.DependencyWalker<_InferenceNode> { |
| final Linker _linker; |
| final Map<Element, _InferenceNode> _nodes = Map.identity(); |
| |
| _InferenceWalker(this._linker); |
| |
| @override |
| void evaluate(_InferenceNode v) { |
| v.evaluate(); |
| } |
| |
| @override |
| void evaluateScc(List<_InferenceNode> scc) { |
| for (var node in scc) { |
| node.markCircular(scc); |
| } |
| } |
| |
| _InferenceNode getNode(Element element) { |
| return _nodes[element]; |
| } |
| |
| void walkNodes() { |
| for (var node in _nodes.values) { |
| if (!node.isEvaluated) { |
| walk(node); |
| } |
| } |
| } |
| } |
| |
| class _InitializerInference { |
| final Linker _linker; |
| final _InferenceWalker _walker; |
| |
| LibraryElement _library; |
| Scope _scope; |
| |
| _InitializerInference(this._linker) : _walker = _InferenceWalker(_linker); |
| |
| void createNodes() { |
| for (var builder in _linker.builders.values) { |
| _library = builder.element; |
| for (var unit in _library.units) { |
| unit.types.forEach(_addClassElementFields); |
| unit.mixins.forEach(_addClassElementFields); |
| |
| _scope = builder.libraryScope; |
| for (var element in unit.topLevelVariables) { |
| _addNode(element); |
| } |
| } |
| } |
| } |
| |
| void perform() { |
| createNodes(); |
| _walker.walkNodes(); |
| } |
| |
| void _addClassElementFields(ClassElement class_) { |
| var node = _getLinkedNode(class_); |
| _scope = LinkingNodeContext.get(node).scope; |
| for (var element in class_.fields) { |
| _addNode(element); |
| } |
| } |
| |
| void _addNode(PropertyInducingElement element) { |
| if (element.isSynthetic) return; |
| |
| VariableDeclaration node = _getLinkedNode(element); |
| if (LazyAst.getType(node) == null || element.isConst) { |
| if (node.initializer != null) { |
| var inferenceNode = _InferenceNode(_walker, _library, _scope, node); |
| _walker._nodes[element] = inferenceNode; |
| (element as PropertyInducingElementImpl).initializer = |
| _FunctionElementForLink_Initializer(inferenceNode); |
| } else { |
| if (LazyAst.getType(node) == null) { |
| LazyAst.setType(node, DynamicTypeImpl.instance); |
| } |
| } |
| } |
| } |
| } |