Skip to content

Commit d5bb24b

Browse files
authored
Convert Tuples to Records and split a helper out from findCanonicalModelElementFor (#3432)
1 parent 449478c commit d5bb24b

File tree

5 files changed

+121
-185
lines changed

5 files changed

+121
-185
lines changed

analysis_options.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ linter:
2323
- avoid_dynamic_calls
2424
- avoid_slow_async_io
2525
- avoid_unused_constructor_parameters
26+
- collection_methods_unrelated_type
2627
- directives_ordering
2728
- no_adjacent_strings_in_list
2829
- omit_local_variable_types

lib/src/model/model_element.dart

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ import 'package:dartdoc/src/render/parameter_renderer.dart';
2929
import 'package:dartdoc/src/render/source_code_renderer.dart';
3030
import 'package:dartdoc/src/source_linker.dart';
3131
import 'package:dartdoc/src/special_elements.dart';
32-
import 'package:dartdoc/src/tuple.dart';
3332
import 'package:dartdoc/src/type_utils.dart';
3433
import 'package:dartdoc/src/warnings.dart';
3534
import 'package:meta/meta.dart';
@@ -173,9 +172,8 @@ abstract class ModelElement extends Canonicalization
173172
}
174173

175174
// Return the cached ModelElement if it exists.
176-
var key =
177-
Tuple3<Element, Library, Container?>(e, library, enclosingContainer);
178-
var cachedModelElement = packageGraph.allConstructedModelElements[key];
175+
var cachedModelElement = packageGraph
176+
.allConstructedModelElements[(e, library, enclosingContainer)];
179177
if (cachedModelElement != null) {
180178
return cachedModelElement;
181179
}
@@ -264,9 +262,8 @@ abstract class ModelElement extends Canonicalization
264262
}
265263

266264
// Return the cached ModelElement if it exists.
267-
var key =
268-
Tuple3<Element, Library?, Container?>(e, library, enclosingContainer);
269-
var cachedModelElement = packageGraph.allConstructedModelElements[key];
265+
var cachedModelElement = packageGraph
266+
.allConstructedModelElements[(e, library, enclosingContainer)];
270267
if (cachedModelElement != null) {
271268
return cachedModelElement;
272269
}
@@ -295,14 +292,11 @@ abstract class ModelElement extends Canonicalization
295292
// TODO(jcollins-g): Reenable Parameter caching when dart-lang/sdk#30146
296293
// is fixed?
297294
if (library != Library.sentinel && newModelElement is! Parameter) {
298-
var key =
299-
Tuple3<Element, Library, Container?>(e, library, enclosingContainer);
295+
var key = (e, library, enclosingContainer);
300296
library.packageGraph.allConstructedModelElements[key] = newModelElement;
301297
if (newModelElement is Inheritable) {
302-
var iKey = Tuple2<Element, Library>(e, library);
303298
library.packageGraph.allInheritableElements
304-
.putIfAbsent(iKey, () => {})
305-
.add(newModelElement);
299+
.putIfAbsent((e, library), () => {}).add(newModelElement);
306300
}
307301
}
308302
}

lib/src/model/package_graph.dart

Lines changed: 110 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import 'package:dartdoc/src/render/renderer_factory.dart';
2626
import 'package:dartdoc/src/special_elements.dart';
2727
import 'package:dartdoc/src/tool_definition.dart';
2828
import 'package:dartdoc/src/tool_runner.dart';
29-
import 'package:dartdoc/src/tuple.dart';
3029
import 'package:dartdoc/src/warnings.dart';
3130
import 'package:meta/meta.dart';
3231

