diff --git a/lib/src/element_type.dart b/lib/src/element_type.dart index e03e188d26..453f1ab9e8 100644 --- a/lib/src/element_type.dart +++ b/lib/src/element_type.dart @@ -5,23 +5,31 @@ /// The models used to represent Dart code. library dartdoc.element_type; +import 'dart:collection'; + import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/dart/element/type.dart'; import 'package:dartdoc/src/model.dart'; +import 'package:dartdoc/src/model_utils.dart'; /// Base class representing a type in Dartdoc. It wraps a [DartType], and /// may link to a [ModelElement]. abstract class ElementType extends Privacy { final DartType _type; final PackageGraph packageGraph; - final DefinedElementType returnedFrom; + final ElementType returnedFrom; + final Library library; - ElementType(this._type, this.packageGraph, this.returnedFrom); + ElementType(this._type, this.library, this.packageGraph, this.returnedFrom); - factory ElementType.from(DartType f, PackageGraph packageGraph, + factory ElementType.from( + DartType f, Library library, PackageGraph packageGraph, [ElementType returnedFrom]) { if (f.element == null || f.element.kind == ElementKind.DYNAMIC) { - return UndefinedElementType(f, packageGraph, returnedFrom); + if (f is FunctionType) { + return FunctionTypeElementType(f, library, packageGraph, returnedFrom); + } + return UndefinedElementType(f, library, packageGraph, returnedFrom); } else { ModelElement element = ModelElement.fromElement(f.element, packageGraph); assert(f is ParameterizedType || f is TypeParameterType); @@ -32,29 +40,32 @@ abstract class ElementType extends Privacy { if (isGenericTypeAlias) { assert(element is! ModelFunctionAnonymous); return CallableGenericTypeAliasElementType( - f, packageGraph, element, returnedFrom); + f, library, packageGraph, element, returnedFrom); } else { if (element is ModelFunctionAnonymous) { return CallableAnonymousElementType( - f, packageGraph, element, returnedFrom); + f, library, packageGraph, element, returnedFrom); } else { assert(element is! ModelFunctionAnonymous); - return CallableElementType(f, packageGraph, element, returnedFrom); + return CallableElementType( + f, library, packageGraph, element, returnedFrom); } } } else if (isGenericTypeAlias) { assert(f is TypeParameterType); assert(element is! ModelFunctionAnonymous); return GenericTypeAliasElementType( - f, packageGraph, element, returnedFrom); + f, library, packageGraph, element, returnedFrom); } if (f is TypeParameterType) { assert(element is! ModelFunctionAnonymous); - return TypeParameterElementType(f, packageGraph, element, returnedFrom); + return TypeParameterElementType( + f, library, packageGraph, element, returnedFrom); } assert(f is ParameterizedType); assert(element is! ModelFunctionAnonymous); - return ParameterizedElementType(f, packageGraph, element, returnedFrom); + return ParameterizedElementType( + f, library, packageGraph, element, returnedFrom); } } @@ -83,31 +94,104 @@ abstract class ElementType extends Privacy { /// An [ElementType] that isn't pinned to an Element (or one that is, but whose /// element is irrelevant). class UndefinedElementType extends ElementType { - UndefinedElementType( - DartType f, PackageGraph packageGraph, ElementType returnedFrom) - : super(f, packageGraph, returnedFrom); + UndefinedElementType(DartType f, Library library, PackageGraph packageGraph, + ElementType returnedFrom) + : super(f, library, packageGraph, returnedFrom); + + String _linkedName; @override bool get isPublic => true; @override + String get nameWithGenerics => name; /// dynamic and void are not allowed to have parameterized types. + @override String get linkedName { if (type.isDynamic && returnedFrom != null && - returnedFrom.element.isAsynchronous) return 'Future'; + (returnedFrom is DefinedElementType && + (returnedFrom as DefinedElementType).element.isAsynchronous)) { + return 'Future'; + } return name; } @override - String get nameWithGenerics => name; + String get name => type.name ?? ''; +} + +/// A FunctionType that does not have an underpinning Element. +class FunctionTypeElementType extends UndefinedElementType { + FunctionTypeElementType(DartType f, Library library, + PackageGraph packageGraph, ElementType returnedFrom) + : super(f, library, packageGraph, returnedFrom); + + @override + List get parameters { + List params = (type as FunctionType).parameters; + return UnmodifiableListView(params + .map((p) => ModelElement.from(p, library, packageGraph) as Parameter) + .toList()); + } + + ElementType get returnType => ElementType.from( + (type as FunctionType).returnType, library, packageGraph, this); + + @override + String get linkedName { + if (_linkedName == null) { + StringBuffer buf = StringBuffer(); + buf.write('${returnType.linkedName} '); + buf.write('${nameWithGenerics}'); + buf.write(''); + buf.write('(${linkedParams(parameters)})'); + buf.write(''); + _linkedName = buf.toString(); + } + return _linkedName; + } + + @override + String createLinkedReturnTypeName() => returnType.linkedName; + + String _nameWithGenerics; + + @override + String get nameWithGenerics { + if (_nameWithGenerics == null) { + StringBuffer buf = StringBuffer(); + buf.write(name); + if ((type as FunctionType).typeFormals.isNotEmpty) { + if (!typeFormals.every((t) => t.name == 'dynamic')) { + buf.write('<'); + buf.writeAll(typeFormals.map((t) => t.name), + ', '); + buf.write('>'); + } + } + _nameWithGenerics = buf.toString(); + } + return _nameWithGenerics; + } + + List get typeFormals { + List typeFormals = (type as FunctionType).typeFormals; + return UnmodifiableListView(typeFormals + .map( + (p) => ModelElement.from(p, library, packageGraph) as TypeParameter) + .toList()); + } + + @override + String get name => 'Function'; } class ParameterizedElementType extends DefinedElementType { - ParameterizedElementType(ParameterizedType type, PackageGraph packageGraph, - ModelElement element, ElementType returnedFrom) - : super(type, packageGraph, element, returnedFrom); + ParameterizedElementType(ParameterizedType type, Library library, + PackageGraph packageGraph, ModelElement element, ElementType returnedFrom) + : super(type, library, packageGraph, element, returnedFrom); String _linkedName; @override @@ -154,9 +238,9 @@ class ParameterizedElementType extends DefinedElementType { } class TypeParameterElementType extends DefinedElementType { - TypeParameterElementType(TypeParameterType type, PackageGraph packageGraph, - ModelElement element, ElementType returnedFrom) - : super(type, packageGraph, element, returnedFrom); + TypeParameterElementType(TypeParameterType type, Library library, + PackageGraph packageGraph, ModelElement element, ElementType returnedFrom) + : super(type, library, packageGraph, element, returnedFrom); @override String get linkedName => name; @@ -175,9 +259,9 @@ class TypeParameterElementType extends DefinedElementType { abstract class DefinedElementType extends ElementType { final ModelElement _element; - DefinedElementType(DartType type, PackageGraph packageGraph, this._element, - ElementType returnedFrom) - : super(type, packageGraph, returnedFrom); + DefinedElementType(DartType type, Library library, PackageGraph packageGraph, + this._element, ElementType returnedFrom) + : super(type, library, packageGraph, returnedFrom); ModelElement get element { assert(_element != null); @@ -208,7 +292,7 @@ abstract class DefinedElementType extends ElementType { ElementType _returnType; ElementType get returnType { if (_returnType == null) { - _returnType = ElementType.from(type, packageGraph, this); + _returnType = ElementType.from(type, library, packageGraph, this); } return _returnType; } @@ -221,7 +305,7 @@ abstract class DefinedElementType extends ElementType { if (_typeArguments == null) { _typeArguments = (type as ParameterizedType) .typeArguments - .map((f) => ElementType.from(f, packageGraph)) + .map((f) => ElementType.from(f, library, packageGraph)) .toList(); } return _typeArguments; @@ -238,7 +322,8 @@ abstract class CallableElementTypeMixin implements ParameterizedElementType { @override ElementType get returnType { if (_returnType == null) { - _returnType = ElementType.from(type.returnType, packageGraph, this); + _returnType = + ElementType.from(type.returnType, library, packageGraph, this); } return _returnType; } @@ -252,19 +337,28 @@ abstract class CallableElementTypeMixin implements ParameterizedElementType { Iterable get typeArguments { if (_typeArguments == null) { Iterable dartTypeArguments; - if (type.typeFormals.isEmpty && - element is! ModelFunctionAnonymous && - returnedFrom?.element is! ModelFunctionAnonymous) { - dartTypeArguments = type.typeArguments; - } else if (returnedFrom != null && - returnedFrom.type.element is GenericFunctionTypeElement) { - _typeArguments = returnedFrom.typeArguments; + if (returnedFrom is FunctionTypeElementType) { + if (type.typeFormals.isEmpty) { + dartTypeArguments = type.typeArguments; + } else { + dartTypeArguments = type.typeFormals.map((f) => f.type); + } } else { - dartTypeArguments = type.typeFormals.map((f) => f.type); + DefinedElementType elementType = returnedFrom as DefinedElementType; + if (type.typeFormals.isEmpty && + element is! ModelFunctionAnonymous && + elementType?.element is! ModelFunctionAnonymous) { + dartTypeArguments = type.typeArguments; + } else if (returnedFrom != null && + returnedFrom.type.element is GenericFunctionTypeElement) { + _typeArguments = (returnedFrom as DefinedElementType).typeArguments; + } else { + dartTypeArguments = type.typeFormals.map((f) => f.type); + } } if (dartTypeArguments != null) { _typeArguments = dartTypeArguments - .map((f) => ElementType.from(f, packageGraph)) + .map((f) => ElementType.from(f, library, packageGraph)) .toList(); } } @@ -276,23 +370,23 @@ abstract class CallableElementTypeMixin implements ParameterizedElementType { /// function syntax. class CallableElementType extends ParameterizedElementType with CallableElementTypeMixin { - CallableElementType(FunctionType t, PackageGraph packageGraph, - ModelElement element, ElementType returnedFrom) - : super(t, packageGraph, element, returnedFrom); + CallableElementType(FunctionType t, Library library, + PackageGraph packageGraph, ModelElement element, ElementType returnedFrom) + : super(t, library, packageGraph, element, returnedFrom); @override String get linkedName { if (name != null && name.isNotEmpty) return super.linkedName; - return '${nameWithGenerics}(${element.linkedParams(showNames: false).trim()}) → ${returnType.linkedName}'; + return '${nameWithGenerics}(${linkedParams(element.parameters, showNames: false).trim()}) → ${returnType.linkedName}'; } } /// This is an anonymous function using the generic function syntax (declared /// literally with "Function"). class CallableAnonymousElementType extends CallableElementType { - CallableAnonymousElementType(FunctionType t, PackageGraph packageGraph, - ModelElement element, ElementType returnedFrom) - : super(t, packageGraph, element, returnedFrom); + CallableAnonymousElementType(FunctionType t, Library library, + PackageGraph packageGraph, ModelElement element, ElementType returnedFrom) + : super(t, library, packageGraph, element, returnedFrom); @override String get name => 'Function'; @@ -300,7 +394,7 @@ class CallableAnonymousElementType extends CallableElementType { String get linkedName { if (_linkedName == null) { _linkedName = - '${returnType.linkedName} ${super.linkedName}(${element.linkedParams()})'; + '${returnType.linkedName} ${super.linkedName}(${linkedParams(element.parameters)})'; } return _linkedName; } @@ -312,17 +406,17 @@ abstract class GenericTypeAliasElementTypeMixin {} /// A non-callable type backed by a [GenericTypeAliasElement]. class GenericTypeAliasElementType extends TypeParameterElementType with GenericTypeAliasElementTypeMixin { - GenericTypeAliasElementType(TypeParameterType t, PackageGraph packageGraph, - ModelElement element, ElementType returnedFrom) - : super(t, packageGraph, element, returnedFrom); + GenericTypeAliasElementType(TypeParameterType t, Library library, + PackageGraph packageGraph, ModelElement element, ElementType returnedFrom) + : super(t, library, packageGraph, element, returnedFrom); } /// A Callable generic type alias that may or may not have a name. class CallableGenericTypeAliasElementType extends ParameterizedElementType with CallableElementTypeMixin, GenericTypeAliasElementTypeMixin { - CallableGenericTypeAliasElementType(FunctionType t, PackageGraph packageGraph, - ModelElement element, ElementType returnedFrom) - : super(t, packageGraph, element, returnedFrom); + CallableGenericTypeAliasElementType(FunctionType t, Library library, + PackageGraph packageGraph, ModelElement element, ElementType returnedFrom) + : super(t, library, packageGraph, element, returnedFrom); ModelElement _returnElement; @override @@ -337,8 +431,8 @@ class CallableGenericTypeAliasElementType extends ParameterizedElementType @override ElementType get returnType { if (_returnType == null) { - _returnType = - ElementType.from(returnElement.modelType.type, packageGraph, this); + _returnType = ElementType.from( + returnElement.modelType.type, library, packageGraph, this); } return _returnType; } diff --git a/lib/src/model.dart b/lib/src/model.dart index af08b7b175..8caa2a66ac 100644 --- a/lib/src/model.dart +++ b/lib/src/model.dart @@ -36,7 +36,7 @@ import 'package:analyzer/src/dart/element/inheritance_manager3.dart'; import 'package:analyzer/src/dart/element/member.dart' show ExecutableMember, Member, ParameterMember; import 'package:analyzer/src/dart/sdk/sdk.dart'; -import 'package:analyzer/src/generated/engine.dart' hide AnalysisResult; +import 'package:analyzer/src/generated/engine.dart'; import 'package:analyzer/src/generated/java_io.dart'; import 'package:analyzer/src/generated/resolver.dart' show Namespace, NamespaceBuilder; @@ -54,7 +54,7 @@ import 'package:dartdoc/src/io_utils.dart'; import 'package:dartdoc/src/line_number_cache.dart'; import 'package:dartdoc/src/logging.dart'; import 'package:dartdoc/src/markdown_processor.dart' show Documentation; -import 'package:dartdoc/src/model_utils.dart'; +import 'package:dartdoc/src/model_utils.dart' as utils; import 'package:dartdoc/src/package_meta.dart' show PackageMeta, FileContents; import 'package:dartdoc/src/source_linker.dart'; import 'package:dartdoc/src/special_elements.dart'; @@ -572,7 +572,7 @@ class Mixin extends Class { _superclassConstraints = (element as ClassElement) .superclassConstraints .map( - (InterfaceType i) => ElementType.from(i, packageGraph)) + (InterfaceType i) => ElementType.from(i, library, packageGraph)) .toList(); } return _superclassConstraints; @@ -582,7 +582,7 @@ class Mixin extends Class { publicSuperclassConstraints.isNotEmpty; Iterable get publicSuperclassConstraints => - filterNonPublic(superclassConstraints); + utils.filterNonPublic(superclassConstraints); @override bool get hasModifiers => super.hasModifiers || hasPublicSuperclassConstraints; @@ -626,10 +626,11 @@ abstract class Container extends ModelElement { return _instanceMethods; } - bool get hasPublicMethods => filterNonPublic(instanceMethods).isNotEmpty; + bool get hasPublicMethods => + utils.filterNonPublic(instanceMethods).isNotEmpty; Iterable get allPublicInstanceMethods => - filterNonPublic(instanceMethods); + utils.filterNonPublic(instanceMethods); List get staticMethods { if (_staticMethods != null) return _staticMethods; @@ -640,9 +641,11 @@ abstract class Container extends ModelElement { return _staticMethods; } - bool get hasPublicStaticMethods => filterNonPublic(staticMethods).isNotEmpty; + bool get hasPublicStaticMethods => + utils.filterNonPublic(staticMethods).isNotEmpty; - Iterable get publicStaticMethods => filterNonPublic(staticMethods); + Iterable get publicStaticMethods => + utils.filterNonPublic(staticMethods); List get operators { if (_operators != null) return _operators; @@ -658,9 +661,10 @@ abstract class Container extends ModelElement { bool get hasPublicOperators => publicOperators.isNotEmpty; - Iterable get allPublicOperators => filterNonPublic(allOperators); + Iterable get allPublicOperators => + utils.filterNonPublic(allOperators); - Iterable get publicOperators => filterNonPublic(operators); + Iterable get publicOperators => utils.filterNonPublic(operators); List get _allFields => []; @@ -675,7 +679,7 @@ abstract class Container extends ModelElement { } Iterable get publicStaticProperties => - filterNonPublic(staticProperties); + utils.filterNonPublic(staticProperties); bool get hasPublicStaticProperties => publicStaticProperties.isNotEmpty; @@ -690,14 +694,14 @@ abstract class Container extends ModelElement { } Iterable get publicInstanceProperties => - filterNonPublic(instanceProperties); + utils.filterNonPublic(instanceProperties); bool get hasPublicProperties => publicInstanceProperties.isNotEmpty; Iterable get allInstanceFields => instanceProperties; Iterable get allPublicInstanceProperties => - filterNonPublic(allInstanceFields); + utils.filterNonPublic(allInstanceFields); bool isInheritingFrom(Container other) => false; @@ -709,7 +713,7 @@ abstract class Container extends ModelElement { return _constants; } - Iterable get publicConstants => filterNonPublic(constants); + Iterable get publicConstants => utils.filterNonPublic(constants); bool get hasPublicConstants => publicConstants.isNotEmpty; @@ -735,18 +739,19 @@ class Class extends Container packageGraph.specialClasses.addSpecial(this); _mixins = _cls.mixins .map((f) { - DefinedElementType t = ElementType.from(f, packageGraph); + DefinedElementType t = ElementType.from(f, library, packageGraph); return t; }) .where((mixin) => mixin != null) .toList(growable: false); if (_cls.supertype != null && _cls.supertype.element.supertype != null) { - supertype = ElementType.from(_cls.supertype, packageGraph); + supertype = ElementType.from(_cls.supertype, library, packageGraph); } _interfaces = _cls.interfaces - .map((f) => ElementType.from(f, packageGraph) as DefinedElementType) + .map((f) => + ElementType.from(f, library, packageGraph) as DefinedElementType) .toList(growable: false); } @@ -765,7 +770,7 @@ class Class extends Container @override Iterable get allPublicInstanceMethods => - filterNonPublic(allInstanceMethods); + utils.filterNonPublic(allInstanceMethods); bool get allPublicInstanceMethodsInherited => instanceMethods.every((f) => f.isInherited); @@ -884,7 +889,8 @@ class Class extends Container return _constructors; } - Iterable get publicConstructors => filterNonPublic(constructors); + Iterable get publicConstructors => + utils.filterNonPublic(constructors); /// Returns the library that encloses this element. @override @@ -945,7 +951,7 @@ class Class extends Container /// Returns all the implementors of this class. Iterable get publicImplementors { - return filterNonPublic(findCanonicalFor( + return utils.filterNonPublic(utils.findCanonicalFor( packageGraph.implementors[href] != null ? packageGraph.implementors[href] : [])); @@ -974,7 +980,8 @@ class Class extends Container return _inheritedMethods; } - Iterable get publicInheritedMethods => filterNonPublic(inheritedMethods); + Iterable get publicInheritedMethods => + utils.filterNonPublic(inheritedMethods); bool get hasPublicInheritedMethods => publicInheritedMethods.isNotEmpty; @@ -1000,7 +1007,7 @@ class Class extends Container } Iterable get publicInheritedOperators => - filterNonPublic(inheritedOperators); + utils.filterNonPublic(inheritedOperators); List get inheritedProperties { if (_inheritedProperties == null) { @@ -1011,14 +1018,14 @@ class Class extends Container } Iterable get publicInheritedProperties => - filterNonPublic(inheritedProperties); + utils.filterNonPublic(inheritedProperties); Iterable get publicInstanceMethods => instanceMethods; List get interfaces => _interfaces; Iterable get publicInterfaces => - filterNonPublic(interfaces); + utils.filterNonPublic(interfaces); bool get isAbstract => _cls.isAbstract; @@ -1047,7 +1054,8 @@ class Class extends Container List get mixins => _mixins; - Iterable get publicMixins => filterNonPublic(mixins); + Iterable get publicMixins => + utils.filterNonPublic(mixins); @override DefinedElementType get modelType => super.modelType; @@ -1093,7 +1101,7 @@ class Class extends Container parent = null; } else { parent = ElementType.from( - (parent.type as InterfaceType).superclass, packageGraph); + (parent.type as InterfaceType).superclass, library, packageGraph); } } else { parent = (parent.element as Class).supertype; @@ -1103,7 +1111,7 @@ class Class extends Container } Iterable get publicSuperChain => - filterNonPublic(superChain); + utils.filterNonPublic(superChain); Iterable get publicSuperChainReversed => publicSuperChain.toList().reversed; @@ -1286,7 +1294,8 @@ class Extension extends Container Extension( ExtensionElement element, Library library, PackageGraph packageGraph) : super(element, library, packageGraph) { - extendedType = ElementType.from(_extension.extendedType, packageGraph); + extendedType = + ElementType.from(_extension.extendedType, library, packageGraph); } @override @@ -1648,7 +1657,7 @@ class ModelNode { } String get sourceCode { - String contents = getFileContentsFor(element); + String contents = utils.getFileContentsFor(element); if (_sourceOffset != null) { // Find the start of the line, so that we can line up all the indents. int i = _sourceOffset; @@ -1665,8 +1674,8 @@ class ModelNode { String source = contents.substring(start, _sourceEnd); source = const HtmlEscape().convert(source); - source = stripIndentFromSource(source); - source = stripDartdocCommentsFromSource(source); + source = utils.stripIndentFromSource(source); + source = utils.stripDartdocCommentsFromSource(source); return source.trim(); } else { @@ -3364,9 +3373,9 @@ abstract class ModelElement extends Canonicalization } else { String docComment = documentationComment; if (docComment == null) { - _isPublic = hasPublicName(element); + _isPublic = utils.hasPublicName(element); } else { - _isPublic = hasPublicName(element) && + _isPublic = utils.hasPublicName(element) && !(docComment.contains('@nodoc') || docComment.contains('')); } @@ -3593,7 +3602,7 @@ abstract class ModelElement extends Canonicalization // Privately named elements can never have a canonical library, so // just shortcut them out. - if (!hasPublicName(element)) { + if (!utils.hasPublicName(element)) { _canonicalLibrary = null; } else if (!packageGraph.localPublicLibraries.contains(definingLibrary)) { List candidateLibraries = definingLibrary.exportedInLibraries @@ -3843,12 +3852,16 @@ abstract class ModelElement extends Canonicalization return _linkedName; } - String get linkedParamsLines => linkedParams().trim(); + String get linkedParams => utils.linkedParams(parameters); + + String get linkedParamsLines => utils.linkedParams(parameters).trim(); - String get linkedParamsNoMetadata => linkedParams(showMetadata: false); + String get linkedParamsNoMetadata => + utils.linkedParams(parameters, showMetadata: false); String get linkedParamsNoMetadataOrNames { - return linkedParams(showMetadata: false, showNames: false); + return utils.linkedParams(parameters, + showMetadata: false, showNames: false); } ElementType get modelType { @@ -3859,18 +3872,21 @@ abstract class ModelElement extends Canonicalization _originalMember is ParameterMember)) { if (_originalMember is ExecutableMember) { _modelType = ElementType.from( - (_originalMember as ExecutableMember).type, packageGraph); + (_originalMember as ExecutableMember).type, + library, + packageGraph); } else { // ParameterMember _modelType = ElementType.from( - (_originalMember as ParameterMember).type, packageGraph); + (_originalMember as ParameterMember).type, library, packageGraph); } } else if (element is ExecutableElement || element is FunctionTypedElement || element is ParameterElement || element is TypeDefiningElement || element is PropertyInducingElement) { - _modelType = ElementType.from((element as dynamic).type, packageGraph); + _modelType = + ElementType.from((element as dynamic).type, library, packageGraph); } } return _modelType; @@ -3924,8 +3940,9 @@ abstract class ModelElement extends Canonicalization recursedParameters.addAll(newParameters); newParameters.clear(); for (Parameter p in recursedParameters) { - newParameters.addAll(p.modelType.parameters - .where((p) => !recursedParameters.contains(p))); + var l = p.modelType.parameters + .where((pm) => !recursedParameters.contains(pm)); + newParameters.addAll(l); } } _allParameters = recursedParameters.toList(); @@ -4013,123 +4030,6 @@ abstract class ModelElement extends Canonicalization } } - String renderParam( - Parameter param, String suffix, bool showMetadata, bool showNames) { - StringBuffer buf = StringBuffer(); - ElementType paramModelType = param.modelType; - - buf.write(''); - if (showMetadata && param.hasAnnotations) { - param.annotations.forEach((String annotation) { - buf.write('$annotation '); - }); - } - if (param.isCovariant) { - buf.write('covariant '); - } - if (paramModelType is CallableElementTypeMixin) { - String returnTypeName; - if (paramModelType.isTypedef) { - returnTypeName = paramModelType.linkedName; - } else { - returnTypeName = paramModelType.createLinkedReturnTypeName(); - } - buf.write('${returnTypeName}'); - if (showNames) { - buf.write(' ${param.name}'); - } else if (paramModelType.isTypedef || - paramModelType is CallableAnonymousElementType) { - buf.write( - ' ${paramModelType.name}'); - } - if (!paramModelType.isTypedef) { - buf.write('('); - buf.write(paramModelType.element - .linkedParams(showNames: showNames, showMetadata: showMetadata)); - buf.write(')'); - } - } else if (param.modelType != null) { - String typeName = paramModelType.linkedName; - if (typeName.isNotEmpty) { - buf.write('$typeName'); - } - if (typeName.isNotEmpty && showNames && param.name.isNotEmpty) { - buf.write(' '); - } - if (showNames && param.name.isNotEmpty) { - buf.write('${param.name}'); - } - } - - if (param.hasDefaultValue) { - if (param.isOptionalNamed) { - buf.write(': '); - } else { - buf.write(' = '); - } - buf.write('${param.defaultValue}'); - } - buf.write('${suffix}'); - return buf.toString(); - } - - String linkedParams( - {bool showMetadata = true, - bool showNames = true, - String separator = ', '}) { - List requiredParams = - parameters.where((Parameter p) => !p.isOptional).toList(); - List positionalParams = - parameters.where((Parameter p) => p.isOptionalPositional).toList(); - List namedParams = - parameters.where((Parameter p) => p.isOptionalNamed).toList(); - - StringBuffer builder = StringBuffer(); - - // prefix - if (requiredParams.isEmpty && positionalParams.isNotEmpty) { - builder.write('['); - } else if (requiredParams.isEmpty && namedParams.isNotEmpty) { - builder.write('{'); - } - - // index over params - for (Parameter param in requiredParams) { - bool isLast = param == requiredParams.last; - String ext; - if (isLast && positionalParams.isNotEmpty) { - ext = ', ['; - } else if (isLast && namedParams.isNotEmpty) { - ext = ', {'; - } else { - ext = isLast ? '' : ', '; - } - builder.write(renderParam(param, ext, showMetadata, showNames)); - builder.write(' '); - } - for (Parameter param in positionalParams) { - bool isLast = param == positionalParams.last; - builder.write( - renderParam(param, isLast ? '' : ', ', showMetadata, showNames)); - builder.write(' '); - } - for (Parameter param in namedParams) { - bool isLast = param == namedParams.last; - builder.write( - renderParam(param, isLast ? '' : ', ', showMetadata, showNames)); - builder.write(' '); - } - - // suffix - if (namedParams.isNotEmpty) { - builder.write('}'); - } else if (positionalParams.isNotEmpty) { - builder.write(']'); - } - - return builder.toString().trim(); - } - @override String toString() => '$runtimeType $name'; @@ -4137,7 +4037,7 @@ abstract class ModelElement extends Canonicalization e ??= this; fqName ??= e.name; - if (e is! EnclosedElement) { + if (e is! EnclosedElement || e.enclosingElement == null) { return fqName; } @@ -5107,8 +5007,10 @@ class PackageGraph { void _populateModelNodeFor( Element element, Map compilationUnitMap) { - _modelNodes.putIfAbsent(element, - () => ModelNode(getAstNode(element, compilationUnitMap), element)); + _modelNodes.putIfAbsent( + element, + () => + ModelNode(utils.getAstNode(element, compilationUnitMap), element)); } ModelNode _getModelNodeFor(Element element) => _modelNodes[element]; @@ -5131,7 +5033,7 @@ class PackageGraph { assert(packageGraph.allLibrariesAdded); _findRefElementCache = Map(); for (final modelElement - in filterNonDocumented(packageGraph.allLocalModelElements)) { + in utils.filterNonDocumented(packageGraph.allLocalModelElements)) { _findRefElementCache.putIfAbsent( modelElement.fullyQualifiedNameWithoutLibrary, () => Set()); _findRefElementCache.putIfAbsent( @@ -5553,7 +5455,7 @@ class PackageGraph { Iterable get publicLibraries { if (_publicLibraries == null) { assert(allLibrariesAdded); - _publicLibraries = filterNonPublic(libraries).toList(); + _publicLibraries = utils.filterNonPublic(libraries).toList(); } return _publicLibraries; } @@ -5574,7 +5476,7 @@ class PackageGraph { Iterable get localPublicLibraries { if (_localPublicLibraries == null) { assert(allLibrariesAdded); - _localPublicLibraries = filterNonPublic(localLibraries).toList(); + _localPublicLibraries = utils.filterNonPublic(localLibraries).toList(); } return _localPublicLibraries; } @@ -5937,24 +5839,26 @@ abstract class TopLevelContainer implements Nameable { bool get hasPublicTypedefs => publicTypedefs.isNotEmpty; - Iterable get publicClasses => filterNonPublic(classes); + Iterable get publicClasses => utils.filterNonPublic(classes); - Iterable get publicExtensions => filterNonPublic(extensions); + Iterable get publicExtensions => utils.filterNonPublic(extensions); - Iterable get publicConstants => filterNonPublic(constants); + Iterable get publicConstants => + utils.filterNonPublic(constants); - Iterable get publicEnums => filterNonPublic(enums); + Iterable get publicEnums => utils.filterNonPublic(enums); - Iterable get publicExceptions => filterNonPublic(exceptions); + Iterable get publicExceptions => utils.filterNonPublic(exceptions); - Iterable get publicFunctions => filterNonPublic(functions); + Iterable get publicFunctions => + utils.filterNonPublic(functions); - Iterable get publicMixins => filterNonPublic(mixins); + Iterable get publicMixins => utils.filterNonPublic(mixins); Iterable get publicProperties => - filterNonPublic(properties); + utils.filterNonPublic(properties); - Iterable get publicTypedefs => filterNonPublic(typedefs); + Iterable get publicTypedefs => utils.filterNonPublic(typedefs); } /// A set of libraries, initialized after construction by accessing [_libraries]. @@ -5968,7 +5872,7 @@ abstract class LibraryContainer PackageGraph get packageGraph; - Iterable get publicLibraries => filterNonPublic(libraries); + Iterable get publicLibraries => utils.filterNonPublic(libraries); bool get hasPublicLibraries => publicLibraries.isNotEmpty; @@ -6554,8 +6458,9 @@ class Parameter extends ModelElement implements EnclosedElement { } @override - ModelElement get enclosingElement => - ModelElement.from(_parameter.enclosingElement, library, packageGraph); + ModelElement get enclosingElement => (_parameter.enclosingElement != null) + ? ModelElement.from(_parameter.enclosingElement, library, packageGraph) + : null; bool get hasDefaultValue { return _parameter.defaultValueCode != null && @@ -6569,21 +6474,32 @@ class Parameter extends ModelElement implements EnclosedElement { @override String get htmlId { - String enclosingName = _parameter.enclosingElement.name; - if (_parameter.enclosingElement is GenericFunctionTypeElement) { - // TODO(jcollins-g): Drop when GenericFunctionTypeElement populates name. - // Also, allowing null here is allowed as a workaround for - // dart-lang/sdk#32005. - for (Element e = _parameter.enclosingElement; - e.enclosingElement != null; - e = e.enclosingElement) { - enclosingName = e.name; - if (enclosingName != null && enclosingName.isNotEmpty) break; + if (_parameter.enclosingElement != null) { + String enclosingName = _parameter.enclosingElement.name; + if (_parameter.enclosingElement is GenericFunctionTypeElement) { + // TODO(jcollins-g): Drop when GenericFunctionTypeElement populates name. + // Also, allowing null here is allowed as a workaround for + // dart-lang/sdk#32005. + for (Element e = _parameter.enclosingElement; + e.enclosingElement != null; + e = e.enclosingElement) { + enclosingName = e.name; + if (enclosingName != null && enclosingName.isNotEmpty) break; + } } + return '${enclosingName}-param-${name}'; + } else { + return 'param-${name}'; } - return '${enclosingName}-param-${name}'; } + @override + int get hashCode => _element == null ? 0 : _element.hashCode; + + @override + bool operator ==(Object object) => + object is Parameter && (_parameter.type == object._parameter.type); + bool get isCovariant => _parameter.isCovariant; bool get isOptional => _parameter.isOptional; @@ -6780,8 +6696,9 @@ class TypeParameter extends ModelElement { : super(element, library, packageGraph, null); @override - ModelElement get enclosingElement => - ModelElement.from(element.enclosingElement, library, packageGraph); + ModelElement get enclosingElement => (element.enclosingElement != null) + ? ModelElement.from(element.enclosingElement, library, packageGraph) + : null; @override String get href { @@ -6802,7 +6719,7 @@ class TypeParameter extends ModelElement { if (_boundType == null) { var bound = _typeParameter.bound; if (bound != null) { - _boundType = ElementType.from(bound, packageGraph); + _boundType = ElementType.from(bound, library, packageGraph); } } return _boundType; @@ -6955,8 +6872,6 @@ class PackageBuilder { // TODO(jcollins-g): Make use of currently not existing API for managing // many AnalysisDrivers // TODO(jcollins-g): make use of DartProject isApi() - // TODO(keertip): Use summary 2 after fixing #2017 - AnalysisDriver.useSummary2 = false; _driver = AnalysisDriver( scheduler, log, diff --git a/lib/src/model_utils.dart b/lib/src/model_utils.dart index 42711b1fa5..18fbc358ed 100644 --- a/lib/src/model_utils.dart +++ b/lib/src/model_utils.dart @@ -9,11 +9,139 @@ import 'dart:io'; import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/element/element.dart'; +import 'package:analyzer/dart/element/type.dart'; import 'package:analyzer/src/dart/ast/utilities.dart'; import 'package:dartdoc/src/model.dart'; +import 'package:dartdoc/src/element_type.dart'; final Map _fileContents = {}; +String linkedParams(List parameters, + {bool showMetadata = true, + bool showNames = true, + String separator = ', '}) { + List requiredParams = + parameters.where((Parameter p) => !p.isOptional).toList(); + List positionalParams = + parameters.where((Parameter p) => p.isOptionalPositional).toList(); + List namedParams = + parameters.where((Parameter p) => p.isOptionalNamed).toList(); + + StringBuffer builder = StringBuffer(); + + // prefix + if (requiredParams.isEmpty && positionalParams.isNotEmpty) { + builder.write('['); + } else if (requiredParams.isEmpty && namedParams.isNotEmpty) { + builder.write('{'); + } + + // index over params + for (Parameter param in requiredParams) { + bool isLast = param == requiredParams.last; + String ext; + if (isLast && positionalParams.isNotEmpty) { + ext = ', ['; + } else if (isLast && namedParams.isNotEmpty) { + ext = ', {'; + } else { + ext = isLast ? '' : ', '; + } + builder.write(renderParam(param, ext, showMetadata, showNames)); + builder.write(' '); + } + for (Parameter param in positionalParams) { + bool isLast = param == positionalParams.last; + builder + .write(renderParam(param, isLast ? '' : ', ', showMetadata, showNames)); + builder.write(' '); + } + for (Parameter param in namedParams) { + bool isLast = param == namedParams.last; + builder + .write(renderParam(param, isLast ? '' : ', ', showMetadata, showNames)); + builder.write(' '); + } + + // suffix + if (namedParams.isNotEmpty) { + builder.write('}'); + } else if (positionalParams.isNotEmpty) { + builder.write(']'); + } + + return builder.toString().trim(); +} + +String renderParam( + Parameter param, String suffix, bool showMetadata, bool showNames) { + StringBuffer buf = StringBuffer(); + ElementType paramModelType = param.modelType; + + buf.write(''); + if (showMetadata && param.hasAnnotations) { + param.annotations.forEach((String annotation) { + buf.write('$annotation '); + }); + } + if (param.isCovariant) { + buf.write('covariant '); + } + if (paramModelType is CallableElementTypeMixin || + paramModelType.type is FunctionType) { + String returnTypeName; + if (paramModelType.isTypedef) { + returnTypeName = paramModelType.linkedName; + } else { + returnTypeName = paramModelType.createLinkedReturnTypeName(); + } + buf.write('${returnTypeName}'); + if (showNames) { + buf.write(' ${param.name}'); + } else if (paramModelType.isTypedef || + paramModelType is CallableAnonymousElementType || + paramModelType.type is FunctionType) { + buf.write(' ${paramModelType.name}'); + } + if (!paramModelType.isTypedef && paramModelType is DefinedElementType) { + buf.write('('); + buf.write(linkedParams(paramModelType.element.parameters, + showNames: showNames, showMetadata: showMetadata)); + buf.write(')'); + } + if (!paramModelType.isTypedef && paramModelType.type is FunctionType) { + buf.write('('); + buf.write(linkedParams( + (paramModelType as UndefinedElementType).parameters, + showNames: showNames, + showMetadata: showMetadata)); + buf.write(')'); + } + } else if (param.modelType != null) { + String typeName = paramModelType.linkedName; + if (typeName.isNotEmpty) { + buf.write('$typeName'); + } + if (typeName.isNotEmpty && showNames && param.name.isNotEmpty) { + buf.write(' '); + } + if (showNames && param.name.isNotEmpty) { + buf.write('${param.name}'); + } + } + + if (param.hasDefaultValue) { + if (param.isOptionalNamed) { + buf.write(': '); + } else { + buf.write(' = '); + } + buf.write('${param.defaultValue}'); + } + buf.write('${suffix}'); + return buf.toString(); +} + /// Returns the [AstNode] for a given [Element]. /// /// Uses a precomputed map of [element.source.fullName] to [CompilationUnit] diff --git a/pubspec.yaml b/pubspec.yaml index 01b2e1ea13..c87b6cd4ba 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -8,7 +8,7 @@ environment: sdk: '>=2.1.0 <3.0.0' dependencies: - analyzer: ^0.38.2 + analyzer: ^0.38.3 args: '>=1.5.0 <2.0.0' collection: ^1.2.0 crypto: ^2.0.6 diff --git a/test/model_test.dart b/test/model_test.dart index dbc6257c46..4398476726 100644 --- a/test/model_test.dart +++ b/test/model_test.dart @@ -8,6 +8,7 @@ import 'dart:io'; import 'package:dartdoc/dartdoc.dart'; import 'package:dartdoc/src/model.dart'; +import 'package:dartdoc/src/model_utils.dart'; import 'package:dartdoc/src/special_elements.dart'; import 'package:dartdoc/src/warnings.dart'; import 'package:test/test.dart'; @@ -2240,6 +2241,7 @@ void main() { ModelFunction thisIsFutureOrT; ModelFunction topLevelFunction; ModelFunction typeParamOfFutureOr; + ModelFunction doAComplicatedThing; setUpAll(() { f1 = exLibrary.functions.first; @@ -2259,6 +2261,8 @@ void main() { fakeLibrary.functions.firstWhere((f) => f.name == 'topLevelFunction'); typeParamOfFutureOr = fakeLibrary.functions .firstWhere((f) => f.name == 'typeParamOfFutureOr'); + doAComplicatedThing = fakeLibrary.functions + .firstWhere((f) => f.name == 'doAComplicatedThing'); }); test('has a fully qualified name', () { @@ -2286,7 +2290,7 @@ void main() { }); test('handles dynamic parameters correctly', () { - expect(f1.linkedParams(), contains('lastParam')); + expect(linkedParams(f1.parameters), contains('lastParam')); }); test('async function', () { @@ -2325,7 +2329,7 @@ void main() { test('function with a parameter having type FutureOr', () { expect( - paramOfFutureOrNull.linkedParams(), + linkedParams(paramOfFutureOrNull.parameters), equals( 'FutureOr<Null> future')); }); @@ -2353,7 +2357,7 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, test('typedef params have proper signature', () { ModelFunction function = fakeLibrary.functions.firstWhere((f) => f.name == 'addCallback'); - String params = function.linkedParams(); + String params = linkedParams(function.parameters); expect( params, '' @@ -2362,7 +2366,7 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, function = fakeLibrary.functions.firstWhere((f) => f.name == 'addCallback2'); - params = function.linkedParams(); + params = linkedParams(function.parameters); expect( params, '' @@ -2374,6 +2378,12 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, expect(genericFunction.nameWithGenerics, 'genericFunction<T>'); }); + + test('can resolve functions as parameters', () { + String params = linkedParams(doAComplicatedThing.parameters); + expect(params, + 'int x, { void doSomething(int aThingParameter, String anotherThing), void doSomethingElse(int aThingParameter, double somethingElse) }'); + }); }); group('Type expansion', () { @@ -2392,7 +2402,7 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, // TODO(jcollins-g): really, these shouldn't be called "parameters" in // the span class. expect(explicitSetter.linkedReturnType, - 'dynamic Function(int, Cool, List<int>)'); + 'dynamic Function(int, Cool, List<int>)'); }); test('parameterized type from field is correctly displayed', () { @@ -2488,7 +2498,7 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, .singleWhere((m) => m.name == 'operator +'); expect(aInheritedAdditionOperator.linkedReturnType, 'ParameterizedClass<List<int>>'); - expect(aInheritedAdditionOperator.linkedParams(), + expect(linkedParams(aInheritedAdditionOperator.parameters), 'ParameterizedClass<List<int>> other'); }); @@ -2548,7 +2558,7 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, expect( getAFunctionReturningVoid.linkedReturnType, equals( - 'void Function(T1, T2)')); + 'void Function(T1, T2)')); }); test( @@ -2557,7 +2567,7 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, expect( getAFunctionReturningBool.linkedReturnType, equals( - 'bool Function<T4>(String, T1, T4)')); + 'bool Function<T4>(String, T1, T4)')); }); test('has a fully qualified name', () { @@ -3112,19 +3122,18 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, test( 'Verify that a map containing anonymous functions as values works correctly', () { - Iterable typeArguments = + Iterable typeArguments = (importantComputations.modelType.returnType as DefinedElementType) - .typeArguments - .cast(); + .typeArguments; expect(typeArguments, isNotEmpty); expect( typeArguments.last.linkedName, equals( - '(List<num>) → dynamic')); + 'dynamic Function(List<num> a)')); expect( importantComputations.linkedReturnType, equals( - 'Map<int, (List<num>) → dynamic>')); + 'Map<int, dynamic Function(List<num> a)>')); }); test( @@ -3133,7 +3142,7 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, expect( complicatedReturn.linkedReturnType, equals( - 'ATypeTakingClass<String Function(int)>')); + 'ATypeTakingClass<String Function(int)>')); }); test('@nodoc on simple property works', () { @@ -3395,7 +3404,8 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, test('a function requiring a Future parameter', () { expect( - aVoidParameter.linkedParams(showMetadata: true, showNames: true), + linkedParams(aVoidParameter.parameters, + showMetadata: true, showNames: true), equals( 'Future<void> p1')); }); @@ -3477,7 +3487,7 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, () { Constructor theConstructor = TypedefUsingClass.constructors.first; expect( - theConstructor.linkedParams(), + linkedParams(theConstructor.parameters), equals( 'ParameterizedTypedef<double> x')); }); @@ -3496,7 +3506,7 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, expect( aComplexTypedef.linkedReturnType, equals( - 'void Function(A1, A2, A3)')); + 'void Function(A1, A2, A3)')); expect( aComplexTypedef.linkedParamsLines, equals( @@ -3620,21 +3630,21 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, }); test('param with generics', () { - var params = methodWithGenericParam.linkedParams(); + var params = linkedParams(methodWithGenericParam.parameters); expect(params.contains('List') && params.contains('Apple'), isTrue); }); test('commas on same param line', () { ModelFunction method = fakeLibrary.functions.firstWhere((f) => f.name == 'paintImage1'); - String params = method.linkedParams(); + String params = linkedParams(method.parameters); expect(params, contains(', ')); }); test('param with annotations', () { ModelFunction method = fakeLibrary.functions.firstWhere((f) => f.name == 'paintImage1'); - String params = method.linkedParams(); + String params = linkedParams(method.parameters); expect(params, contains('@required')); }); @@ -3645,7 +3655,7 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans, }); test('typedef param is linked and does not include types', () { - var params = methodWithTypedefParam.linkedParams(); + var params = linkedParams(methodWithTypedefParam.parameters); expect( params, equals(