| // Copyright (c) 2014, 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:analysis_server/src/collections.dart'; | |
| import 'package:analysis_server/src/protocol_server.dart' as proto; | |
| import 'package:analyzer/dart/ast/ast.dart'; | |
| import 'package:analyzer/dart/ast/token.dart'; | |
| import 'package:analyzer/dart/element/element.dart'; | |
| import 'package:collection/collection.dart'; | |
| /// Return the elements that the given [element] overrides. | |
| OverriddenElements findOverriddenElements(Element element) { | |
| if (element.enclosingElement is InterfaceElement) { | |
| return _OverriddenElementsFinder(element).find(); | |
| } | |
| return OverriddenElements(element, <Element>[], <Element>[]); | |
| } | |
| /// A computer for class member overrides in a Dart [CompilationUnit]. | |
| class DartUnitOverridesComputer { | |
| final CompilationUnit _unit; | |
| final List<proto.Override> _overrides = <proto.Override>[]; | |
| new(this._unit); | |
| /// Returns the computed occurrences, not `null`. | |
| List<proto.Override> compute() { | |
| for (var unitMember in _unit.declarations) { | |
| switch (unitMember) { | |
| case ClassDeclaration(:var namePart, :var body): | |
| _primaryConstructor(namePart); | |
| _classMembers(body.members); | |
| case EnumDeclaration(:var namePart, :var body): | |
| _primaryConstructor(namePart); | |
| _classMembers(body.members); | |
| case ExtensionTypeDeclaration(:var namePart, :var body): | |
| _primaryConstructor(namePart); | |
| _classMembers(body.members); | |
| case MixinDeclaration(:var body): | |
| _classMembers(body.members); | |
| } | |
| } | |
| return _overrides; | |
| } | |
| /// Add a new [proto.Override] for the declaration with the given name | |
| /// [token]. | |
| void _addOverride(Token token, Element? element) { | |
| if (element != null) { | |
| var overridesResult = _OverriddenElementsFinder(element).find(); | |
| var superElements = overridesResult.superElements; | |
| var interfaceElements = overridesResult.interfaceElements; | |
| if (superElements.isNotEmpty || interfaceElements.isNotEmpty) { | |
| var superMember = superElements.isNotEmpty | |
| ? proto.newOverriddenMember_fromEngine( | |
| superElements.first.nonSynthetic, | |
| ) | |
| : null; | |
| var interfaceMembers = interfaceElements | |
| .map( | |
| (member) => | |
| proto.newOverriddenMember_fromEngine(member.nonSynthetic), | |
| ) | |
| .toList(); | |
| _overrides.add( | |
| proto.Override( | |
| token.offset, | |
| token.length, | |
| superclassMember: superMember, | |
| interfaceMembers: nullIfEmpty(interfaceMembers), | |
| ), | |
| ); | |
| } | |
| } | |
| } | |
| void _classMembers(List<ClassMember> members) { | |
| for (var classMember in members) { | |
| if (classMember is MethodDeclaration) { | |
| if (classMember.isStatic) { | |
| continue; | |
| } | |
| _addOverride(classMember.name, classMember.declaredFragment?.element); | |
| } | |
| if (classMember is FieldDeclaration) { | |
| if (classMember.isStatic) { | |
| continue; | |
| } | |
| List<VariableDeclaration> fields = classMember.fields.variables; | |
| for (var field in fields) { | |
| _addOverride(field.name, field.declaredFragment?.element); | |
| } | |
| } | |
| } | |
| } | |
| void _primaryConstructor(ClassNamePart namePart) { | |
| if (namePart is! PrimaryConstructorDeclaration) return; | |
| for (var parameter in namePart.formalParameters.parameters) { | |
| var name = parameter.name; | |
| if (name == null) continue; | |
| var parameterElement = parameter.declaredFragment?.element; | |
| if (parameterElement case FieldFormalParameterElement( | |
| isDeclaring: true, | |
| :var field?, | |
| )) { | |
| _addOverride(name, field); | |
| } | |
| } | |
| } | |
| } | |
| /// The container with elements that a class member overrides. | |
| class OverriddenElements { | |
| /// The element that overrides other class members. | |
| final Element element; | |
| /// The elements that [element] overrides and which is defined in a class that | |
| /// is a superclass of the class that defines [element]. | |
| final List<Element> superElements; | |
| /// The elements that [element] overrides and which is defined in a class that | |
| /// which is implemented by the class that defines [element]. | |
| final List<Element> interfaceElements; | |
| new(this.element, this.superElements, this.interfaceElements); | |
| } | |
| class _OverriddenElementsFinder { | |
| Element _seed; | |
| LibraryElement _library; | |
| InterfaceElement _class; | |
| String _name; | |
| List<ElementKind> _kinds; | |
| final List<Element> _superElements = <Element>[]; | |
| final List<Element> _interfaceElements = <Element>[]; | |
| final Set<InterfaceElement> _visited = {}; | |
| factory(Element seed) { | |
| var class_ = seed.enclosingElement as InterfaceElement; | |
| var library = class_.library; | |
| var name = seed.displayName; | |
| List<ElementKind> kinds; | |
| if (seed is FieldElement) { | |
| kinds = [ElementKind.GETTER, if (!seed.isFinal) ElementKind.SETTER]; | |
| } else if (seed is MethodElement) { | |
| kinds = const [ElementKind.METHOD]; | |
| } else if (seed is GetterElement) { | |
| kinds = const [ElementKind.GETTER]; | |
| } else if (seed is SetterElement) { | |
| kinds = const [ElementKind.SETTER]; | |
| } else { | |
| kinds = const []; | |
| } | |
| return _OverriddenElementsFinder._(seed, library, class_, name, kinds); | |
| } | |
| new _(this._seed, this._library, this._class, this._name, this._kinds); | |
| /// Add the [OverriddenElements] for this element. | |
| OverriddenElements find() { | |
| _visited.clear(); | |
| _addSuperOverrides(_class, withThisType: false); | |
| _visited.clear(); | |
| _addInterfaceOverrides(_class, false); | |
| _superElements.forEach(_interfaceElements.remove); | |
| return OverriddenElements(_seed, _superElements, _interfaceElements); | |
| } | |
| void _addInterfaceOverrides(InterfaceElement? class_, bool checkType) { | |
| if (class_ == null) { | |
| return; | |
| } | |
| if (!_visited.add(class_)) { | |
| return; | |
| } | |
| // this type | |
| if (checkType) { | |
| var element = _lookupMember(class_); | |
| if (element != null && !_interfaceElements.contains(element)) { | |
| _interfaceElements.add(element); | |
| } | |
| } | |
| // interfaces | |
| for (var interfaceType in class_.interfaces) { | |
| _addInterfaceOverrides(interfaceType.element, true); | |
| } | |
| // super | |
| _addInterfaceOverrides(class_.supertype?.element, checkType); | |
| if (class_ is MixinElement) { | |
| for (var constraint in class_.superclassConstraints) { | |
| _addInterfaceOverrides(constraint.element, true); | |
| } | |
| } | |
| } | |
| void _addSuperOverrides( | |
| InterfaceElement? class_, { | |
| bool withThisType = true, | |
| }) { | |
| if (class_ == null) { | |
| return; | |
| } | |
| if (!_visited.add(class_)) { | |
| return; | |
| } | |
| if (withThisType) { | |
| var element = _lookupMember(class_); | |
| if (element != null && !_superElements.contains(element)) { | |
| _superElements.add(element); | |
| } | |
| } | |
| _addSuperOverrides(class_.supertype?.element); | |
| for (var mixin_ in class_.mixins) { | |
| _addSuperOverrides(mixin_.element); | |
| } | |
| if (class_ is MixinElement) { | |
| for (var constraint in class_.superclassConstraints) { | |
| _addSuperOverrides(constraint.element); | |
| } | |
| } | |
| } | |
| Element? _lookupMember(InterfaceElement classElement) { | |
| Element? findMatchingElement(Iterable<Element> elements) { | |
| return elements.firstWhereOrNull((Element element) { | |
| if (!identical(element.library, _library) && _name.startsWith('_')) { | |
| return false; | |
| } | |
| return element.name == _name; | |
| }); | |
| } | |
| // method | |
| if (_kinds.contains(ElementKind.METHOD)) { | |
| var member = findMatchingElement(classElement.methods); | |
| if (member != null) { | |
| return member; | |
| } | |
| } | |
| // getter | |
| if (_kinds.contains(ElementKind.GETTER)) { | |
| var member = findMatchingElement(classElement.getters); | |
| if (member != null) { | |
| return member; | |
| } | |
| } | |
| // setter | |
| if (_kinds.contains(ElementKind.SETTER)) { | |
| var member = findMatchingElement(classElement.setters); | |
| if (member != null) { | |
| return member; | |
| } | |
| } | |
| // not found | |
| return null; | |
| } | |
| } |