Skip to content

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

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

Merged
merged 1 commit into from
Jun 25, 2024
Merged
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
@@ -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,
22 changes: 19 additions & 3 deletions lib/src/model/constructor.dart
Original file line number Diff line number Diff line change
@@ -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<TypeParameter> 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
2 changes: 1 addition & 1 deletion lib/src/model/inheriting_container.dart
Original file line number Diff line number Diff line change
@@ -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);
}
}
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>');
}
}