| // Copyright (c) 2025, 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/src/dart/ast/ast.dart'; | |
| import 'package:analyzer/src/dart/element/element.dart'; | |
| import 'package:analyzer/src/summary2/link.dart'; | |
| import 'package:analyzer/src/utilities/extensions/object.dart'; | |
| /// Clears interfaces for declarations that have cycles. | |
| void breakInterfaceCycles(Linker linker, List<AstNode> declarations) { | |
| var walker = _ImplementsWalker(); | |
| var elements = <InterfaceElementImpl>[]; | |
| for (var declaration in declarations) { | |
| var element = declaration | |
| .tryCast<FragmentDeclaringNode>() | |
| ?.declaredFragment | |
| ?.element; | |
| if (element is InterfaceElementImpl) { | |
| elements.add(element); | |
| } | |
| } | |
| for (var element in elements) { | |
| var node = walker.getNode(element); | |
| walker.walk(node); | |
| } | |
| // Class hierarchies might have been computed before breaking cycles, e.g. | |
| // during mixin inference, so they include the removed supertypes. | |
| if (elements.any((element) => element.interfaceCycle != null)) { | |
| for (var element in elements) { | |
| element.library.session.classHierarchy.remove(element); | |
| } | |
| } | |
| } | |
| class _ImplementsNode extends graph.Node<_ImplementsNode> { | |
| final _ImplementsWalker walker; | |
| final InterfaceElementImpl element; | |
| @override | |
| bool isEvaluated = false; | |
| _ImplementsNode(this.walker, this.element); | |
| @override | |
| List<_ImplementsNode> computeDependencies() { | |
| return [ | |
| element.supertype, | |
| ...element.mixins, | |
| ...element.interfaces, | |
| if (element case MixinElementImpl element) | |
| ...element.superclassConstraints, | |
| ].nonNulls | |
| .map((interface) => interface.element) | |
| .map(walker.getNode) | |
| .toList(); | |
| } | |
| void _evaluate() { | |
| isEvaluated = true; | |
| } | |
| void _markCircular(List<InterfaceElementImpl> elements) { | |
| isEvaluated = true; | |
| element.interfaceCycle = elements; | |
| var typeProvider = element.library.typeProvider; | |
| var typeSystem = element.library.typeSystem; | |
| switch (element) { | |
| case ClassElementImpl element: | |
| element.supertype = typeProvider.objectType; | |
| element.mixins = []; | |
| element.interfaces = []; | |
| case EnumElementImpl element: | |
| element.mixins = []; | |
| element.interfaces = []; | |
| case ExtensionTypeElementImpl element: | |
| element.hasImplementsSelfReference = true; | |
| var representationType = element.representation.type; | |
| var superInterface = typeSystem.isNonNullable(representationType) | |
| ? typeSystem.objectNone | |
| : typeSystem.objectQuestion; | |
| element.interfaces = [superInterface]; | |
| case MixinElementImpl element: | |
| element.superclassConstraints = [typeProvider.objectType]; | |
| element.interfaces = []; | |
| } | |
| } | |
| } | |
| class _ImplementsWalker extends graph.DependencyWalker<_ImplementsNode> { | |
| final Map<InterfaceElementImpl, _ImplementsNode> nodeMap = Map.identity(); | |
| @override | |
| void evaluate(_ImplementsNode v) { | |
| v._evaluate(); | |
| } | |
| @override | |
| void evaluateScc(List<_ImplementsNode> scc) { | |
| var elements = scc.map((node) => node.element).toList(); | |
| for (var node in scc) { | |
| node._markCircular(elements); | |
| } | |
| } | |
| _ImplementsNode getNode(InterfaceElementImpl element) { | |
| return nodeMap[element] ??= _ImplementsNode(this, element); | |
| } | |
| } |