| // 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/element/scope.dart'; | |
| import 'package:analyzer/src/analysis_options/analysis_options.dart'; | |
| import 'package:analyzer/src/dart/ast/ast.dart'; | |
| import 'package:analyzer/src/dart/ast/extensions.dart'; | |
| import 'package:analyzer/src/dart/element/element.dart'; | |
| import 'package:analyzer/src/dart/element/type.dart'; | |
| import 'package:analyzer/src/dart/element/type_schema.dart'; | |
| import 'package:analyzer/src/dart/resolver/ast_resolver.dart'; | |
| import 'package:analyzer/src/error/inference_error.dart'; | |
| import 'package:analyzer/src/summary2/instance_member_inferrer.dart'; | |
| import 'package:analyzer/src/summary2/library_builder.dart'; | |
| import 'package:analyzer/src/summary2/link.dart'; | |
| import 'package:analyzer/src/utilities/extensions/object.dart'; | |
| import 'package:collection/collection.dart'; | |
| /// Resolver for typed constant top-level variables and fields initializers. | |
| /// | |
| /// Initializers of untyped variables are resolved during [TopLevelInference]. | |
| class ConstantInitializersResolver { | |
| final Linker linker; | |
| ConstantInitializersResolver(this.linker); | |
| void perform() { | |
| for (var builder in linker.builders.values) { | |
| var analysisOptions = builder.kind.file.analysisOptions; | |
| var libraryElement = builder.element; | |
| var instanceElementListList = [ | |
| libraryElement.classes, | |
| libraryElement.enums, | |
| libraryElement.extensions, | |
| libraryElement.extensionTypes, | |
| libraryElement.mixins, | |
| ]; | |
| for (var instanceElementList in instanceElementListList) { | |
| for (var instanceElement in instanceElementList) { | |
| for (var field in instanceElement.fields) { | |
| _resolveVariable(analysisOptions, field); | |
| } | |
| } | |
| } | |
| for (var variable in libraryElement.topLevelVariables) { | |
| _resolveVariable(analysisOptions, variable); | |
| } | |
| } | |
| } | |
| void _resolveVariable( | |
| AnalysisOptionsImpl analysisOptions, | |
| PropertyInducingElementImpl element, | |
| ) { | |
| if (element is FieldElementImpl && element.isEnumConstant) { | |
| return; | |
| } | |
| var constantInitializer = element.constantInitializerData; | |
| if (constantInitializer == null) { | |
| return; | |
| } | |
| var fragment = constantInitializer.fragment; | |
| var node = linker.getLinkingNode(fragment) as VariableDeclarationImpl; | |
| // TopLevelInference resolves an initializer when it infers the variable's | |
| // type from it. Only the remaining constant initializers need resolution. | |
| if (!element.isTypeInferredFromInitializer) { | |
| var astResolver = AstResolver.forLinking( | |
| inheritance: linker.inheritance, | |
| libraryFragment: fragment.libraryFragment as LibraryFragmentImpl, | |
| nameScope: node.initializerScope!, | |
| analysisOptions: analysisOptions, | |
| enclosingClassElement: null, | |
| enclosingExecutableElement: null, | |
| ); | |
| List<FormalParameterElementImpl>? inScopePrimaryConstructorParameters; | |
| if (element case FieldElementImpl field) { | |
| if (field.isInstanceField && !field.isLate) { | |
| inScopePrimaryConstructorParameters = field.enclosingElement | |
| .tryCast<InterfaceElementImpl>() | |
| ?.primaryConstructor | |
| ?.formalParameters; | |
| } | |
| } | |
| astResolver.resolveVariableInitializer( | |
| node, | |
| contextType: element.type, | |
| inScopePrimaryConstructorParameters: | |
| inScopePrimaryConstructorParameters, | |
| isThisAccessible: false, | |
| ); | |
| } | |
| // We could have rewritten the initializer. | |
| fragment.constantInitializer2 = node.initializer2; | |
| element.resetConstantInitializer(); | |
| } | |
| } | |
| class TopLevelInference { | |
| final Linker linker; | |
| TopLevelInference(this.linker); | |
| void infer() { | |
| var initializerInference = _InitializerInference(linker); | |
| initializerInference.createNodes(); | |
| _performOverrideInference(); | |
| initializerInference.perform(); | |
| } | |
| void _performOverrideInference() { | |
| var interfacesToInfer = linker.builders.values.expand((builder) { | |
| return builder.element.children.whereType<InterfaceElementImpl>(); | |
| }).toList(); | |
| var inferrer = InstanceMemberInferrer(linker.inheritance); | |
| inferrer.perform(interfacesToInfer); | |
| } | |
| } | |
| enum _InferenceStatus { notInferred, beingInferred, inferred } | |
| class _InitializerInference { | |
| final Linker _linker; | |
| final List<PropertyInducingElementImpl> _toInfer = []; | |
| final List<_PropertyInducingElementTypeInference> _inferring = []; | |
| late LibraryBuilder _libraryBuilder; | |
| _InitializerInference(this._linker); | |
| void createNodes() { | |
| for (var builder in _linker.builders.values) { | |
| _libraryBuilder = builder; | |
| var libraryElement = builder.element; | |
| var instanceElementListList = [ | |
| libraryElement.classes, | |
| libraryElement.enums, | |
| libraryElement.extensions, | |
| libraryElement.extensionTypes, | |
| libraryElement.mixins, | |
| ]; | |
| for (var instanceElementList in instanceElementListList) { | |
| for (var instanceElement in instanceElementList) { | |
| for (var field in instanceElement.fields) { | |
| _addVariableNode(field); | |
| } | |
| } | |
| } | |
| for (var variable in libraryElement.topLevelVariables) { | |
| _addVariableNode(variable); | |
| } | |
| } | |
| } | |
| /// Perform type inference for variables for which it was not done yet. | |
| void perform() { | |
| for (var element in _toInfer) { | |
| // Will perform inference, if not done yet. | |
| element.type; | |
| } | |
| } | |
| void _addVariableNode(PropertyInducingElementImpl element) { | |
| if (element.isOriginGetterSetter) { | |
| return; | |
| } | |
| if (!element.hasImplicitType) return; | |
| _toInfer.add(element); | |
| element.typeInference = _PropertyInducingElementTypeInference( | |
| _linker, | |
| _inferring, | |
| element, | |
| _libraryBuilder, | |
| ); | |
| } | |
| } | |
| class _PropertyInducingElementTypeInference | |
| implements PropertyInducingElementTypeInference { | |
| final Linker _linker; | |
| /// The stack of objects performing inference now. A new object is pushed | |
| /// when we start resolving the initializer, and popped when we are done. | |
| final List<_PropertyInducingElementTypeInference> _inferring; | |
| /// The status is used to identify a cycle, when we are asked to infer the | |
| /// type, but the status is already [_InferenceStatus.beingInferred]. | |
| _InferenceStatus _status = _InferenceStatus.notInferred; | |
| final LibraryBuilder _libraryBuilder; | |
| final PropertyInducingElementImpl _element; | |
| _PropertyInducingElementTypeInference( | |
| this._linker, | |
| this._inferring, | |
| this._element, | |
| this._libraryBuilder, | |
| ); | |
| @override | |
| ({TypeImpl type, bool isTypeInferredFromInitializer}) perform() { | |
| LibraryFragmentImpl? initializerLibraryFragment; | |
| Scope? scope; | |
| ExpressionImpl Function(AstResolver astResolver)? resolveInitializer; | |
| // Augmentations cannot change the type of the element, so only | |
| // the initializer of the first fragment can be used for type inference. | |
| var firstFragment = _element.firstFragment; | |
| var node = _linker.getLinkingNode(firstFragment); | |
| switch (node) { | |
| case VariableDeclarationImpl(): | |
| if (node.initializer2 != null) { | |
| initializerLibraryFragment = firstFragment.libraryFragment; | |
| scope = node.initializerScope!; | |
| List<FormalParameterElementImpl>? inScopePrimaryConstructorParameters; | |
| var isThisAccessible = false; | |
| if (_element case FieldElementImpl field) { | |
| if (field.isInstanceField) { | |
| if (!field.isLate) { | |
| inScopePrimaryConstructorParameters = field.enclosingElement | |
| .tryCast<InterfaceElementImpl>() | |
| ?.primaryConstructor | |
| ?.formalParameters; | |
| } else { | |
| isThisAccessible = true; | |
| } | |
| } | |
| } | |
| resolveInitializer = (astResolver) { | |
| return astResolver.resolveVariableInitializer( | |
| node, | |
| contextType: UnknownInferredType.instance, | |
| inScopePrimaryConstructorParameters: | |
| inScopePrimaryConstructorParameters, | |
| isThisAccessible: isThisAccessible, | |
| ); | |
| }; | |
| } | |
| case FormalParameterImpl(): | |
| _assertElementFieldOriginDeclaringFormalParameter(); | |
| if (node.defaultClause case var defaultClause?) { | |
| initializerLibraryFragment = firstFragment.libraryFragment; | |
| scope = node.scope!; | |
| resolveInitializer = (astResolver) { | |
| return astResolver.resolveDefaultValue( | |
| defaultClause, | |
| contextType: UnknownInferredType.instance, | |
| ); | |
| }; | |
| } else if (node is RegularFormalParameterImpl && | |
| node.functionTypedSuffix == null) { | |
| _status = _InferenceStatus.inferred; | |
| return ( | |
| type: _element.library.typeSystem.objectQuestion, | |
| isTypeInferredFromInitializer: false, | |
| ); | |
| } | |
| } | |
| if (initializerLibraryFragment == null || | |
| scope == null || | |
| resolveInitializer == null) { | |
| _status = _InferenceStatus.inferred; | |
| return ( | |
| type: DynamicTypeImpl.instance, | |
| isTypeInferredFromInitializer: false, | |
| ); | |
| } | |
| // With this status the type must be already set. | |
| // So, the element knows the type, ans should not call the inferrer. | |
| if (_status == _InferenceStatus.inferred) { | |
| assert(false, 'Should not happen: $_element'); | |
| return ( | |
| type: DynamicTypeImpl.instance, | |
| isTypeInferredFromInitializer: false, | |
| ); | |
| } | |
| // If we are already inferring this element, we found a cycle. | |
| if (_status == _InferenceStatus.beingInferred) { | |
| var startIndex = _inferring.indexOf(this); | |
| var cycle = _inferring.slice(startIndex); | |
| var inferenceError = TopLevelInferenceErrorDependencyCycle( | |
| cycle: cycle.map((e) => e._element.name ?? '').sorted(), | |
| ); | |
| for (var inference in cycle) { | |
| if (inference._status == _InferenceStatus.beingInferred) { | |
| var element = inference._element; | |
| element.typeInferenceError = inferenceError; | |
| element.type = DynamicTypeImpl.instance; | |
| inference._status = _InferenceStatus.inferred; | |
| } | |
| } | |
| return ( | |
| type: DynamicTypeImpl.instance, | |
| isTypeInferredFromInitializer: false, | |
| ); | |
| } | |
| assert(_status == _InferenceStatus.notInferred); | |
| // Push self into the stack, and mark. | |
| _inferring.add(this); | |
| _status = _InferenceStatus.beingInferred; | |
| var enclosingElement = _element.enclosingElement; | |
| var enclosingInterfaceElement = enclosingElement | |
| .tryCast<InterfaceElementImpl>(); | |
| var analysisOptions = _libraryBuilder.kind.file.analysisOptions; | |
| var astResolver = AstResolver.forLinking( | |
| inheritance: _linker.inheritance, | |
| libraryFragment: initializerLibraryFragment, | |
| nameScope: scope, | |
| analysisOptions: analysisOptions, | |
| enclosingClassElement: enclosingInterfaceElement, | |
| enclosingExecutableElement: null, | |
| ); | |
| var initializer = resolveInitializer(astResolver); | |
| // Pop self from the stack. | |
| var self = _inferring.removeLast(); | |
| assert(identical(self, this)); | |
| // We might have found a cycle, and already set the type. | |
| // Anyway, we are done. | |
| if (_status == _InferenceStatus.inferred) { | |
| return (type: _element.type, isTypeInferredFromInitializer: false); | |
| } else { | |
| _status = _InferenceStatus.inferred; | |
| } | |
| var initializerType = initializer.typeOrThrow; | |
| return ( | |
| type: _refineType(initializerType), | |
| isTypeInferredFromInitializer: true, | |
| ); | |
| } | |
| void _assertElementFieldOriginDeclaringFormalParameter() { | |
| assert(() { | |
| if (_element case FieldElementImpl element) { | |
| return element.isOriginDeclaringFormalParameter; | |
| } | |
| return false; | |
| }()); | |
| } | |
| TypeImpl _refineType(TypeImpl type) { | |
| if (type.isDartCoreNull) { | |
| // When `T` is `Null`, `p` has declared type `Object?`. | |
| if (_element case FieldElementImpl field) { | |
| if (field.declaringFormalParameter != null) { | |
| return _element.library.typeSystem.objectQuestion; | |
| } | |
| } | |
| // Logic for older language versions. | |
| return DynamicTypeImpl.instance; | |
| } | |
| return type; | |
| } | |
| } |