@@ -11,8 +11,10 @@ import 'package:analyzer/dart/element/type.dart';
1111import 'package:analyzer/diagnostic/diagnostic.dart' ;
1212import 'package:analyzer/error/error.dart' ;
1313import 'package:analyzer/source/source.dart' ;
14+ import 'package:analyzer/src/dart/element/extensions.dart' ;
1415import 'package:analyzer/src/dart/element/type.dart' ;
1516import 'package:analyzer/src/diagnostic/diagnostic.dart' ;
17+ import 'package:analyzer/src/utilities/extensions/collection.dart' ;
1618import 'package:meta/meta.dart' ;
1719import 'package:source_span/source_span.dart' ;
1820
@@ -168,7 +170,19 @@ class ErrorReporter {
168170 return ;
169171 }
170172
171- _convertElements (arguments);
173+ if (arguments != null ) {
174+ var invalid = arguments
175+ .whereNotType <String >()
176+ .whereNotType <DartType >()
177+ .whereNotType <Element >()
178+ .whereNotType <int >()
179+ .whereNotType <Uri >();
180+ if (invalid.isNotEmpty) {
181+ throw ArgumentError ('Tried to format an error using '
182+ '${invalid .map ((e ) => e .runtimeType ).join (', ' )}' );
183+ }
184+ }
185+
172186 contextMessages ?? = [];
173187 contextMessages.addAll (_convertTypeNames (arguments));
174188 _errorListener.onError (
@@ -334,92 +348,76 @@ class ErrorReporter {
334348 );
335349 }
336350
337- /// Convert all [Element] s in the [arguments] into their display strings.
338- void _convertElements (List <Object >? arguments) {
339- if (arguments == null ) {
340- return ;
341- }
342-
343- for (var i = 0 ; i < arguments.length; i++ ) {
344- var argument = arguments[i];
345- if (argument is Element ) {
346- arguments[i] = argument.getDisplayString ();
347- } else if (! (argument is String ||
348- argument is DartType ||
349- argument is int ||
350- argument is Uri )) {
351- throw ArgumentError (
352- 'Tried to format an error using ${argument .runtimeType }' );
353- }
354- }
355- }
356-
357- /// Given an array of [arguments] that is expected to contain two or more
358- /// types, convert the types into strings by using the display names of the
359- /// types, unless there are two or more types with the same names, in which
360- /// case the extended display names of the types will be used in order to
351+ /// Given an array of [arguments] that may contain [DartType] s and [Element] s,
352+ /// converts the types and elements into strings by using the display names of
353+ /// each, unless there are two or more types or elements with the same display
354+ /// names, in which case the extended display names will be used in order to
361355 /// clarify the message.
362356 List <DiagnosticMessage > _convertTypeNames (List <Object ?>? arguments) {
363- var messages = < DiagnosticMessage > [];
364357 if (arguments == null ) {
365- return messages ;
358+ return const [] ;
366359 }
367360
368- Map <String , List <_TypeToConvert >> typeGroups = {};
369- for (int i = 0 ; i < arguments.length; i++ ) {
361+ var typeGroups = < String , List <_ToConvert > > {};
362+ for (var i = 0 ; i < arguments.length; i++ ) {
370363 var argument = arguments[i];
371364 if (argument is TypeImpl ) {
372- String displayName = argument.getDisplayString (
373- preferTypeAlias: true ,
374- );
375- List <_TypeToConvert > types =
376- typeGroups.putIfAbsent (displayName, () => < _TypeToConvert > []);
365+ var displayName = argument.getDisplayString (preferTypeAlias: true );
366+ var types = typeGroups.putIfAbsent (displayName, () => []);
377367 types.add (_TypeToConvert (i, argument, displayName));
368+ } else if (argument is Element ) {
369+ var displayName = argument.getDisplayString ();
370+ var types = typeGroups.putIfAbsent (displayName, () => []);
371+ types.add (_ElementToConvert (i, argument, displayName));
378372 }
379373 }
380- for (List <_TypeToConvert > typeGroup in typeGroups.values) {
374+
375+ var messages = < DiagnosticMessage > [];
376+ for (var typeGroup in typeGroups.values) {
381377 if (typeGroup.length == 1 ) {
382- _TypeToConvert typeToConvert = typeGroup[0 ];
378+ var typeToConvert = typeGroup[0 ];
379+ // If the display name of a type is unambiguous, just replace the type
380+ // in the arguments list with its display name.
383381 arguments[typeToConvert.index] = typeToConvert.displayName;
384- } else {
385- Map < String , Set < Element >> nameToElementMap = {};
386- for ( _TypeToConvert typeToConvert in typeGroup) {
387- for ( Element element in typeToConvert. allElements ()) {
388- Set < Element > elements =
389- nameToElementMap. putIfAbsent (element.name ! , () => < Element > {});
390- elements. add (element);
391- }
382+ continue ;
383+ }
384+
385+ var nameToElementMap = < String , Set < Element > > {};
386+ for ( var typeToConvert in typeGroup) {
387+ for ( var element in typeToConvert.allElements) {
388+ var elements = nameToElementMap. putIfAbsent (element.name ! , () => {} );
389+ elements. add (element);
392390 }
393- for (_TypeToConvert typeToConvert in typeGroup) {
394- // TODO(brianwilkerson): When clients do a better job of displaying
395- // context messages, remove the extra text added to the buffer.
396- StringBuffer ? buffer;
397- for (Element element in typeToConvert.allElements ()) {
398- String name = element.name! ;
399- if (nameToElementMap[name]! .length > 1 ) {
400- if (buffer == null ) {
401- buffer = StringBuffer ();
402- buffer.write ('where ' );
403- } else {
404- buffer.write (', ' );
405- }
406- buffer.write ('$name is defined in ${element .source !.fullName }' );
407- }
408- messages.add (DiagnosticMessageImpl (
409- filePath: element.source! .fullName,
410- length: element.nameLength,
411- message: '$name is defined in ${element .source !.fullName }' ,
412- offset: element.nameOffset,
413- url: null ));
414- }
391+ }
415392
416- if (buffer != null ) {
417- arguments[typeToConvert.index] =
418- '${typeToConvert .displayName } ($buffer )' ;
419- } else {
420- arguments[typeToConvert.index] = typeToConvert.displayName;
393+ for (var typeToConvert in typeGroup) {
394+ // TODO(brianwilkerson): When clients do a better job of displaying
395+ // context messages, remove the extra text added to the buffer.
396+ StringBuffer ? buffer;
397+ for (var element in typeToConvert.allElements) {
398+ var name = element.name! ;
399+ var sourcePath = element.source! .fullName;
400+ if (nameToElementMap[name]! .length > 1 ) {
401+ if (buffer == null ) {
402+ buffer = StringBuffer ();
403+ buffer.write ('where ' );
404+ } else {
405+ buffer.write (', ' );
406+ }
407+ buffer.write ('$name is defined in $sourcePath ' );
421408 }
409+ messages.add (DiagnosticMessageImpl (
410+ filePath: element.source! .fullName,
411+ length: element.nameLength,
412+ message: '$name is defined in $sourcePath ' ,
413+ offset: element.nameOffset,
414+ url: null ,
415+ ));
422416 }
417+
418+ arguments[typeToConvert.index] = buffer != null
419+ ? '${typeToConvert .displayName } ($buffer )'
420+ : typeToConvert.displayName;
423421 }
424422 }
425423 return messages;
@@ -453,6 +451,22 @@ class RecordingErrorListener implements AnalysisErrorListener {
453451 }
454452}
455453
454+ /// Used by [ErrorReporter._convertTypeNames] to keep track of an error argument
455+ /// that is an [Element] , that is being converted to a display string.
456+ class _ElementToConvert implements _ToConvert {
457+ @override
458+ final int index;
459+
460+ @override
461+ final String displayName;
462+
463+ @override
464+ final Iterable <Element > allElements;
465+
466+ _ElementToConvert (this .index, Element element, this .displayName)
467+ : allElements = [element];
468+ }
469+
456470/// An [AnalysisErrorListener] that ignores error.
457471class _NullErrorListener implements AnalysisErrorListener {
458472 @override
@@ -461,42 +475,61 @@ class _NullErrorListener implements AnalysisErrorListener {
461475 }
462476}
463477
464- /// Used by ` ErrorReporter._convertTypeNames` to keep track of a type that is
465- /// being converted.
466- class _TypeToConvert {
467- final int index;
468- final DartType type;
469- final String displayName ;
478+ /// Used by [ ErrorReporter._convertTypeNames] to keep track of an argument that
479+ /// is being converted to a display string .
480+ abstract class _ToConvert {
481+ /// A list of all elements involved in the [DartType] or [Element] 's display
482+ /// string.
483+ Iterable < Element > get allElements ;
470484
471- List <Element >? _allElements;
485+ /// The argument's display string, to replace the argument in the argument
486+ /// list.
487+ String get displayName;
472488
473- _TypeToConvert (this .index, this .type, this .displayName);
489+ /// The index of the argument in the argument list.
490+ int get index;
491+ }
474492
475- List <Element > allElements () {
476- if (_allElements == null ) {
477- Set <Element > elements = < Element > {};
493+ /// Used by [ErrorReporter._convertTypeNames] to keep track of an error argument
494+ /// that is a [DartType] , that is being converted to a display string.
495+ class _TypeToConvert implements _ToConvert {
496+ @override
497+ final int index;
478498
479- void addElementsFrom (DartType type) {
480- if (type is FunctionType ) {
481- addElementsFrom (type.returnType);
482- for (ParameterElement parameter in type.parameters) {
483- addElementsFrom (parameter.type);
484- }
485- } else if (type is InterfaceType ) {
486- if (elements.add (type.element)) {
487- for (DartType typeArgument in type.typeArguments) {
488- addElementsFrom (typeArgument);
489- }
499+ final DartType _type;
500+
501+ @override
502+ final String displayName;
503+
504+ @override
505+ late final Iterable <Element > allElements = () {
506+ var elements = < Element > {};
507+
508+ void addElementsFrom (DartType type) {
509+ if (type is FunctionType ) {
510+ addElementsFrom (type.returnType);
511+ for (var parameter in type.parameters) {
512+ addElementsFrom (parameter.type);
513+ }
514+ } else if (type is RecordType ) {
515+ for (var parameter in type.fields) {
516+ addElementsFrom (parameter.type);
517+ }
518+ } else if (type is InterfaceType ) {
519+ if (elements.add (type.element)) {
520+ for (var typeArgument in type.typeArguments) {
521+ addElementsFrom (typeArgument);
490522 }
491523 }
492524 }
493-
494- addElementsFrom (type);
495- _allElements = elements.where ((element) {
496- var name = element.name;
497- return name != null && name.isNotEmpty;
498- }).toList ();
499525 }
500- return _allElements! ;
501- }
526+
527+ addElementsFrom (_type);
528+ return elements.where ((element) {
529+ var name = element.name;
530+ return name != null && name.isNotEmpty;
531+ });
532+ }();
533+
534+ _TypeToConvert (this .index, this ._type, this .displayName);
502535}
0 commit comments