@@ -14,6 +14,7 @@ import 'package:analyzer/source/source.dart';
1414import 'package:analyzer/source/source_range.dart' ;
1515import 'package:analyzer/src/dart/ast/ast.dart' ;
1616import 'package:analyzer/src/dart/ast/extensions.dart' ;
17+ import 'package:analyzer/src/dart/element/element.dart' ;
1718import 'package:analyzer/src/dart/element/extensions.dart' ;
1819import 'package:analyzer/src/dart/element/type.dart' ;
1920import 'package:analyzer/src/diagnostic/diagnostic_message.dart' ;
@@ -80,9 +81,12 @@ List<DiagnosticMessage> convertTypeNames(
8081 // context messages, remove the extra text added to the buffer.
8182 StringBuffer ? buffer;
8283 for (var element in typeToConvert.allElements) {
83- var name = element.name;
84- name ?? = element is ExtensionElement ? unnamedExtension : unnamed;
85- var sourcePath = element.firstFragment.libraryFragment! .source.fullName;
84+ var nonSynthetic = element.nonSynthetic;
85+ var name = nonSynthetic.name;
86+ name ?? = nonSynthetic is ExtensionElement ? unnamedExtension : unnamed;
87+
88+ var location = _DiagnosticLocation .forElement (nonSynthetic);
89+ var sourcePath = location.sourcePath! ;
8690 if (nameToElementMap[name]! .length > 1 ) {
8791 if (buffer == null ) {
8892 buffer = StringBuffer ();
@@ -95,9 +99,9 @@ List<DiagnosticMessage> convertTypeNames(
9599 messages.add (
96100 DiagnosticMessageImpl (
97101 filePath: sourcePath,
98- length: element.name? .length ?? 0 ,
99102 message: '$name is defined in $sourcePath ' ,
100- offset: element.firstFragment.nameOffset ?? - 1 ,
103+ offset: location.offset,
104+ length: location.length,
101105 url: null ,
102106 ),
103107 );
@@ -203,10 +207,11 @@ class DiagnosticReporter {
203207 List <DiagnosticMessage >? contextMessages,
204208 }) {
205209 var nonSynthetic = element.nonSynthetic;
210+ var location = _DiagnosticLocation .forElement (nonSynthetic);
206211 return atOffset (
207212 diagnosticCode: diagnosticCode,
208- offset: nonSynthetic.firstFragment.nameOffset ?? - 1 ,
209- length: nonSynthetic.name ? . length ?? 0 ,
213+ offset: location.offset ,
214+ length: location. length,
210215 arguments: arguments,
211216 contextMessages: contextMessages,
212217 );
@@ -391,6 +396,49 @@ class DiagnosticReporter {
391396 }
392397}
393398
399+ /// The location to report a diagnostic at.
400+ class _DiagnosticLocation {
401+ final int offset;
402+ final int length;
403+ final String ? sourcePath;
404+
405+ _DiagnosticLocation (
406+ this .sourcePath, {
407+ required this .offset,
408+ required this .length,
409+ });
410+
411+ /// Computes the diagnostic location for a given element.
412+ factory _DiagnosticLocation .forElement (Element element) {
413+ var location = (element.nonSynthetic as ElementImpl ).firstFragmentLocation;
414+
415+ var sourcePath = location.libraryFragment? .source.fullName;
416+ var nameOffset = location.nameOffset;
417+ var firstTokenOffset = location.firstTokenOffset;
418+
419+ int offset, length;
420+ if (nameOffset != null ) {
421+ // Has name, use offset and length.
422+ offset = nameOffset;
423+ length = location.name? .length ?? 0 ;
424+ } else if (firstTokenOffset != null ) {
425+ // No name, use zero-length offset at the first token,
426+ offset = firstTokenOffset;
427+ length = 0 ;
428+ } else {
429+ // We don't have any valid location to use. We currently use -1 as an
430+ // indicator of this. We could consider using 0 (so the location is at
431+ // least valid, even if not specific), but it might make it more difficult
432+ // to track down bugs (for example the LSP mapping code has an assert that
433+ // would flag this if it happened during a test/assert-enabled run).
434+ offset = - 1 ;
435+ length = 0 ;
436+ }
437+
438+ return _DiagnosticLocation (sourcePath, offset: offset, length: length);
439+ }
440+ }
441+
394442/// Used by [convertTypeNames] to keep track of an error argument that is an
395443/// [Element] , that is being converted to a display string.
396444class _ElementToConvert implements _ToConvert {
0 commit comments