Skip to content

Commit 52d46a4

Browse files
authored
Prevent private or non-canonical elements from ever generating links (#1589)
* First version * dartfmt * Add a test
1 parent 8cdb710 commit 52d46a4

File tree

81 files changed

+375
-57
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

81 files changed

+375
-57
lines changed

lib/src/element_type.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ class ElementType extends Privacy {
2121

2222
DartType get type => _type;
2323

24+
Library get library => element.library;
25+
2426
bool get isDynamic => _type.isDynamic;
2527

2628
bool get isFunctionType => (_type is FunctionType);

lib/src/model.dart

Lines changed: 92 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,13 @@ abstract class Inheritable implements ModelElement {
128128
ModelElement get definingEnclosingElement =>
129129
_memoizer.memoized(_definingEnclosingElement);
130130

131-
ModelElement _canonicalEnclosingElement() {
131+
@override
132+
ModelElement _canonicalModelElement() {
133+
return canonicalEnclosingElement?.allCanonicalModelElements
134+
?.firstWhere((m) => m.name == name, orElse: () => null);
135+
}
136+
137+
Class _canonicalEnclosingElement() {
132138
Class canonicalEnclosingClass;
133139
Element searchElement = element;
134140
if (isInherited) {
@@ -180,7 +186,7 @@ abstract class Inheritable implements ModelElement {
180186
return canonicalEnclosingClass;
181187
}
182188

183-
ModelElement get canonicalEnclosingElement =>
189+
Class get canonicalEnclosingElement =>
184190
_memoizer.memoized(_canonicalEnclosingElement);
185191

186192
List<Class> get inheritance {
@@ -608,6 +614,7 @@ class Class extends ModelElement
608614
@override
609615
ModelElement get enclosingElement => library;
610616

617+
@override
611618
String get fileName => "${name}-class.html";
612619

613620
String get fullkind {
@@ -654,8 +661,11 @@ class Class extends ModelElement
654661

655662
@override
656663
String get href {
657-
if (canonicalLibrary == null) return null;
658-
return '${canonicalLibrary.dirName}/$fileName';
664+
if (!identical(canonicalModelElement, this))
665+
return canonicalModelElement?.href;
666+
assert(canonicalLibrary != null);
667+
assert(canonicalLibrary == library);
668+
return '${library.dirName}/$fileName';
659669
}
660670

661671
/// Returns all the implementors of this class.
@@ -1132,8 +1142,11 @@ class Constructor extends ModelElement
11321142

11331143
@override
11341144
String get href {
1135-
if (canonicalLibrary == null) return null;
1136-
return '${canonicalLibrary.dirName}/${_constructor.enclosingElement.name}/$name.html';
1145+
if (!identical(canonicalModelElement, this))
1146+
return canonicalModelElement?.href;
1147+
assert(canonicalLibrary != null);
1148+
assert(canonicalLibrary == library);
1149+
return '${enclosingElement.library.dirName}/${enclosingElement.name}/$name.html';
11371150
}
11381151

11391152
@override
@@ -1346,9 +1359,12 @@ class EnumField extends Field {
13461359

13471360
@override
13481361
String get href {
1349-
if (canonicalLibrary == null || canonicalEnclosingElement == null)
1350-
return null;
1351-
return '${canonicalEnclosingElement.library.dirName}/${(canonicalEnclosingElement as Class).fileName}';
1362+
if (!identical(canonicalModelElement, this))
1363+
return canonicalModelElement?.href;
1364+
assert(!(canonicalLibrary == null || canonicalEnclosingElement == null));
1365+
assert(canonicalLibrary == library);
1366+
assert(canonicalEnclosingElement == enclosingElement);
1367+
return '${enclosingElement.library.dirName}/${(enclosingElement as Class).fileName}';
13521368
}
13531369

13541370
@override
@@ -1422,19 +1438,12 @@ class Field extends ModelElement
14221438

14231439
@override
14241440
String get href {
1425-
String retval;
1426-
if (canonicalLibrary == null) return null;
1427-
if (enclosingElement is Class) {
1428-
if (canonicalEnclosingElement == null) return null;
1429-
retval =
1430-
'${canonicalEnclosingElement.canonicalLibrary.dirName}/${canonicalEnclosingElement.name}/$_fileName';
1431-
} else if (enclosingElement is Library) {
1432-
retval = '${canonicalLibrary.dirName}/$_fileName';
1433-
} else {
1434-
throw new StateError(
1435-
'$name is not in a class or library, instead it is a ${enclosingElement.element}');
1436-
}
1437-
return retval;
1441+
if (!identical(canonicalModelElement, this))
1442+
return canonicalModelElement?.href;
1443+
assert(canonicalLibrary != null);
1444+
assert(canonicalEnclosingElement == enclosingElement);
1445+
assert(canonicalLibrary == library);
1446+
return '${enclosingElement.library.dirName}/${enclosingElement.name}/$fileName';
14381447
}
14391448

14401449
@override
@@ -1494,7 +1503,8 @@ class Field extends ModelElement
14941503

14951504
FieldElement get _field => (element as FieldElement);
14961505

1497-
String get _fileName => isConst ? '$name-constant.html' : '$name.html';
1506+
@override
1507+
String get fileName => isConst ? '$name-constant.html' : '$name.html';
14981508

14991509
String _sourceCode() {
15001510
// We could use a set to figure the dupes out, but that would lose ordering.
@@ -1906,6 +1916,7 @@ class Library extends ModelElement {
19061916

19071917
Iterable<Class> get publicExceptions => filterNonPublic(exceptions);
19081918

1919+
@override
19091920
String get fileName => '$dirName-library.html';
19101921

19111922
List<ModelFunction> _functions() {
@@ -1943,8 +1954,9 @@ class Library extends ModelElement {
19431954

19441955
@override
19451956
String get href {
1946-
if (canonicalLibrary == null) return null;
1947-
return '${canonicalLibrary.dirName}/$fileName';
1957+
if (!identical(canonicalModelElement, this))
1958+
return canonicalModelElement?.href;
1959+
return '${library.dirName}/$fileName';
19481960
}
19491961

19501962
InheritanceManager _inheritanceManager() => new InheritanceManager(element);
@@ -2275,18 +2287,19 @@ class Method extends ModelElement
22752287
return _enclosingClass;
22762288
}
22772289

2278-
String get fileName => "${name}.html";
2279-
22802290
String get fullkind {
22812291
if (_method.isAbstract) return 'abstract $kind';
22822292
return kind;
22832293
}
22842294

22852295
@override
22862296
String get href {
2287-
if (canonicalLibrary == null || canonicalEnclosingElement == null)
2288-
return null;
2289-
return '${canonicalEnclosingElement.canonicalLibrary.dirName}/${canonicalEnclosingElement.name}/${fileName}';
2297+
if (!identical(canonicalModelElement, this))
2298+
return canonicalModelElement?.href;
2299+
assert(!(canonicalLibrary == null || canonicalEnclosingElement == null));
2300+
assert(canonicalLibrary == library);
2301+
assert(canonicalEnclosingElement == enclosingElement);
2302+
return '${enclosingElement.library.dirName}/${enclosingElement.name}/${fileName}';
22902303
}
22912304

22922305
@override
@@ -2693,6 +2706,20 @@ abstract class ModelElement extends Canonicalization
26932706
bool get canHaveParameters =>
26942707
element is ExecutableElement || element is FunctionTypedElement;
26952708

2709+
ModelElement _canonicalModelElement() {
2710+
Class preferredClass;
2711+
if (enclosingElement is Class) {
2712+
preferredClass = enclosingElement;
2713+
}
2714+
return package.findCanonicalModelElementFor(element,
2715+
preferredClass: preferredClass);
2716+
}
2717+
2718+
// Returns the canonical ModelElement for this ModelElement, or null
2719+
// if there isn't one.
2720+
ModelElement get canonicalModelElement =>
2721+
_memoizer.memoized(_canonicalModelElement);
2722+
26962723
// TODO(jcollins-g): untangle when mixins can call super
26972724
@override
26982725
List<ModelElement> get documentationFrom =>
@@ -2888,6 +2915,8 @@ abstract class ModelElement extends Canonicalization
28882915
return '';
28892916
}
28902917

2918+
String get fileName => "${name}.html";
2919+
28912920
/// Returns the fully qualified name.
28922921
///
28932922
/// For example: libraryName.className.methodName
@@ -3559,12 +3588,13 @@ class ModelFunctionTyped extends ModelElement
35593588
@override
35603589
ModelElement get enclosingElement => library;
35613590

3562-
String get fileName => "$name.html";
3563-
35643591
@override
35653592
String get href {
3566-
if (canonicalLibrary == null) return null;
3567-
return '${canonicalLibrary.dirName}/$fileName';
3593+
if (!identical(canonicalModelElement, this))
3594+
return canonicalModelElement?.href;
3595+
assert(canonicalLibrary != null);
3596+
assert(canonicalLibrary == library);
3597+
return '${library.dirName}/$fileName';
35683598
}
35693599

35703600
@override
@@ -4182,6 +4212,9 @@ class Package extends Canonicalization with Nameable, Warnable, Memoizeable {
41824212
if (e is PropertyAccessorElement) {
41834213
searchElement = e.variable;
41844214
}
4215+
if (e is GenericFunctionTypeElement) {
4216+
searchElement = e.enclosingElement;
4217+
}
41854218

41864219
for (Library library in publicLibraries) {
41874220
if (library.modelElementsMap.containsKey(searchElement)) {
@@ -4211,6 +4244,10 @@ class Package extends Canonicalization with Nameable, Warnable, Memoizeable {
42114244
/// Tries to find a canonical ModelElement for this element. If we know
42124245
/// this element is related to a particular class, pass preferredClass to
42134246
/// disambiguate.
4247+
///
4248+
/// This doesn't know anything about [Package.inheritThrough] and probably
4249+
/// shouldn't, so using it with [Inheritable]s without special casing is
4250+
/// not advised.
42144251
ModelElement findCanonicalModelElementFor(Element e, {Class preferredClass}) {
42154252
assert(allLibrariesAdded);
42164253
Library lib = findCanonicalLibraryFor(e);
@@ -4295,7 +4332,9 @@ class Package extends Canonicalization with Nameable, Warnable, Memoizeable {
42954332
}
42964333

42974334
assert(matches.length <= 1);
4298-
if (!matches.isEmpty) modelElement = matches.first;
4335+
if (matches.isNotEmpty) {
4336+
modelElement = matches.first;
4337+
}
42994338
} else {
43004339
if (lib != null) {
43014340
Accessor getter;
@@ -4431,19 +4470,7 @@ class Parameter extends ModelElement implements EnclosedElement {
44314470

44324471
@override
44334472
String get href {
4434-
if (canonicalLibrary == null) return null;
4435-
var p = _parameter.enclosingElement;
4436-
4437-
if (p is FunctionElement) {
4438-
return '${canonicalLibrary.dirName}/${p.name}.html';
4439-
} else {
4440-
// TODO: why is this logic here?
4441-
var name = Operator.friendlyNames.containsKey(p.name)
4442-
? Operator.friendlyNames[p.name]
4443-
: p.name;
4444-
return '${canonicalLibrary.dirName}/${p.enclosingElement.name}/' +
4445-
'${name}.html#${htmlId}';
4446-
}
4473+
throw new StateError('href not implemented for parameters');
44474474
}
44484475

44494476
@override
@@ -4636,8 +4663,11 @@ class TopLevelVariable extends ModelElement
46364663

46374664
@override
46384665
String get href {
4639-
if (canonicalLibrary == null) return null;
4640-
return '${canonicalLibrary.dirName}/$_fileName';
4666+
if (!identical(canonicalModelElement, this))
4667+
return canonicalModelElement?.href;
4668+
assert(canonicalLibrary != null);
4669+
assert(canonicalLibrary == library);
4670+
return '${library.dirName}/$fileName';
46414671
}
46424672

46434673
@override
@@ -4664,7 +4694,8 @@ class TopLevelVariable extends ModelElement
46644694
return docs;
46654695
}
46664696

4667-
String get _fileName => isConst ? '$name-constant.html' : '$name.html';
4697+
@override
4698+
String get fileName => isConst ? '$name-constant.html' : '$name.html';
46684699

46694700
TopLevelVariableElement get _variable => (element as TopLevelVariableElement);
46704701
}
@@ -4678,8 +4709,6 @@ class Typedef extends ModelElement
46784709
@override
46794710
ModelElement get enclosingElement => library;
46804711

4681-
String get fileName => '$name.html';
4682-
46834712
@override
46844713
String get nameWithGenerics => '$name${super.genericParameters}';
46854714

@@ -4697,8 +4726,11 @@ class Typedef extends ModelElement
46974726

46984727
@override
46994728
String get href {
4700-
if (canonicalLibrary == null) return null;
4701-
return '${canonicalLibrary.dirName}/$fileName';
4729+
if (!identical(canonicalModelElement, this))
4730+
return canonicalModelElement?.href;
4731+
assert(canonicalLibrary != null);
4732+
assert(canonicalLibrary == library);
4733+
return '${library.dirName}/$fileName';
47024734
}
47034735

47044736
// Food for mustache.
@@ -4730,8 +4762,11 @@ class TypeParameter extends ModelElement {
47304762

47314763
@override
47324764
String get href {
4733-
if (canonicalLibrary == null) return null;
4734-
return '${canonicalLibrary.dirName}/${_typeParameter.enclosingElement.name}/$name';
4765+
if (!identical(canonicalModelElement, this))
4766+
return canonicalModelElement?.href;
4767+
assert(canonicalLibrary != null);
4768+
assert(canonicalLibrary == library);
4769+
return '${enclosingElement.library.dirName}/${enclosingElement.name}/$name';
47354770
}
47364771

47374772
@override

lib/src/model_utils.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,13 @@ bool hasPrivateName(Element e) {
7070
if (e.name.startsWith('_')) {
7171
return true;
7272
}
73+
// GenericFunctionTypeElements have the name we care about in the enclosing
74+
// element.
75+
if (e is GenericFunctionTypeElement) {
76+
if (e.enclosingElement.name.startsWith('_')) {
77+
return true;
78+
}
79+
}
7380
if (e is LibraryElement &&
7481
(e.identifier.startsWith('dart:_') ||
7582
['dart:nativewrappers'].contains(e.identifier))) {

test/model_test.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1889,6 +1889,7 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans,
18891889
group('Constant', () {
18901890
TopLevelVariable greenConstant,
18911891
cat,
1892+
customClassPrivate,
18921893
orangeConstant,
18931894
prettyColorsConstant,
18941895
deprecated;
@@ -1906,6 +1907,7 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans,
19061907
deprecated =
19071908
exLibrary.constants.firstWhere((c) => c.name == 'deprecated');
19081909
Class Dog = exLibrary.allClasses.firstWhere((c) => c.name == 'Dog');
1910+
customClassPrivate = fakeLibrary.constants.firstWhere((c) => c.name == 'CUSTOM_CLASS_PRIVATE');
19091911
aStaticConstField =
19101912
Dog.allFields.firstWhere((f) => f.name == 'aStaticConstField');
19111913
aName = Dog.allFields.firstWhere((f) => f.name == 'aName');
@@ -1920,6 +1922,10 @@ String topLevelFunction(int param1, bool param2, Cool coolBeans,
19201922
expect(aStaticConstField.constantValue, '&quot;A Constant Dog&quot;');
19211923
});
19221924

1925+
test('privately constructed constants are unlinked', () {
1926+
expect(customClassPrivate.constantValue, 'const _APrivateConstClass()');
1927+
});
1928+
19231929
test('has a fully qualified name', () {
19241930
expect(greenConstant.fullyQualifiedName, 'ex.COLOR_GREEN');
19251931
});

testing/test_package/lib/fake.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,12 @@ class ConstantClass {
160160
ConstantClass.notConstant(this.value);
161161
}
162162

163+
class _APrivateConstClass {
164+
const _APrivateConstClass();
165+
}
166+
167+
const _APrivateConstClass CUSTOM_CLASS_PRIVATE = const _APrivateConstClass();
168+
163169
// No dart docs on purpose. Also, a non-primitive const class.
164170
const ConstantClass CUSTOM_CLASS = const ConstantClass('custom');
165171

testing/test_package_docs/fake/AClassUsingASuperMixin-class.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ <h5>fake library</h5>
7171

7272
<li class="section-title"><a href="fake/fake-library.html#constants">Constants</a></li>
7373
<li><a href="fake/CUSTOM_CLASS-constant.html">CUSTOM_CLASS</a></li>
74+
<li><a href="fake/CUSTOM_CLASS_PRIVATE-constant.html">CUSTOM_CLASS_PRIVATE</a></li>
7475
<li><a class="deprecated" href="fake/DOWN-constant.html">DOWN</a></li>
7576
<li><a href="fake/greatAnnotation-constant.html">greatAnnotation</a></li>
7677
<li><a href="fake/greatestAnnotation-constant.html">greatestAnnotation</a></li>

testing/test_package_docs/fake/AMixinCallingSuper-class.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ <h5>fake library</h5>
7171

7272
<li class="section-title"><a href="fake/fake-library.html#constants">Constants</a></li>
7373
<li><a href="fake/CUSTOM_CLASS-constant.html">CUSTOM_CLASS</a></li>
74+
<li><a href="fake/CUSTOM_CLASS_PRIVATE-constant.html">CUSTOM_CLASS_PRIVATE</a></li>
7475
<li><a class="deprecated" href="fake/DOWN-constant.html">DOWN</a></li>
7576
<li><a href="fake/greatAnnotation-constant.html">greatAnnotation</a></li>
7677
<li><a href="fake/greatestAnnotation-constant.html">greatestAnnotation</a></li>

0 commit comments

Comments
 (0)