@@ -473,13 +473,14 @@ abstract class ModelElement extends Canonicalization
473473 return _config;
474474 }
475475
476+ Set <String > _locationPieces;
477+
476478 @override
477- Set <String > get locationPieces {
478- return Set .from (element.location
479- .toString ()
480- .split (locationSplitter)
481- .where ((s) => s.isNotEmpty));
482- }
479+ Set <String > get locationPieces =>
480+ _locationPieces ?? = Set .from (element.location
481+ .toString ()
482+ .split (locationSplitter)
483+ .where ((s) => s.isNotEmpty));
483484
484485 Set <String > get features {
485486 var allFeatures = < String > {};
@@ -648,81 +649,13 @@ abstract class ModelElement extends Canonicalization
648649 if (! _canonicalLibraryIsSet) {
649650 // This is not accurate if we are constructing the Package.
650651 assert (packageGraph.allLibrariesAdded);
651- // Since we're looking for a library, find the [Element] immediately
652- // contained by a [CompilationUnitElement] in the tree.
653- var topLevelElement = element;
654- while (topLevelElement != null &&
655- topLevelElement.enclosingElement is ! LibraryElement &&
656- topLevelElement.enclosingElement is ! CompilationUnitElement &&
657- topLevelElement.enclosingElement != null ) {
658- topLevelElement = topLevelElement.enclosingElement;
659- }
660652
661653 // Privately named elements can never have a canonical library, so
662654 // just shortcut them out.
663655 if (! utils.hasPublicName (element)) {
664656 _canonicalLibrary = null ;
665- } else if (definingLibrary != null &&
666- ! packageGraph.localPublicLibraries.contains (definingLibrary)) {
667- var candidateLibraries = definingLibrary.exportedInLibraries
668- ? .where ((l) =>
669- l.isPublic &&
670- l.package.documentedWhere != DocumentLocation .missing)
671- ? .toList ();
672-
673- if (candidateLibraries != null ) {
674- candidateLibraries = candidateLibraries.where ((l) {
675- var lookup =
676- l.element.exportNamespace.definedNames[topLevelElement? .name];
677- if (lookup is PropertyAccessorElement ) {
678- lookup = (lookup as PropertyAccessorElement ).variable;
679- }
680- if (topLevelElement == lookup) return true ;
681- return false ;
682- }).toList ();
683-
684- // Avoid claiming canonicalization for elements outside of this element's
685- // defining package.
686- // TODO(jcollins-g): Make the else block unconditional.
687- if (candidateLibraries.isNotEmpty &&
688- ! candidateLibraries
689- .any ((l) => l.package == definingLibrary.package)) {
690- warn (PackageWarning .reexportedPrivateApiAcrossPackages,
691- message: definingLibrary.package.fullyQualifiedName,
692- referredFrom: candidateLibraries);
693- } else {
694- candidateLibraries
695- .removeWhere ((l) => l.package != definingLibrary.package);
696- }
697-
698- // Start with our top-level element.
699- var warnable =
700- ModelElement .fromElement (topLevelElement, packageGraph);
701- if (candidateLibraries.length > 1 ) {
702- // Heuristic scoring to determine which library a human likely
703- // considers this element to be primarily 'from', and therefore,
704- // canonical. Still warn if the heuristic isn't that confident.
705- var scoredCandidates =
706- warnable.scoreCanonicalCandidates (candidateLibraries);
707- candidateLibraries =
708- scoredCandidates.map ((s) => s.library).toList ();
709- var secondHighestScore =
710- scoredCandidates[scoredCandidates.length - 2 ].score;
711- var highestScore = scoredCandidates.last.score;
712- var confidence = highestScore - secondHighestScore;
713- var message =
714- '${candidateLibraries .map ((l ) => l .name )} -> ${candidateLibraries .last .name } (confidence ${confidence .toStringAsPrecision (4 )})' ;
715-
716- if (confidence < config.ambiguousReexportScorerMinConfidence) {
717- warnable.warn (PackageWarning .ambiguousReexport,
718- message: message,
719- extendedDebug: scoredCandidates.map ((s) => '$s ' ));
720- }
721- }
722- if (candidateLibraries.isNotEmpty) {
723- _canonicalLibrary = candidateLibraries.last;
724- }
725- }
657+ } else if (! packageGraph.localPublicLibraries.contains (definingLibrary)) {
658+ _canonicalLibrary = _searchForCanonicalLibrary ();
726659 } else {
727660 _canonicalLibrary = definingLibrary;
728661 }
@@ -743,6 +676,79 @@ abstract class ModelElement extends Canonicalization
743676 return _canonicalLibrary;
744677 }
745678
679+ Library _searchForCanonicalLibrary () {
680+ var thisAndExported = definingLibrary.exportedInLibraries;
681+
682+ if (thisAndExported == null ) {
683+ return null ;
684+ }
685+
686+ // Since we're looking for a library, find the [Element] immediately
687+ // contained by a [CompilationUnitElement] in the tree.
688+ var topLevelElement = element;
689+ while (topLevelElement != null &&
690+ topLevelElement.enclosingElement is ! LibraryElement &&
691+ topLevelElement.enclosingElement is ! CompilationUnitElement &&
692+ topLevelElement.enclosingElement != null ) {
693+ topLevelElement = topLevelElement.enclosingElement;
694+ }
695+
696+ var candidateLibraries = thisAndExported
697+ .where ((l) =>
698+ l.isPublic && l.package.documentedWhere != DocumentLocation .missing)
699+ .where ((l) {
700+ var lookup =
701+ l.element.exportNamespace.definedNames[topLevelElement? .name];
702+ if (lookup is PropertyAccessorElement ) {
703+ lookup = (lookup as PropertyAccessorElement ).variable;
704+ }
705+ return topLevelElement == lookup;
706+ }).toList ();
707+
708+ // Avoid claiming canonicalization for elements outside of this element's
709+ // defining package.
710+ // TODO(jcollins-g): Make the else block unconditional.
711+ if (candidateLibraries.isNotEmpty &&
712+ ! candidateLibraries.any ((l) => l.package == definingLibrary.package)) {
713+ warn (PackageWarning .reexportedPrivateApiAcrossPackages,
714+ message: definingLibrary.package.fullyQualifiedName,
715+ referredFrom: candidateLibraries);
716+ } else {
717+ candidateLibraries
718+ .removeWhere ((l) => l.package != definingLibrary.package);
719+ }
720+
721+ if (candidateLibraries.isEmpty) {
722+ return null ;
723+ }
724+ if (candidateLibraries.length == 1 ) {
725+ return candidateLibraries.single;
726+ }
727+
728+ // Start with our top-level element.
729+ var warnable = ModelElement .fromElement (topLevelElement, packageGraph);
730+ // Heuristic scoring to determine which library a human likely
731+ // considers this element to be primarily 'from', and therefore,
732+ // canonical. Still warn if the heuristic isn't that confident.
733+ var scoredCandidates =
734+ warnable.scoreCanonicalCandidates (candidateLibraries);
735+ candidateLibraries = scoredCandidates.map ((s) => s.library).toList ();
736+ var secondHighestScore =
737+ scoredCandidates[scoredCandidates.length - 2 ].score;
738+ var highestScore = scoredCandidates.last.score;
739+ var confidence = highestScore - secondHighestScore;
740+
741+ if (confidence < config.ambiguousReexportScorerMinConfidence) {
742+ var libraryNames = candidateLibraries.map ((l) => l.name);
743+ var message = '$libraryNames -> ${candidateLibraries .last .name } '
744+ '(confidence ${confidence .toStringAsPrecision (4 )})' ;
745+ warnable.warn (PackageWarning .ambiguousReexport,
746+ message: message, extendedDebug: scoredCandidates.map ((s) => '$s ' ));
747+ }
748+
749+ return candidateLibraries.last;
750+ }
751+
746752 @override
747753 bool get isCanonical {
748754 if (! isPublic) return false ;
@@ -1072,6 +1078,9 @@ abstract class ModelElement extends Canonicalization
10721078
10731079 String computeDocumentationComment () => element.documentationComment;
10741080
1081+ /// The documentation comment on the Element may be null, so memoization
1082+ /// cannot rely on the null-ness of [_documentationComment] , it must be
1083+ /// more explicit.
10751084 bool _documentationCommentComputed = false ;
10761085 String _documentationComment;
10771086
0 commit comments