@@ -219,12 +218,11 @@ class PackageGraph with CommentReferable, Nameable, ModelBuilder {
219218

220219
/// All [ModelElement]s constructed for this package; a superset of
221220
/// [_allModelElements].
222-
final Map<Tuple3<Element, Library, Container?>, ModelElement?>
223-
allConstructedModelElements = {};
221+
final Map<(Element element, Library? library, Container? enclosingElement),
222+
ModelElement?> allConstructedModelElements = {};
224223

225224
/// Anything that might be inheritable, place here for later lookup.
226-
final Map<Tuple2<Element, Library>, Set<ModelElement>>
227-
allInheritableElements = {};
225+
final Map<(Element, Library), Set<ModelElement>> allInheritableElements = {};
228226

229227
/// A mapping of the list of classes which implement each class.
230228
final Map<InheritingContainer, List<InheritingContainer>> _implementors =
@@ -287,18 +285,19 @@ class PackageGraph with CommentReferable, Nameable, ModelBuilder {
287285
late final PackageWarningCounter packageWarningCounter =
288286
PackageWarningCounter(this);
289287

290-
final Set<Tuple3<Element?, PackageWarning, String?>> _warnAlreadySeen = {};
288+
final Set<(Element? element, PackageWarning packageWarning, String? message)>
289+
_warnAlreadySeen = {};
291290

292291
void warnOnElement(Warnable? warnable, PackageWarning kind,
293292
{String? message,
294293
Iterable<Locatable> referredFrom = const [],
295294
Iterable<String> extendedDebug = const []}) {
296-
var newEntry = Tuple3(warnable?.element, kind, message);
295+
var newEntry = (warnable?.element, kind, message);
297296
if (_warnAlreadySeen.contains(newEntry)) {
298297
return;
299298
}
300-
// Warnings can cause other warnings. Queue them up via the stack but
301-
// don't allow warnings we're already working on to get in there.
299+
// Warnings can cause other warnings. Queue them up via the stack but don't
300+
// allow warnings we're already working on to get in there.
302301
_warnAlreadySeen.add(newEntry);
303302
_warnOnElement(warnable, kind,
304303
message: message ?? '',
@@ -417,12 +416,12 @@ class PackageGraph with CommentReferable, Nameable, ModelBuilder {
417416
Map<LibraryElement, Set<Library>> _libraryElementReexportedBy = {};
418417

419418
/// Prevent cycles from breaking our stack.
420-
Set<Tuple2<Library, LibraryElement?>> _reexportsTagged = {};
419+
Set<(Library, LibraryElement?)> _reexportsTagged = {};
421420

422421
void _tagReexportsFor(
423422
final Library topLevelLibrary, final LibraryElement? libraryElement,
424423
[LibraryExportElement? lastExportedElement]) {
425-
var key = Tuple2<Library, LibraryElement?>(topLevelLibrary, libraryElement);
424+
var key = (topLevelLibrary, libraryElement);
426425
if (_reexportsTagged.contains(key)) {
427426
return;
428427
}
@@ -625,35 +624,36 @@ class PackageGraph with CommentReferable, Nameable, ModelBuilder {
625624

626625
final Map<Element?, Library?> _canonicalLibraryFor = {};
627626

627+
@Deprecated('Not public API; will be removed')
628+
Library? findCanonicalLibraryFor(Element e) => _findCanonicalLibraryFor(e);
629+
628630
/// Tries to find a top level library that references this element.
629-
Library? findCanonicalLibraryFor(Element? e) {
631+
Library? _findCanonicalLibraryFor(Element e) {
630632
assert(allLibrariesAdded);
631-
var searchElement = e;
632-
if (e is PropertyAccessorElement) {
633-
searchElement = e.variable;
634-
}
635-
if (e is GenericFunctionTypeElement) {
636-
searchElement = e.enclosingElement;
637-
}
638-
639633
if (_canonicalLibraryFor.containsKey(e)) {
640634
return _canonicalLibraryFor[e];
641635
}
642636
_canonicalLibraryFor[e] = null;
637+
638+
var searchElement = switch (e) {
639+
PropertyAccessorElement() => e.variable,
640+
GenericFunctionTypeElement() => e.enclosingElement,
641+
_ => e,
642+
};
643+
if (searchElement == null) return null;
643644
for (var library in publicLibraries) {
644-
if (library.modelElementsMap.containsKey(searchElement)) {
645-
for (var modelElement in library.modelElementsMap[searchElement!]!) {
646-
if (modelElement.isCanonical) {
647-
return _canonicalLibraryFor[e] = library;
648-
}
645+
var modelElements = library.modelElementsMap[searchElement];
646+
if (modelElements != null) {
647+
if (modelElements.any((element) => element.isCanonical)) {
648+
return _canonicalLibraryFor[e] = library;
649649
}
650650
}
651651
}
652652
return _canonicalLibraryFor[e];
653653
}
654654

655-
/// Tries to find a canonical ModelElement for this [e]. If we know
656-
/// this element is related to a particular class, pass a [preferredClass] to
655+
/// Tries to find a canonical [ModelElement] for this [e]. If we know this
656+
/// element is related to a particular class, pass a [preferredClass] to
657657
/// disambiguate.
658658
///
659659
/// This doesn't know anything about [PackageGraph.inheritThrough] and
@@ -662,107 +662,35 @@ class PackageGraph with CommentReferable, Nameable, ModelBuilder {
662662
ModelElement? findCanonicalModelElementFor(Element? e,
663663
{Container? preferredClass}) {
664664
assert(allLibrariesAdded);
665-
var lib = findCanonicalLibraryFor(e);
665+
if (e == null) return null;
666666
if (preferredClass != null) {
667667
var canonicalClass =
668668
findCanonicalModelElementFor(preferredClass.element) as Container?;
669669
if (canonicalClass != null) preferredClass = canonicalClass;
670670
}
671+
var lib = _findCanonicalLibraryFor(e);
671672
if (lib == null && preferredClass != null) {
672-
lib = findCanonicalLibraryFor(preferredClass.element);
673+
lib = _findCanonicalLibraryFor(preferredClass.element);
673674
}
674675
// For elements defined in extensions, they are canonical.
675-
var enclosingElement = e?.enclosingElement;
676+
var enclosingElement = e.enclosingElement;
676677
if (enclosingElement is ExtensionElement) {
677678
lib ??= modelBuilder.fromElement(enclosingElement.library) as Library?;
678-
// (TODO:keertip) Find a better way to exclude members of extensions
679-
// when libraries are specified using the "--include" flag
679+
// TODO(keertip): Find a better way to exclude members of extensions
680+
// when libraries are specified using the "--include" flag.
680681
if (lib?.isDocumented == true) {
681-
return modelBuilder.from(e!, lib!);
682+
return modelBuilder.from(e, lib!);
682683
}
683684
}
685+
// TODO(jcollins-g): The data structures should be changed to eliminate
686+
// guesswork with member elements.
687+
var declaration = e.declaration;
684688
ModelElement? modelElement;
685-
// TODO(jcollins-g): Special cases are pretty large here. Refactor to split
686-
// out into helpers.
687-
// TODO(jcollins-g): The data structures should be changed to eliminate guesswork
688-
// with member elements.
689-
var declaration = e?.declaration;
690689
if (declaration != null &&
691690
(e is ClassMemberElement || e is PropertyAccessorElement)) {
692691
e = declaration;
693-
var candidates = <ModelElement>{};
694-
var iKey = Tuple2<Element, Library?>(e, lib);
695-
var key = Tuple3<Element, Library?, Class?>(e, lib, null);
696-
var keyWithClass = Tuple3<Element, Library?, InheritingContainer?>(
697-
e, lib, preferredClass as InheritingContainer?);
698-
var constructedWithKey = allConstructedModelElements[key];
699-
if (constructedWithKey != null) {
700-
candidates.add(constructedWithKey);
701-
}
702-
var constructedWithKeyWithClass =
703-
allConstructedModelElements[keyWithClass];
704-
if (constructedWithKeyWithClass != null) {
705-
candidates.add(constructedWithKeyWithClass);
706-
}
707-
if (candidates.isEmpty && allInheritableElements.containsKey(iKey)) {
708-
candidates.addAll(
709-
allInheritableElements[iKey as Tuple2<Element, Library>]!
710-
.where((me) => me.isCanonical));
711-
}
712-
713-
var canonicalClass = findCanonicalModelElementFor(e.enclosingElement);
714-
if (canonicalClass is InheritingContainer) {
715-
candidates.addAll(canonicalClass.allCanonicalModelElements.where((m) {
716-
return m.element == e;
717-
}));
718-
}
719-
720-
var matches = {...candidates.where((me) => me.isCanonical)};
721-
722-
// It's possible to find accessors but no combos. Be sure that if we
723-
// have Accessors, we find their combos too.
724-
if (matches.any((me) => me is Accessor)) {
725-
var combos = matches
726-
.whereType<Accessor>()
727-
.map((a) => a.enclosingCombo)
728-
.toList(growable: false);
729-
matches.addAll(combos);
730-
assert(combos.every((c) => c.isCanonical));
731-
}
732-
733-
// This is for situations where multiple classes may actually be canonical
734-
// for an inherited element whose defining Class is not canonical.
735-
if (matches.length > 1 && preferredClass != null) {
736-
// Search for matches inside our superchain.
737-
var superChain = [
738-
for (final elementType in preferredClass.superChain)
739-
elementType.modelElement as InheritingContainer,
740-
preferredClass,
741-
];
742-
743-
matches.removeWhere((me) => !superChain.contains(me.enclosingElement));
744-
// Assumed all matches are EnclosedElement because we've been told about a
745-
// preferredClass.
746-
var enclosingElements = {
747-
...matches.map((me) => me.enclosingElement as Class?)
748-
};
749-
for (var c in superChain.reversed) {
750-
if (enclosingElements.contains(c)) {
751-
matches.removeWhere((me) => me.enclosingElement != c);
752-
}
753-
if (matches.length <= 1) break;
754-
}
755-
}
756-
757-
// Prefer a GetterSetterCombo to Accessors.
758-
if (matches.any((me) => me is GetterSetterCombo)) {
759-
matches.removeWhere((me) => me is Accessor);
760-
}
761-
762-
assert(matches.length <= 1);
763-
if (matches.isNotEmpty) {
764-
modelElement = matches.first;
765-
}
692+
modelElement = _findCanonicalModelElementForAmbiguous(e, lib,
693+
preferredClass: preferredClass as InheritingContainer?);
766694
} else {
767695
if (lib != null) {
768696
if (e is PropertyInducingElement) {
@@ -773,7 +701,7 @@ class PackageGraph with CommentReferable, Nameable, ModelBuilder {
773701
modelElement = modelBuilder.fromPropertyInducingElement(e, lib,
774702
getter: getter as Accessor?, setter: setter as Accessor?);
775703
} else {
776-
modelElement = modelBuilder.from(e!, lib);
704+
modelElement = modelBuilder.from(e, lib);
777705
}
778706
}
779707
assert(modelElement is! Inheritable);
@@ -788,6 +716,76 @@ class PackageGraph with CommentReferable, Nameable, ModelBuilder {
788716
return modelElement;
789717
}
790718

719+
ModelElement? _findCanonicalModelElementForAmbiguous(Element e, Library? lib,
720+
{InheritingContainer? preferredClass}) {
721+
var candidates = <ModelElement?>{};
722+
var constructedWithKey = allConstructedModelElements[(e, lib, null)];
723+
if (constructedWithKey != null) {
724+
candidates.add(constructedWithKey);
725+
}
726+
var constructedWithKeyWithClass =
727+
allConstructedModelElements[(e, lib, preferredClass)];
728+
if (constructedWithKeyWithClass != null) {
729+
candidates.add(constructedWithKeyWithClass);
730+
}
731+
if (candidates.isEmpty) {
732+
candidates = {
733+
...?allInheritableElements[(e, lib)]?.where((me) => me.isCanonical),
734+
};
735+
}
736+
737+
var canonicalClass = findCanonicalModelElementFor(e.enclosingElement);
738+
if (canonicalClass is InheritingContainer) {
739+
candidates.addAll(canonicalClass.allCanonicalModelElements
740+
.where((m) => m.element == e));
741+
}
742+
743+
var matches = {...candidates.whereNotNull().where((me) => me.isCanonical)};
744+
745+
// It's possible to find [Accessor]s but no combos. Be sure that if we
746+
// have accessors, we find their combos too.
747+
if (matches.any((me) => me is Accessor)) {
748+
var combos = matches
749+
.whereType<Accessor>()
750+
.map((a) => a.enclosingCombo)
751+
.toList(growable: false);
752+
matches.addAll(combos);
753+
assert(combos.every((c) => c.isCanonical));
754+
}
755+
756+
// This is for situations where multiple classes may actually be canonical
757+
// for an inherited element whose defining Class is not canonical.
758+
if (matches.length > 1 && preferredClass != null) {
759+
// Search for matches inside our superchain.
760+
var superChain = [
761+
...preferredClass.superChain.map((e) => e.modelElement),
762+
preferredClass,
763+
];
764+
765+
matches.removeWhere((me) => !superChain.contains(me.enclosingElement));
766+
// Assumed all matches are EnclosedElement because we've been told about a
767+
// preferredClass.
768+
var enclosingElements = {...matches.map((me) => me.enclosingElement)};
769+
for (var c in superChain.reversed) {
770+
if (enclosingElements.contains(c)) {
771+
matches.removeWhere((me) => me.enclosingElement != c);
772+
}
773+
if (matches.length <= 1) break;
774+
}
775+
}
776+
777+
// Prefer a GetterSetterCombo to Accessors.
778+
if (matches.any((me) => me is GetterSetterCombo)) {
779+
matches.removeWhere((me) => me is Accessor);
780+
}
781+
782+
assert(matches.length <= 1);
783+
if (matches.isNotEmpty) {
784+
return matches.first;
785+
}
786+
return null;
787+
}
788+
791789
/// This is used when we might need a Library object that isn't actually
792790
/// a documentation entry point (for elements that have no Library within the
793791
/// set of canonical Libraries).

0 commit comments

Comments
 (0)