Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hide constructors that cannot be called or referenced by user code #3796

Merged
merged 1 commit into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions lib/src/generator/templates.runtime_renderers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2187,19 +2187,19 @@ class _Renderer_Constructor extends RendererBase<Constructor> {
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<CT_> self,
List<String> 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<CT_> self,
List<String> remainingNames) =>
self.renderSimpleVariable(c, remainingNames, 'bool'),
getBool: (CT_ c) => c.isFactory,
getBool: (CT_ c) => c.isPublic,
),
'isUnnamedConstructor': Property(
getValue: (CT_ c) => c.isUnnamedConstructor,
Expand Down
22 changes: 19 additions & 3 deletions lib/src/model/constructor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<TypeParameter> get typeParameters =>
(enclosingElement as Constructable).typeParameters;
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion lib/src/model/inheriting_container.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Expand Down
148 changes: 148 additions & 0 deletions test/constructors_test.dart
Original file line number Diff line number Diff line change
@@ -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, '<p>Constructor.</p>');
}

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, '<p>Constructor.</p>');
}

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, '<p>Constructor.</p>');
}

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, '<p>Constructor.</p>');
}

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, '<p>Constructor.</p>');
}

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, '<p>Constructor.</p>');
}

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, '<p>Constructor.</p>');
}

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, '<p>Constructor.</p>');
}

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, '<p>Constructor.</p>');
}
}