1309dffc0a
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>
180 lines
5.8 KiB
Dart
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 }
|