blob: 4e2e00b99f91c3b93598f7c7718d972360f558aa [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.
library computer.overrides;
import 'package:analysis_server/src/collections.dart';
import 'package:analysis_server/src/protocol_server.dart';
import 'package:analyzer/dart/element/element.dart' as engine;
import 'package:analyzer/dart/element/type.dart' as engine;
import 'package:analyzer/src/generated/ast.dart';
/**
* A computer for class member overrides in a Dart [CompilationUnit].
*/
class DartUnitOverridesComputer {
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
];
final CompilationUnit _unit;
final List<Override> _overrides = <Override>[];
engine.ClassElement _currentClass;
DartUnitOverridesComputer(this._unit);
/**
* Returns the computed occurrences, not `null`.
*/
List<Override> compute() {
for (CompilationUnitMember unitMember in _unit.declarations) {
if (unitMember is ClassDeclaration) {
_currentClass = unitMember.element;
for (ClassMember classMember in unitMember.members) {
if (classMember is MethodDeclaration) {
if (classMember.isStatic) {
continue;
}
SimpleIdentifier nameNode = classMember.name;
List<ElementKind> kinds;
if (classMember.isGetter) {
kinds = GETTER_KINDS;
} else if (classMember.isSetter) {
kinds = SETTER_KINDS;
} else {
kinds = METHOD_KINDS;
}
_addOverride(
nameNode.offset, nameNode.length, nameNode.name, kinds);
}
if (classMember is FieldDeclaration) {
if (classMember.isStatic) {
continue;
}
List<VariableDeclaration> fields = classMember.fields.variables;
for (VariableDeclaration field in fields) {
SimpleIdentifier nameNode = field.name;
_addOverride(
nameNode.offset, nameNode.length, nameNode.name, FIELD_KINDS);
}
}
}
}
}
return _overrides;
}
void _addInterfaceOverrides(
Set<engine.Element> elements,
String name,
List<ElementKind> kinds,
engine.InterfaceType type,
bool checkType,
Set<engine.InterfaceType> visited) {
if (type == null) {
return;
}
if (!visited.add(type)) {
return;
}
// check type
if (checkType) {
engine.Element element = _lookupMember(type.element, name, kinds);
if (element != null) {
elements.add(element);
return;
}
}
// check interfaces
for (engine.InterfaceType interfaceType in type.interfaces) {
_addInterfaceOverrides(
elements, name, kinds, interfaceType, true, visited);
}
// check super
_addInterfaceOverrides(
elements, name, kinds, type.superclass, checkType, visited);
}
void _addOverride(
int offset, int length, String name, List<ElementKind> kinds) {
// super
engine.Element superEngineElement;
{
engine.InterfaceType superType = _currentClass.supertype;
if (superType != null) {
superEngineElement = _lookupMember(superType.element, name, kinds);
}
}
// interfaces
Set<engine.Element> interfaceEngineElements = new Set<engine.Element>();
_addInterfaceOverrides(interfaceEngineElements, name, kinds,
_currentClass.type, false, new Set<engine.InterfaceType>());
interfaceEngineElements.remove(superEngineElement);
// is there any override?
if (superEngineElement != null || interfaceEngineElements.isNotEmpty) {
OverriddenMember superMember = superEngineElement != null
? newOverriddenMember_fromEngine(superEngineElement)
: null;
List<OverriddenMember> interfaceMembers = interfaceEngineElements
.map((member) => newOverriddenMember_fromEngine(member))
.toList();
_overrides.add(new Override(offset, length,
superclassMember: superMember,
interfaceMembers: nullIfEmpty(interfaceMembers)));
}
}
static engine.Element _lookupMember(
engine.ClassElement classElement, String name, List<ElementKind> kinds) {
if (classElement == null) {
return null;
}
engine.LibraryElement library = classElement.library;
engine.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;
}
}