Files
sdk/pkg/analyzer/lib/diagnostic/diagnostic.dart
T
Konstantin Shcheglov 1309dffc0a CQ. Move analyzer diagnostics back into analyzer.
Move the analyzer-only Diagnostic, DiagnosticMessage, Severity, and
locatable diagnostic helper types out of _fe_analyzer_shared and into
package:analyzer.

I paln to make changes outlined in
https://github.com/dart-lang/sdk/issues/63311 and chat discussion.
Keeping these classes in the analyzer simplifies the migration and
avoids introducing a shared abstraction before there is a concrete need
for one.

If we decide later need to have a shared abstraction, we can always
extract one at that point. With coding agents internal code motion is
cheap.

Update analyzer, analysis server plugin, analyzer plugin, linter, and
scanner call sites to import the moved APIs from analyzer libraries, and
refresh API baselines to reflect the new public owner.

Change-Id: Ie0ef0f01c6e4be7ebaac25619ac3e3fe991a44d9
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/501000
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Reviewed-by: Paul Berry <paulberry@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
2026-05-07 13:54:59 -07:00

180 lines
5.8 KiB
Dart

// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:_fe_analyzer_shared/src/base/errors.dart';
import 'package:analyzer/source/source.dart';
import 'package:analyzer/src/diagnostic/diagnostic.dart'
show DiagnosticMessage, DiagnosticMessageImpl;
export 'package:analyzer/src/diagnostic/diagnostic.dart' show DiagnosticMessage;
/// A diagnostic, as defined by the [Diagnostic Design Guidelines][guidelines]:
///
/// > An indication of a specific problem at a specific location within the
/// > source code being processed by a development tool.
///
/// Clients may not extend, implement or mix-in this class.
///
/// [guidelines]: https://github.com/dart-lang/sdk/blob/main/pkg/analyzer/doc/implementation/diagnostics.md
class Diagnostic {
/// The diagnostic code associated with the diagnostic.
final DiagnosticCode diagnosticCode;
/// A list of messages that provide context for understanding the problem
/// being reported. The list will be empty if there are no such messages.
final List<DiagnosticMessage> contextMessages;
/// A description of how to fix the problem, or `null` if there is no such
/// description.
final String? correctionMessage;
/// A message describing what is wrong and why.
final DiagnosticMessage problemMessage;
/// The source in which the diagnostic occurred, or `null` if unknown.
final Source source;
Diagnostic.forValues({
required this.source,
required int offset,
required int length,
required this.diagnosticCode,
required String message,
this.correctionMessage,
this.contextMessages = const [],
}) : problemMessage = DiagnosticMessageImpl(
filePath: source.fullName,
length: length,
message: message,
offset: offset,
url: null,
);
/// Initialize a newly created diagnostic.
///
/// The diagnostic is associated with the given [source] and is located at the
/// given [offset] with the given [length]. The diagnostic will have the given
/// [diagnosticCode] and the list of [arguments] will be used to complete the
/// message and correction. If any [contextMessages] are provided, they will
/// be recorded with the diagnostic.
factory Diagnostic.tmp({
required Source source,
required int offset,
required int length,
required DiagnosticCode diagnosticCode,
List<Object?> arguments = const [],
List<DiagnosticMessage> contextMessages = const [],
}) {
assert(
arguments.length == diagnosticCode.numParameters,
'Message $diagnosticCode requires ${diagnosticCode.numParameters} '
'argument${diagnosticCode.numParameters == 1 ? '' : 's'}, but '
'${arguments.length} '
'argument${arguments.length == 1 ? ' was' : 's were'} '
'provided',
);
String message = formatList(diagnosticCode.problemMessage, arguments);
String? correctionTemplate = diagnosticCode.correctionMessage;
String? correctionMessage;
if (correctionTemplate != null) {
correctionMessage = formatList(correctionTemplate, arguments);
}
return Diagnostic.forValues(
source: source,
offset: offset,
length: length,
diagnosticCode: diagnosticCode,
message: message,
correctionMessage: correctionMessage,
contextMessages: contextMessages,
);
}
/// The template used to create the correction to be displayed for this
/// diagnostic, or `null` if there is no correction information for this
/// error. The correction should indicate how the user can fix the error.
@Deprecated("Use 'correctionMessage' instead.")
String? get correction => correctionMessage;
@Deprecated("Use 'diagnosticCode' instead")
DiagnosticCode get errorCode => diagnosticCode;
@override
int get hashCode {
int hashCode = offset;
hashCode ^= message.hashCode;
hashCode ^= source.hashCode;
return hashCode;
}
/// The number of characters from the offset to the end of the source which
/// encompasses the compilation error.
int get length => problemMessage.length;
/// The message to be displayed for this diagnostic.
///
/// The message indicates what is wrong and why it is wrong.
String get message => problemMessage.messageText(includeUrl: true);
/// The character offset from the beginning of the source (zero based) where
/// the diagnostic occurred.
int get offset => problemMessage.offset;
Severity get severity {
switch (diagnosticCode.severity) {
case DiagnosticSeverity.ERROR:
return Severity.error;
case DiagnosticSeverity.WARNING:
return Severity.warning;
case DiagnosticSeverity.INFO:
return Severity.info;
default:
throw StateError('Invalid severity: ${diagnosticCode.severity}');
}
}
@override
bool operator ==(Object other) {
if (identical(other, this)) {
return true;
}
// prepare the other Diagnostic.
if (other is Diagnostic) {
// Quick checks.
if (!identical(diagnosticCode, other.diagnosticCode)) {
return false;
}
if (offset != other.offset || length != other.length) {
return false;
}
// Deep checks.
if (message != other.message) {
return false;
}
if (source != other.source) {
return false;
}
return true;
}
return false;
}
@override
String toString() {
StringBuffer buffer = StringBuffer();
buffer.write(source.fullName);
buffer.write("(");
buffer.write(offset);
buffer.write("..");
buffer.write(offset + length - 1);
buffer.write("): ");
buffer.write(message);
return buffer.toString();
}
}
/// An indication of the severity of a [Diagnostic].
enum Severity { error, warning, info }