blob: 7eb2b6881383f18bc4a7ce69dd1d3b52a2ba82fb [file] [log] [blame]
// 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/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
/**
* Return the elements that the given [element] overrides.
*/
OverriddenElements findOverriddenElements(Element element) {
if (element?.enclosingElement is ClassElement) {
return new _OverriddenElementsFinder(element).find();
}
return new 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>[];
DartUnitOverridesComputer(this._unit);
/**
* Returns the computed occurrences, not `null`.
*/
List<proto.Override> compute() {
for (CompilationUnitMember unitMember in _unit.declarations) {
if (unitMember is ClassOrMixinDeclaration) {
for (ClassMember classMember in unitMember.members) {
if (classMember is MethodDeclaration) {
if (classMember.isStatic) {
continue;
}
_addOverride(classMember.name);
}
if (classMember is FieldDeclaration) {
if (classMember.isStatic) {
continue;
}
List<VariableDeclaration> fields = classMember.fields.variables;
for (VariableDeclaration field in fields) {
_addOverride(field.name);
}
}
}
}
}
return _overrides;
}
/**
* Add a new [Override] for the declaration with the given name [node].
*/
void _addOverride(SimpleIdentifier node) {
Element element = node.staticElement;
OverriddenElements overridesResult =
new _OverriddenElementsFinder(element).find();
List<Element> superElements = overridesResult.superElements;
List<Element> interfaceElements = overridesResult.interfaceElements;
if (superElements.isNotEmpty || interfaceElements.isNotEmpty) {
proto.OverriddenMember superMember = superElements.isNotEmpty
? proto.newOverriddenMember_fromEngine(superElements.first)
: null;
List<proto.OverriddenMember> interfaceMembers = interfaceElements
.map((member) => proto.newOverriddenMember_fromEngine(member))
.toList();
_overrides.add(new proto.Override(node.offset, node.length,
superclassMember: superMember,
interfaceMembers: nullIfEmpty(interfaceMembers)));
}
}
}
/**
* 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;
OverriddenElements(this.element, this.superElements, this.interfaceElements);
}
class _OverriddenElementsFinder {
static const List<ElementKind> FIELD_KINDS = const <ElementKind>[
ElementKind.FIELD,
ElementKind.GETTER,
ElementKind.SETTER
];
static const List<ElementKind> GETTER_KINDS = const <ElementKind>[
ElementKind.FIELD,
ElementKind.GETTER
];
static const List<ElementKind> METHOD_KINDS = const <ElementKind>[
ElementKind.METHOD
];
static const List<ElementKind> SETTER_KINDS = const <ElementKind>[
ElementKind.FIELD,
ElementKind.SETTER
];
Element _seed;
LibraryElement _library;
ClassElement _class;
String _name;
List<ElementKind> _kinds;
List<Element> _superElements = <Element>[];
List<Element> _interfaceElements = <Element>[];
Set<InterfaceType> _visited = new Set<InterfaceType>();
_OverriddenElementsFinder(Element seed) {
_seed = seed;
_class = seed.enclosingElement;
_library = _class.library;
_name = seed.displayName;
if (seed is MethodElement) {
_kinds = METHOD_KINDS;
} else if (seed is PropertyAccessorElement) {
_kinds = seed.isGetter ? GETTER_KINDS : SETTER_KINDS;
} else {
_kinds = FIELD_KINDS;
}
}
/**
* Add the [OverriddenElements] for this element.
*/
OverriddenElements find() {
_visited.clear();
_addSuperOverrides(_class.type, withThisType: false);
_visited.clear();
_addInterfaceOverrides(_class.type, false);
_superElements.forEach(_interfaceElements.remove);
return new OverriddenElements(_seed, _superElements, _interfaceElements);
}
void _addInterfaceOverrides(InterfaceType type, bool checkType) {
if (type == null) {
return;
}
if (!_visited.add(type)) {
return;
}
// this type
if (checkType) {
Element element = _lookupMember(type.element);
if (element != null && !_interfaceElements.contains(element)) {
_interfaceElements.add(element);
}
}
// interfaces
for (InterfaceType interfaceType in type.interfaces) {
_addInterfaceOverrides(interfaceType, true);
}
// super
_addInterfaceOverrides(type.superclass, checkType);
}
void _addSuperOverrides(InterfaceType type, {bool withThisType: true}) {
if (type == null) {
return;
}
if (!_visited.add(type)) {
return;
}
if (withThisType) {
Element element = _lookupMember(type.element);
if (element != null && !_superElements.contains(element)) {
_superElements.add(element);
}
}
_addSuperOverrides(type.superclass);
type.mixins.forEach(_addSuperOverrides);
type.superclassConstraints.forEach(_addSuperOverrides);
}
Element _lookupMember(ClassElement classElement) {
if (classElement == null) {
return null;
}
Element member;
// method
if (_kinds.contains(ElementKind.METHOD)) {
member = classElement.lookUpMethod(_name, _library);
if (member != null) {
return member;
}
}
// getter
if (_kinds.contains(ElementKind.GETTER)) {
member = classElement.lookUpGetter(_name, _library);
if (member != null) {
return member;
}
}
// setter
if (_kinds.contains(ElementKind.SETTER)) {
member = classElement.lookUpSetter(_name + '=', _library);
if (member != null) {
return member;
}
}
// not found
return null;
}
}