diff --git a/lib/src/generator/templates.runtime_renderers.dart b/lib/src/generator/templates.runtime_renderers.dart index cec79065b8..f1d7961fec 100644 --- a/lib/src/generator/templates.runtime_renderers.dart +++ b/lib/src/generator/templates.runtime_renderers.dart @@ -2187,19 +2187,19 @@ class _Renderer_Constructor extends RendererBase { self.renderSimpleVariable(c, remainingNames, 'bool'), getBool: (CT_ c) => c.isConst, ), - 'isDefaultConstructor': Property( - getValue: (CT_ c) => c.isDefaultConstructor, + 'isFactory': Property( + getValue: (CT_ c) => c.isFactory, renderVariable: (CT_ c, Property self, List remainingNames) => self.renderSimpleVariable(c, remainingNames, 'bool'), - getBool: (CT_ c) => c.isDefaultConstructor, + getBool: (CT_ c) => c.isFactory, ), - 'isFactory': Property( - getValue: (CT_ c) => c.isFactory, + 'isPublic': Property( + getValue: (CT_ c) => c.isPublic, renderVariable: (CT_ c, Property self, List remainingNames) => self.renderSimpleVariable(c, remainingNames, 'bool'), - getBool: (CT_ c) => c.isFactory, + getBool: (CT_ c) => c.isPublic, ), 'isUnnamedConstructor': Property( getValue: (CT_ c) => c.isUnnamedConstructor, diff --git a/lib/src/model/constructor.dart b/lib/src/model/constructor.dart index ecbcb99c11..331ee6482d 100644 --- a/lib/src/model/constructor.dart +++ b/lib/src/model/constructor.dart @@ -7,6 +7,7 @@ import 'package:analyzer/source/line_info.dart'; import 'package:dartdoc/src/element_type.dart'; import 'package:dartdoc/src/model/comment_referable.dart'; import 'package:dartdoc/src/model/model.dart'; +import 'package:dartdoc/src/model_utils.dart'; class Constructor extends ModelElement with ContainerMember, TypeParameters { @override @@ -25,6 +26,24 @@ class Constructor extends ModelElement with ContainerMember, TypeParameters { return super.characterLocation; } + @override + bool get isPublic { + if (!super.isPublic) return false; + if (element.hasPrivateName) return false; + var class_ = element.enclosingElement; + if (class_ is! ClassElement) return true; + if (element.isFactory) return true; + if (class_.isSealed || + (class_.isAbstract && class_.isFinal) || + (class_.isAbstract && class_.isInterface)) { + /// Sealed classes, abstract final classes, and abstract interface + /// classes, cannot be instantiated nor extended, from outside the + /// declaring library. Avoid documenting them. + return false; + } + return true; + } + @override List get typeParameters => (enclosingElement as Constructable).typeParameters; @@ -60,9 +79,6 @@ class Constructor extends ModelElement with ContainerMember, TypeParameters { bool get isUnnamedConstructor => name == enclosingElement.name; - bool get isDefaultConstructor => - isUnnamedConstructor || name == '${enclosingElement.name}.new'; - bool get isFactory => element.isFactory; @override diff --git a/lib/src/model/inheriting_container.dart b/lib/src/model/inheriting_container.dart index 708772671f..77bf68c70b 100644 --- a/lib/src/model/inheriting_container.dart +++ b/lib/src/model/inheriting_container.dart @@ -59,7 +59,7 @@ mixin Constructable implements InheritingContainer { yield MapEntry( '${constructor.enclosingElement.referenceName}.${constructor.referenceName}', constructor); - if (constructor.isDefaultConstructor) { + if (constructor.isUnnamedConstructor) { yield MapEntry('new', constructor); } } diff --git a/test/constructors_test.dart b/test/constructors_test.dart new file mode 100644 index 0000000000..664b5d8b01 --- /dev/null +++ b/test/constructors_test.dart @@ -0,0 +1,148 @@ +// Copyright (c) 2024, 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:test/test.dart'; +import 'package:test_reflective_loader/test_reflective_loader.dart'; + +import 'dartdoc_test_base.dart'; +import 'src/utils.dart'; + +void main() { + defineReflectiveSuite(() { + defineReflectiveTests(ConstructorsTest); + }); +} + +@reflectiveTest +class ConstructorsTest extends DartdocTestBase { + @override + final libraryName = 'constructors'; + + void test_classIsAbstractFinal_factory() async { + var library = await bootPackageWithLibrary(''' +abstract final class C { + /// Constructor. + factory C() => throw 'Nope'; +} +'''); + var c = library.classes.named('C').constructors.first; + expect(c.name, equals('C')); + expect(c.isPublic, isTrue); + expect(c.documentationAsHtml, '

Constructor.

'); + } + + void test_classIsAbstractFinal_unnamed() async { + var library = await bootPackageWithLibrary(''' +abstract final class C { + /// Constructor. + C(); +} +'''); + var c = library.classes.named('C').constructors.first; + expect(c.name, equals('C')); + expect(c.isPublic, isFalse); + expect(c.documentationAsHtml, '

Constructor.

'); + } + + void test_classIsAbstractInterface_unnamed() async { + var library = await bootPackageWithLibrary(''' +abstract interface class C { + /// Constructor. + C(); +} +'''); + var c = library.classes.named('C').constructors.first; + expect(c.name, equals('C')); + expect(c.isPublic, isFalse); + expect(c.documentationAsHtml, '

Constructor.

'); + } + + void test_classIsPrivate_named() async { + var library = await bootPackageWithLibrary(''' +class C { + /// Constructor. + C._(); +} +'''); + var c = library.classes.named('C').constructors.first; + expect(c.name, equals('C._')); + expect(c.isPublic, isFalse); + expect(c.documentationAsHtml, '

Constructor.

'); + } + + void test_classIsPrivate_unnamed() async { + var library = await bootPackageWithLibrary(''' +class _C { + /// Constructor. + _C(); +} +'''); + var c = library.classes.named('_C').constructors.first; + expect(c.name, equals('_C')); + expect(c.isPublic, isFalse); + expect(c.documentationAsHtml, '

Constructor.

'); + } + + void test_classIsPublic_default() async { + var library = await bootPackageWithLibrary(''' +class C {} +'''); + var c = library.classes.named('C').constructors.first; + expect(c.name, equals('C')); + expect(c.isPublic, isTrue); + expect(c.documentationAsHtml, ''); + } + + void test_classIsPublic_named() async { + var library = await bootPackageWithLibrary(''' +class C { + /// Constructor. + C.named(); +} +'''); + var c = library.classes.named('C').constructors.first; + expect(c.name, equals('C.named')); + expect(c.isPublic, isTrue); + expect(c.documentationAsHtml, '

Constructor.

'); + } + + void test_classIsPublic_unnamed() async { + var library = await bootPackageWithLibrary(''' +class C { + /// Constructor. + C(); +} +'''); + var c = library.classes.named('C').constructors.first; + expect(c.name, equals('C')); + expect(c.isPublic, isTrue); + expect(c.documentationAsHtml, '

Constructor.

'); + } + + void test_classIsPublic_unnamed_explicitNew() async { + var library = await bootPackageWithLibrary(''' +class C { + /// Constructor. + C.new(); +} +'''); + var c = library.classes.named('C').constructors.first; + expect(c.name, equals('C')); + expect(c.isPublic, isTrue); + expect(c.documentationAsHtml, '

Constructor.

'); + } + + void test_classIsSealed_unnamed() async { + var library = await bootPackageWithLibrary(''' +sealed class C { + /// Constructor. + C(); +} +'''); + var c = library.classes.named('C').constructors.first; + expect(c.name, equals('C')); + expect(c.isPublic, isFalse); + expect(c.documentationAsHtml, '

Constructor.

'); + } +}