| // Copyright (c) 2023, 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:_fe_analyzer_shared/src/util/dependency_walker.dart' as graph; |
| import 'package:analyzer/dart/element/type.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_visitor.dart'; |
| import 'package:analyzer/src/summary2/link.dart'; |
| import 'package:analyzer/src/utilities/extensions/collection.dart'; |
| |
| /// Builds extension types, in particular representation types. There might be |
| /// dependencies between them, so they all should be processed simultaneously. |
| void buildExtensionTypes(Linker linker, List<AstNode> declarations) { |
| var walker = _Walker(linker); |
| var nodes = <_Node>[]; |
| var elements = <ExtensionTypeElementImpl>[]; |
| for (var declaration in declarations) { |
| if (declaration is ExtensionTypeDeclarationImpl) { |
| var element = declaration.declaredFragment!.element; |
| var node = walker.getNode(declaration); |
| nodes.add(node); |
| elements.add(element); |
| } |
| } |
| |
| for (var node in nodes) { |
| walker.walk(node); |
| } |
| |
| _breakImplementsCycles(elements); |
| } |
| |
| /// Clears interfaces for extension types that have cycles. |
| void _breakImplementsCycles(List<ExtensionTypeElementImpl> elements) { |
| var walker = _ImplementsWalker(); |
| for (var element in elements) { |
| var node = walker.getNode(element); |
| walker.walk(node); |
| } |
| } |
| |
| /// Collector of referenced extension types in a type. |
| class _DependenciesCollector extends RecursiveTypeVisitor { |
| final List<ExtensionTypeElementImpl> dependencies = []; |
| |
| _DependenciesCollector() : super(includeTypeAliasArguments: false); |
| |
| @override |
| bool visitInterfaceType(InterfaceType type) { |
| var element = type.element3; |
| if (element is ExtensionTypeElementImpl) { |
| dependencies.add(element); |
| } |
| |
| return super.visitInterfaceType(type); |
| } |
| } |
| |
| class _ImplementsNode extends graph.Node<_ImplementsNode> { |
| final _ImplementsWalker walker; |
| final ExtensionTypeElementImpl element; |
| |
| @override |
| bool isEvaluated = false; |
| |
| _ImplementsNode(this.walker, this.element); |
| |
| @override |
| List<_ImplementsNode> computeDependencies() { |
| return element.interfaces |
| .map((interface) => interface.element3) |
| .whereType<ExtensionTypeElementImpl>() |
| .map(walker.getNode) |
| .toList(); |
| } |
| |
| void _evaluate() { |
| isEvaluated = true; |
| } |
| |
| void _markCircular() { |
| isEvaluated = true; |
| element.hasImplementsSelfReference = true; |
| |
| var representationType = element.firstFragment.representation.type; |
| var typeSystem = element.library2.typeSystem; |
| |
| var superInterface = |
| typeSystem.isNonNullable(representationType) |
| ? typeSystem.objectNone |
| : typeSystem.objectQuestion; |
| element.firstFragment.interfaces = [superInterface]; |
| } |
| } |
| |
| class _ImplementsWalker extends graph.DependencyWalker<_ImplementsNode> { |
| final Map<ExtensionTypeElementImpl, _ImplementsNode> nodeMap = |
| Map.identity(); |
| |
| @override |
| void evaluate(_ImplementsNode v) { |
| v._evaluate(); |
| } |
| |
| @override |
| void evaluateScc(List<_ImplementsNode> scc) { |
| for (var node in scc) { |
| node._markCircular(); |
| } |
| } |
| |
| _ImplementsNode getNode(ExtensionTypeElementImpl element) { |
| return nodeMap[element] ??= _ImplementsNode(this, element); |
| } |
| } |
| |
| class _Node extends graph.Node<_Node> { |
| final _Walker walker; |
| final ExtensionTypeDeclarationImpl node; |
| final ExtensionTypeElementImpl element; |
| |
| @override |
| bool isEvaluated = false; |
| |
| _Node(this.walker, this.node, this.element); |
| |
| @override |
| List<_Node> computeDependencies() { |
| var type = node.representation.fieldType.typeOrThrow; |
| var visitor = _DependenciesCollector(); |
| type.accept(visitor); |
| |
| var dependencies = <_Node>[]; |
| for (var element in visitor.dependencies) { |
| var declaration = walker.linker.getLinkingNode2(element.firstFragment); |
| if (declaration is ExtensionTypeDeclarationImpl) { |
| var node = walker.getNode(declaration); |
| dependencies.add(node); |
| } |
| } |
| |
| return dependencies; |
| } |
| |
| void _evaluate() { |
| var type = node.representation.fieldType.typeOrThrow; |
| _evaluateWithType(type); |
| } |
| |
| void _evaluateWithType(TypeImpl type) { |
| var typeSystem = element.library2.typeSystem; |
| |
| var representation = element.firstFragment.representation; |
| representation.type = type; |
| // TODO(scheglov): we repeat similar code in many places |
| representation.element.getter2!.returnType = type; |
| representation.element.getter2!.firstFragment.returnType = type; |
| |
| element.firstFragment.typeErasure = type.extensionTypeErasure; |
| element.firstFragment.interfaces = |
| element.interfaces |
| .whereType<InterfaceType>() |
| .where(typeSystem.isValidExtensionTypeSuperinterface) |
| .toFixedList(); |
| |
| var primaryConstructor = element.constructors.first; |
| var primaryFormalParameter = primaryConstructor.formalParameters.first; |
| primaryFormalParameter as FieldFormalParameterElementImpl; |
| primaryFormalParameter.type = type; |
| isEvaluated = true; |
| } |
| |
| void _markCircular() { |
| element.hasRepresentationSelfReference = true; |
| _evaluateWithType(InvalidTypeImpl.instance); |
| } |
| } |
| |
| class _Walker extends graph.DependencyWalker<_Node> { |
| final Linker linker; |
| final Map<ExtensionTypeElementImpl, _Node> nodeMap = Map.identity(); |
| |
| _Walker(this.linker); |
| |
| @override |
| void evaluate(_Node v) { |
| v._evaluate(); |
| } |
| |
| @override |
| void evaluateScc(List<_Node> scc) { |
| for (var node in scc) { |
| node._markCircular(); |
| } |
| } |
| |
| _Node getNode(ExtensionTypeDeclarationImpl node) { |
| var element = node.declaredFragment!.element; |
| return nodeMap[element] ??= _Node(this, node, element); |
| } |
| } |