@@ -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,82 @@ 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+ const unnamedExtension = '<unnamed extension>' ;
386+ const unnamed = '<unnamed>' ;
387+ var nameToElementMap = < String , Set <Element >> {};
388+ for (var typeToConvert in typeGroup) {
389+ for (var element in typeToConvert.allElements) {
390+ var name = element.name;
391+ name ?? = element is ExtensionElement ? unnamedExtension : unnamed;
392+
393+ var elements = nameToElementMap.putIfAbsent (name, () => {});
394+ elements.add (element);
392395 }
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- }
396+ }
415397
416- if (buffer != null ) {
417- arguments[typeToConvert.index] =
418- '${typeToConvert .displayName } ($buffer )' ;
419- } else {
420- arguments[typeToConvert.index] = typeToConvert.displayName;
398+ for (var typeToConvert in typeGroup) {
399+ // TODO(brianwilkerson): When clients do a better job of displaying
400+ // context messages, remove the extra text added to the buffer.
401+ StringBuffer ? buffer;
402+ for (var element in typeToConvert.allElements) {
403+ var name = element.name;
404+ name ?? = element is ExtensionElement ? unnamedExtension : unnamed;
405+ var sourcePath = element.source! .fullName;
406+ if (nameToElementMap[name]! .length > 1 ) {
407+ if (buffer == null ) {
408+ buffer = StringBuffer ();
409+ buffer.write ('where ' );
410+ } else {
411+ buffer.write (', ' );
412+ }
413+ buffer.write ('$name is defined in $sourcePath ' );
421414 }
415+ messages.add (DiagnosticMessageImpl (
416+ filePath: element.source! .fullName,
417+ length: element.nameLength,
418+ message: '$name is defined in $sourcePath ' ,
419+ offset: element.nameOffset,
420+ url: null ,
421+ ));
422422 }
423+
424+ arguments[typeToConvert.index] = buffer != null
425+ ? '${typeToConvert .displayName } ($buffer )'
426+ : typeToConvert.displayName;
423427 }
424428 }
425429 return messages;
@@ -453,6 +457,22 @@ class RecordingErrorListener implements AnalysisErrorListener {
453457 }
454458}
455459
460+ /// Used by [ErrorReporter._convertTypeNames] to keep track of an error argument
461+ /// that is an [Element] , that is being converted to a display string.
462+ class _ElementToConvert implements _ToConvert {
463+ @override
464+ final int index;
465+
466+ @override
467+ final String displayName;
468+
469+ @override
470+ final Iterable <Element > allElements;
471+
472+ _ElementToConvert (this .index, Element element, this .displayName)
473+ : allElements = [element];
474+ }
475+
456476/// An [AnalysisErrorListener] that ignores error.
457477class _NullErrorListener implements AnalysisErrorListener {
458478 @override
@@ -461,42 +481,61 @@ class _NullErrorListener implements AnalysisErrorListener {
461481 }
462482}
463483
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 ;
484+ /// Used by [ ErrorReporter._convertTypeNames] to keep track of an argument that
485+ /// is being converted to a display string .
486+ abstract class _ToConvert {
487+ /// A list of all elements involved in the [DartType] or [Element] 's display
488+ /// string.
489+ Iterable < Element > get allElements ;
470490
471- List <Element >? _allElements;
491+ /// The argument's display string, to replace the argument in the argument
492+ /// list.
493+ String get displayName;
472494
473- _TypeToConvert (this .index, this .type, this .displayName);
495+ /// The index of the argument in the argument list.
496+ int get index;
497+ }
474498
475- List <Element > allElements () {
476- if (_allElements == null ) {
477- Set <Element > elements = < Element > {};
499+ /// Used by [ErrorReporter._convertTypeNames] to keep track of an error argument
500+ /// that is a [DartType] , that is being converted to a display string.
501+ class _TypeToConvert implements _ToConvert {
502+ @override
503+ final int index;
478504
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- }
505+ final DartType _type;
506+
507+ @override
508+ final String displayName;
509+
510+ @override
511+ late final Iterable <Element > allElements = () {
512+ var elements = < Element > {};
513+
514+ void addElementsFrom (DartType type) {
515+ if (type is FunctionType ) {
516+ addElementsFrom (type.returnType);
517+ for (var parameter in type.parameters) {
518+ addElementsFrom (parameter.type);
519+ }
520+ } else if (type is RecordType ) {
521+ for (var parameter in type.fields) {
522+ addElementsFrom (parameter.type);
523+ }
524+ } else if (type is InterfaceType ) {
525+ if (elements.add (type.element)) {
526+ for (var typeArgument in type.typeArguments) {
527+ addElementsFrom (typeArgument);
490528 }
491529 }
492530 }
493-
494- addElementsFrom (type);
495- _allElements = elements.where ((element) {
496- var name = element.name;
497- return name != null && name.isNotEmpty;
498- }).toList ();
499531 }
500- return _allElements! ;
501- }
532+
533+ addElementsFrom (_type);
534+ return elements.where ((element) {
535+ var name = element.name;
536+ return name != null && name.isNotEmpty;
537+ });
538+ }();
539+
540+ _TypeToConvert (this .index, this ._type, this .displayName);
502541}
0 commit comments