blob: ff84265f7f26f71ede68d60fefb159c9622c014e [file] [log] [blame]
// Copyright (c) 2019, 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:analyzer/dart/element/element.dart';
import 'package:analyzer/src/dart/element/member.dart' show Member;
import 'package:dartdoc/src/model/model.dart';
import 'package:dartdoc/src/utils.dart';
import 'package:dartdoc/src/warnings.dart';
/// Getters and setters.
class Accessor extends ModelElement implements EnclosedElement {
GetterSetterCombo enclosingCombo;
Accessor(PropertyAccessorElement element, Library library,
PackageGraph packageGraph, Member originalMember)
: super(element, library, packageGraph, originalMember);
String get linkedReturnType {
assert(isGetter);
return modelType.createLinkedReturnTypeName();
}
bool get isSynthetic => element.isSynthetic;
GetterSetterCombo _definingCombo;
// The [enclosingCombo] where this element was defined.
GetterSetterCombo get definingCombo {
if (_definingCombo == null) {
var variable = (element as PropertyAccessorElement).variable;
_definingCombo = ModelElement.fromElement(variable, packageGraph);
assert(_definingCombo != null, 'Unable to find defining combo');
}
return _definingCombo;
}
String _sourceCode;
@override
String get sourceCode {
if (_sourceCode == null) {
if (isSynthetic) {
_sourceCode =
packageGraph.getModelNodeFor(definingCombo.element).sourceCode;
} else {
_sourceCode = super.sourceCode;
}
}
return _sourceCode;
}
@override
String computeDocumentationComment() {
if (isSynthetic) {
// If we're a setter, only display something if we have something different than the getter.
// TODO(jcollins-g): modify analyzer to do this itself?
if (isGetter ||
definingCombo.hasNodoc ||
(isSetter &&
definingCombo.hasGetter &&
definingCombo.getter.documentationComment !=
definingCombo.documentationComment)) {
return stripComments(definingCombo.documentationComment);
} else {
return '';
}
}
return stripComments(super.computeDocumentationComment());
}
@override
void warn(PackageWarning kind,
{String message,
Iterable<Locatable> referredFrom,
Iterable<String> extendedDebug}) {
enclosingCombo.warn(kind,
message: message,
referredFrom: referredFrom,
extendedDebug: extendedDebug);
}
@override
ModelElement get enclosingElement {
if (_accessor.enclosingElement is CompilationUnitElement) {
return packageGraph.findButDoNotCreateLibraryFor(
_accessor.enclosingElement.enclosingElement);
}
return ModelElement.from(_accessor.enclosingElement, library, packageGraph);
}
@override
String get filePath => enclosingCombo.filePath;
@override
bool get isCanonical => enclosingCombo.isCanonical;
@override
String get href {
return enclosingCombo.href;
}
bool get isGetter => _accessor.isGetter;
bool get isSetter => _accessor.isSetter;
@override
String get kind => 'accessor';
String _namePart;
@override
String get namePart {
_namePart ??= super.namePart.split('=').first;
return _namePart;
}
PropertyAccessorElement get _accessor => (element as PropertyAccessorElement);
}
/// A getter or setter that is a member of a [Container].
class ContainerAccessor extends Accessor with ContainerMember, Inheritable {
/// Factory will return an [ContainerAccessor] with isInherited = true
/// if [element] is in [inheritedAccessors].
factory ContainerAccessor.from(PropertyAccessorElement element,
Set<PropertyAccessorElement> inheritedAccessors, Class enclosingClass) {
ContainerAccessor accessor;
if (element == null) return null;
if (inheritedAccessors.contains(element)) {
accessor = ModelElement.from(
element, enclosingClass.library, enclosingClass.packageGraph,
enclosingContainer: enclosingClass);
} else {
accessor = ModelElement.from(
element, enclosingClass.library, enclosingClass.packageGraph);
}
return accessor;
}
ModelElement _enclosingElement;
bool _isInherited = false;
@override
bool get isCovariant => isSetter && parameters.first.isCovariant;
ContainerAccessor(PropertyAccessorElement element, Library library,
PackageGraph packageGraph)
: super(element, library, packageGraph, null);
ContainerAccessor.inherited(PropertyAccessorElement element, Library library,
PackageGraph packageGraph, this._enclosingElement,
{Member originalMember})
: super(element, library, packageGraph, originalMember) {
_isInherited = true;
}
@override
bool get isInherited => _isInherited;
@override
Container get enclosingElement {
_enclosingElement ??= super.enclosingElement;
return _enclosingElement;
}
bool _overriddenElementIsSet = false;
ModelElement _overriddenElement;
@override
ContainerAccessor get overriddenElement {
assert(packageGraph.allLibrariesAdded);
if (!_overriddenElementIsSet) {
_overriddenElementIsSet = true;
var parent = element.enclosingElement;
if (parent is ClassElement) {
for (var t in parent.allSupertypes) {
Element accessor =
isGetter ? t.getGetter(element.name) : t.getSetter(element.name);
if (accessor != null) {
accessor = accessor.declaration;
Class parentClass =
ModelElement.fromElement(t.element, packageGraph);
var possibleFields = <Field>[];
possibleFields.addAll(parentClass.instanceFields);
possibleFields.addAll(parentClass.staticFields);
var fieldName = accessor.name.replaceFirst('=', '');
var foundField = possibleFields.firstWhere(
(f) => f.element.name == fieldName,
orElse: () => null);
if (foundField != null) {
if (isGetter) {
_overriddenElement = foundField.getter;
} else {
_overriddenElement = foundField.setter;
}
assert(!(_overriddenElement as ContainerAccessor).isInherited);
break;
}
}
}
}
}
return _overriddenElement;
}
}