Skip to content

Commit a094d22

Browse files
authored
Support changes for induced modifiers in analyzer (#3371)
* Initial implementation * induced modifiers support * Fix up tests and clean up unused bits we might do later
1 parent 203a5e6 commit a094d22

8 files changed

+502
-376
lines changed

lib/resources/docs.dart.js

Lines changed: 336 additions & 334 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/resources/docs.dart.js.map

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/src/generator/templates.runtime_renderers.dart

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6807,6 +6807,20 @@ class _Renderer_InheritingContainer extends RendererBase<InheritingContainer> {
68076807
_render_Field(e, ast, r.template, sink, parent: r));
68086808
},
68096809
),
6810+
'containerModifiers': Property(
6811+
getValue: (CT_ c) => c.containerModifiers,
6812+
renderVariable: (CT_ c, Property<CT_> self,
6813+
List<String> remainingNames) =>
6814+
self.renderSimpleVariable(
6815+
c, remainingNames, 'List<ContainerModifier>'),
6816+
renderIterable: (CT_ c, RendererBase<CT_> r,
6817+
List<MustachioNode> ast, StringSink sink) {
6818+
return c.containerModifiers.map((e) => renderSimple(
6819+
e, ast, r.template, sink,
6820+
parent: r,
6821+
getters: _invisibleGetters['ContainerModifier']!));
6822+
},
6823+
),
68106824
'declaredFields': Property(
68116825
getValue: (CT_ c) => c.declaredFields,
68126826
renderVariable: (CT_ c, Property<CT_> self,
@@ -7092,28 +7106,6 @@ class _Renderer_InheritingContainer extends RendererBase<InheritingContainer> {
70927106
parent: r);
70937107
},
70947108
),
7095-
'modifiers': Property(
7096-
getValue: (CT_ c) => c.modifiers,
7097-
renderVariable:
7098-
(CT_ c, Property<CT_> self, List<String> remainingNames) {
7099-
if (remainingNames.isEmpty) {
7100-
return self.getValue(c).toString();
7101-
}
7102-
var name = remainingNames.first;
7103-
var nextProperty =
7104-
_Renderer_String.propertyMap().getValue(name);
7105-
return nextProperty.renderVariable(
7106-
self.getValue(c) as String,
7107-
nextProperty,
7108-
[...remainingNames.skip(1)]);
7109-
},
7110-
isNullValue: (CT_ c) => false,
7111-
renderValue: (CT_ c, RendererBase<CT_> r,
7112-
List<MustachioNode> ast, StringSink sink) {
7113-
_render_String(c.modifiers, ast, r.template, sink,
7114-
parent: r);
7115-
},
7116-
),
71177109
'publicInheritedFields': Property(
71187110
getValue: (CT_ c) => c.publicInheritedFields,
71197111
renderVariable: (CT_ c, Property<CT_> self,
@@ -15847,6 +15839,14 @@ const _invisibleGetters = {
1584715839
'returnType',
1584815840
'runtimeType'
1584915841
},
15842+
'ContainerModifier': {
15843+
'displayName',
15844+
'hashCode',
15845+
'hideIfPresent',
15846+
'name',
15847+
'order',
15848+
'runtimeType'
15849+
},
1585015850
'Context': {'current', 'hashCode', 'runtimeType', 'separator', 'style'},
1585115851
'DartType': {
1585215852
'alias',

lib/src/model/class.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ class Class extends InheritingContainer
5151
bool get isAbstract => element.isAbstract;
5252

5353
@override
54-
bool get isBase => element.isBase;
54+
bool get isBase => element.isBase && !element.isSealed;
5555

5656
bool get isErrorOrException {
5757
bool isError(InterfaceElement element) =>
@@ -64,10 +64,10 @@ class Class extends InheritingContainer
6464
}
6565

6666
@override
67-
bool get isFinal => element.isFinal;
67+
bool get isFinal => element.isFinal && !element.isSealed;
6868

6969
@override
70-
bool get isInterface => element.isInterface;
70+
bool get isInterface => element.isInterface && !element.isSealed;
7171

7272
@override
7373
bool get isMixinClass => element.isMixinClass;
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:collection/collection.dart' show IterableExtension;
6+
7+
/// Represents a single modifier applicable to containers.
8+
class ContainerModifier implements Comparable<ContainerModifier> {
9+
final String name;
10+
final String displayName;
11+
12+
/// If this modifier is present with any of these modifiers, it should
13+
/// not be displayed as part of a fullKind prefix.
14+
final Set<ContainerModifier> hideIfPresent;
15+
16+
/// The display order of this modifier.
17+
final int order;
18+
const ContainerModifier._(
19+
this.name, {
20+
required this.order,
21+
String? displayName,
22+
Set<ContainerModifier>? hideIfPresent,
23+
}) : displayName = displayName ?? name,
24+
hideIfPresent = hideIfPresent ?? const <ContainerModifier>{};
25+
26+
@override
27+
String toString() => displayName;
28+
29+
@override
30+
int compareTo(ContainerModifier a) => order.compareTo(a.order);
31+
32+
static const ContainerModifier sealed =
33+
ContainerModifier._('sealed', order: 0);
34+
static const ContainerModifier abstract =
35+
ContainerModifier._('abstract', order: 0, hideIfPresent: {sealed});
36+
static const ContainerModifier base = ContainerModifier._('base', order: 1);
37+
static const ContainerModifier interface =
38+
ContainerModifier._('interface', order: 2);
39+
static const ContainerModifier finalModifier =
40+
ContainerModifier._('final', order: 3);
41+
static const ContainerModifier mixin = ContainerModifier._('mixin', order: 4);
42+
}
43+
44+
extension ContainerModifiers on Iterable<ContainerModifier> {
45+
/// Returns a string suitable for prefixing the class name in Dartdoc
46+
/// title bars based on given modifiers.
47+
String modifiersAsFullKindPrefix() => sorted((a, b) => a.compareTo(b))
48+
.where((m) => !m.hideIfPresent.any(contains))
49+
.join(' ');
50+
}

lib/src/model/inheriting_container.dart

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import 'package:analyzer/dart/element/type.dart';
77
import 'package:collection/collection.dart' show IterableExtension;
88
import 'package:dartdoc/src/element_type.dart';
99
import 'package:dartdoc/src/model/comment_referable.dart';
10+
import 'package:dartdoc/src/model/container_modifiers.dart';
1011
import 'package:dartdoc/src/model/extension_target.dart';
1112
import 'package:dartdoc/src/model/model.dart';
1213
import 'package:dartdoc/src/model_utils.dart' as model_utils;
@@ -86,16 +87,14 @@ abstract class InheritingContainer extends Container
8687
/// Class modifiers from the Dart feature specification.
8788
///
8889
/// These apply to or have some meaning for [Class]es and [Mixin]s.
89-
late String modifiers = [
90-
if (isSealed) 'sealed' else if (isAbstract) 'abstract',
91-
if (isBase)
92-
'base'
93-
else if (isInterface)
94-
'interface'
95-
else if (isFinal)
96-
'final',
97-
if (isMixinClass) 'mixin',
98-
].join(' ');
90+
late List<ContainerModifier> containerModifiers = [
91+
if (isAbstract) ContainerModifier.abstract,
92+
if (isSealed) ContainerModifier.sealed,
93+
if (isBase) ContainerModifier.base,
94+
if (isInterface) ContainerModifier.interface,
95+
if (isFinal) ContainerModifier.finalModifier,
96+
if (isMixinClass) ContainerModifier.mixin,
97+
];
9998

10099
late final List<ModelElement> _allModelElements = [
101100
...super.allModelElements,
@@ -269,8 +268,8 @@ abstract class InheritingContainer extends Container
269268
Library get enclosingElement => library;
270269

271270
String get fullkind {
272-
if (modifiers.isNotEmpty) {
273-
return '$modifiers $kind';
271+
if (containerModifiers.isNotEmpty) {
272+
return '${containerModifiers.modifiersAsFullKindPrefix()} $kind';
274273
}
275274
return kind;
276275
}

test/class_modifiers_test.dart

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,50 @@ base mixin O {}
7979
expect(Nmixin.fullkind, equals('mixin'));
8080
expect(Omixin.fullkind, equals('base mixin'));
8181
}
82+
83+
void test_abstractSealed() async {
84+
var library = await bootPackageWithLibrary('''
85+
abstract class A {}
86+
sealed class B extends A {}
87+
''');
88+
var Bclass = library.classes.named('B');
89+
expect(Bclass.fullkind, equals('sealed class')); // *not* sealed abstract
90+
}
91+
92+
void test_inferredModifiers() async {
93+
var library = await bootPackageWithLibrary('''
94+
base class A {}
95+
sealed class B extends A {}
96+
base class C extends B {}
97+
98+
interface class D {}
99+
sealed class E extends D {}
100+
interface class F extends E {}
101+
102+
final class G {}
103+
sealed class H extends G {}
104+
final class I extends H {}
105+
106+
class J {}
107+
base mixin K on J {}
108+
sealed class L extends J with K {}
109+
base class M extends L {}
110+
''');
111+
var Bclass = library.classes.named('B');
112+
var Cclass = library.classes.named('C');
113+
var Eclass = library.classes.named('E');
114+
var Fclass = library.classes.named('F');
115+
var Hclass = library.classes.named('H');
116+
var Iclass = library.classes.named('I');
117+
var Lclass = library.classes.named('L');
118+
var Mclass = library.classes.named('M');
119+
expect(Bclass.fullkind, equals('sealed class')); // *not* sealed base
120+
expect(Cclass.fullkind, equals('base class'));
121+
expect(Eclass.fullkind, equals('sealed class'));
122+
expect(Fclass.fullkind, equals('interface class'));
123+
expect(Hclass.fullkind, equals('sealed class'));
124+
expect(Iclass.fullkind, equals('final class'));
125+
expect(Lclass.fullkind, equals('sealed class'));
126+
expect(Mclass.fullkind, equals('base class'));
127+
}
82128
}

test/container_modifiers_test.dart

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'package:dartdoc/src/model/container_modifiers.dart';
6+
import 'package:test/test.dart';
7+
8+
void main() {
9+
group('fullKind string tests', () {
10+
test('basic', () {
11+
var l = {
12+
ContainerModifier.base,
13+
ContainerModifier.interface,
14+
ContainerModifier.abstract
15+
};
16+
expect(l.modifiersAsFullKindPrefix(), equals('abstract base interface'));
17+
});
18+
19+
test('hide abstract on sealed', () {
20+
var l = {ContainerModifier.abstract, ContainerModifier.sealed};
21+
expect(l.modifiersAsFullKindPrefix(), equals('sealed'));
22+
});
23+
24+
test('empty', () {
25+
var l = <ContainerModifier>{};
26+
expect(l.modifiersAsFullKindPrefix(), equals(''));
27+
});
28+
});
29+
}

0 commit comments

Comments
 (0)