0e39ed67e6
Work towards https://github.com/dart-lang/sdk/issues/50986 Cq-Include-Trybots: luci.dart.try:flutter-analyze-try,analyzer-win-release-try,pkg-win-release-try Change-Id: Ie5783240e3fc2c9f8076b3efb13f79a3b8fa5000 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/375060 Reviewed-by: Phil Quitslund <pquitslund@google.com> Reviewed-by: Brian Wilkerson <brianwilkerson@google.com> Commit-Queue: Samuel Rawlins <srawlins@google.com>
283 lines
9.3 KiB
Dart
283 lines
9.3 KiB
Dart
// Copyright (c) 2014, 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 'dart:collection';
|
|
|
|
import 'package:_fe_analyzer_shared/src/base/errors.dart';
|
|
import 'package:analyzer/diagnostic/diagnostic.dart';
|
|
import 'package:analyzer/source/source.dart';
|
|
import 'package:analyzer/src/diagnostic/diagnostic.dart';
|
|
import 'package:analyzer/src/error/error_code_values.g.dart';
|
|
import 'package:analyzer/src/generated/java_core.dart';
|
|
|
|
export 'package:_fe_analyzer_shared/src/base/errors.dart'
|
|
show ErrorCode, ErrorSeverity, ErrorType;
|
|
export 'package:analyzer/src/dart/error/lint_codes.dart' show LintCode;
|
|
export 'package:analyzer/src/error/error_code_values.g.dart';
|
|
|
|
/// The lazy initialized map from [ErrorCode.uniqueName] to the [ErrorCode]
|
|
/// instance.
|
|
final HashMap<String, ErrorCode> _uniqueNameToCodeMap =
|
|
_computeUniqueNameToCodeMap();
|
|
|
|
/// Return the [ErrorCode] with the given [uniqueName], or `null` if not
|
|
/// found.
|
|
ErrorCode? errorCodeByUniqueName(String uniqueName) {
|
|
return _uniqueNameToCodeMap[uniqueName];
|
|
}
|
|
|
|
/// Return the map from [ErrorCode.uniqueName] to the [ErrorCode] instance
|
|
/// for all [errorCodeValues].
|
|
HashMap<String, ErrorCode> _computeUniqueNameToCodeMap() {
|
|
var result = HashMap<String, ErrorCode>();
|
|
for (ErrorCode errorCode in errorCodeValues) {
|
|
var uniqueName = errorCode.uniqueName;
|
|
assert(() {
|
|
if (result.containsKey(uniqueName)) {
|
|
throw StateError('Not unique: $uniqueName');
|
|
}
|
|
return true;
|
|
}());
|
|
result[uniqueName] = errorCode;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/// An error discovered during the analysis of some Dart code.
|
|
///
|
|
/// See `AnalysisErrorListener`.
|
|
class AnalysisError implements Diagnostic {
|
|
/// An empty array of errors used when no errors are expected.
|
|
@Deprecated('Not used')
|
|
static const List<AnalysisError> NO_ERRORS = <AnalysisError>[];
|
|
|
|
/// A [Comparator] that sorts by the name of the file that the [AnalysisError]
|
|
/// was found.
|
|
@Deprecated('Not used')
|
|
static Comparator<AnalysisError> FILE_COMPARATOR =
|
|
(AnalysisError o1, AnalysisError o2) =>
|
|
o1.source.shortName.compareTo(o2.source.shortName);
|
|
|
|
/// A [Comparator] that sorts error codes first by their severity (errors
|
|
/// first, warnings second), and then by the error code type.
|
|
@Deprecated('Not used')
|
|
static Comparator<AnalysisError> ERROR_CODE_COMPARATOR =
|
|
(AnalysisError o1, AnalysisError o2) {
|
|
ErrorCode errorCode1 = o1.errorCode;
|
|
ErrorCode errorCode2 = o2.errorCode;
|
|
ErrorSeverity errorSeverity1 = errorCode1.errorSeverity;
|
|
ErrorSeverity errorSeverity2 = errorCode2.errorSeverity;
|
|
if (errorSeverity1 == errorSeverity2) {
|
|
ErrorType errorType1 = errorCode1.type;
|
|
ErrorType errorType2 = errorCode2.type;
|
|
return errorType1.compareTo(errorType2);
|
|
} else {
|
|
return errorSeverity2.compareTo(errorSeverity1);
|
|
}
|
|
};
|
|
|
|
/// The error code associated with the error.
|
|
final ErrorCode errorCode;
|
|
|
|
/// The message describing the problem.
|
|
late final DiagnosticMessage _problemMessage;
|
|
|
|
/// The context messages associated with the problem. This list will be empty
|
|
/// if there are no context messages.
|
|
final List<DiagnosticMessage> _contextMessages;
|
|
|
|
/// Data associated with this error, specific for [errorCode].
|
|
final Object? data;
|
|
|
|
/// The correction to be displayed for this error, or `null` if there is no
|
|
/// correction information for this error.
|
|
String? _correctionMessage;
|
|
|
|
/// The source in which the error occurred, or `null` if unknown.
|
|
final Source source;
|
|
|
|
/// Initialize a newly created analysis error. The error is associated with
|
|
/// the given [source] and is located at the given [offset] with the given
|
|
/// [length]. The error will have the given [errorCode] 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 error.
|
|
@Deprecated('Use tmp constructor instead')
|
|
factory AnalysisError(
|
|
Source source,
|
|
int offset,
|
|
int length,
|
|
ErrorCode errorCode, [
|
|
List<Object?>? arguments,
|
|
List<DiagnosticMessage> contextMessages = const [],
|
|
Object? data,
|
|
]) {
|
|
return AnalysisError.tmp(
|
|
source: source,
|
|
offset: offset,
|
|
length: length,
|
|
errorCode: errorCode,
|
|
arguments: arguments ?? const [],
|
|
contextMessages: contextMessages,
|
|
data: data,
|
|
);
|
|
}
|
|
|
|
/// Initialize a newly created analysis error with given values.
|
|
AnalysisError.forValues({
|
|
required this.source,
|
|
required int offset,
|
|
required int length,
|
|
required this.errorCode,
|
|
required String message,
|
|
String? correctionMessage,
|
|
List<DiagnosticMessage> contextMessages = const [],
|
|
this.data,
|
|
}) : _correctionMessage = correctionMessage,
|
|
_contextMessages = contextMessages {
|
|
_problemMessage = DiagnosticMessageImpl(
|
|
filePath: source.fullName,
|
|
length: length,
|
|
message: message,
|
|
offset: offset,
|
|
url: null,
|
|
);
|
|
}
|
|
|
|
/// Initialize a newly created analysis error. The error is associated with
|
|
/// the given [source] and is located at the given [offset] with the given
|
|
/// [length]. The error will have the given [errorCode] 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 error.
|
|
AnalysisError.tmp({
|
|
required this.source,
|
|
required int offset,
|
|
required int length,
|
|
required this.errorCode,
|
|
List<Object?> arguments = const [],
|
|
List<DiagnosticMessage> contextMessages = const [],
|
|
this.data,
|
|
}) : _contextMessages = contextMessages {
|
|
assert(
|
|
arguments.length == errorCode.numParameters,
|
|
'Message $errorCode requires ${errorCode.numParameters} '
|
|
'argument${errorCode.numParameters == 1 ? '' : 's'}, but '
|
|
'${arguments.length} '
|
|
'argument${arguments.length == 1 ? ' was' : 's were'} '
|
|
'provided',
|
|
);
|
|
String problemMessage = formatList(errorCode.problemMessage, arguments);
|
|
String? correctionTemplate = errorCode.correctionMessage;
|
|
if (correctionTemplate != null) {
|
|
_correctionMessage = formatList(correctionTemplate, arguments);
|
|
}
|
|
_problemMessage = DiagnosticMessageImpl(
|
|
filePath: source.fullName,
|
|
length: length,
|
|
message: problemMessage,
|
|
offset: offset,
|
|
url: null,
|
|
);
|
|
}
|
|
|
|
@override
|
|
List<DiagnosticMessage> get contextMessages => _contextMessages;
|
|
|
|
/// Return the template used to create the correction to be displayed for this
|
|
/// error, or `null` if there is no correction information for this error. The
|
|
/// correction should indicate how the user can fix the error.
|
|
String? get correction => _correctionMessage;
|
|
|
|
@override
|
|
String? get correctionMessage => _correctionMessage;
|
|
|
|
@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;
|
|
|
|
/// Return the message to be displayed for this error. The message should
|
|
/// indicate 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 error occurred.
|
|
int get offset => _problemMessage.offset;
|
|
|
|
@override
|
|
DiagnosticMessage get problemMessage => _problemMessage;
|
|
|
|
@override
|
|
Severity get severity {
|
|
switch (errorCode.errorSeverity) {
|
|
case ErrorSeverity.ERROR:
|
|
return Severity.error;
|
|
case ErrorSeverity.WARNING:
|
|
return Severity.warning;
|
|
case ErrorSeverity.INFO:
|
|
return Severity.info;
|
|
default:
|
|
throw StateError('Invalid error severity: ${errorCode.errorSeverity}');
|
|
}
|
|
}
|
|
|
|
@override
|
|
bool operator ==(Object other) {
|
|
if (identical(other, this)) {
|
|
return true;
|
|
}
|
|
// prepare other AnalysisError
|
|
if (other is AnalysisError) {
|
|
// Quick checks.
|
|
if (!identical(errorCode, other.errorCode)) {
|
|
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;
|
|
}
|
|
// OK
|
|
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("(" + lineNumber + ":" + columnNumber + "): ");
|
|
buffer.write(message);
|
|
return buffer.toString();
|
|
}
|
|
|
|
/// Merge all of the errors in the lists in the given list of [errorLists]
|
|
/// into a single list of errors.
|
|
@Deprecated('Not used')
|
|
static List<AnalysisError> mergeLists(List<List<AnalysisError>> errorLists) {
|
|
Set<AnalysisError> errors = HashSet<AnalysisError>();
|
|
for (List<AnalysisError> errorList in errorLists) {
|
|
errors.addAll(errorList);
|
|
}
|
|
return errors.toList();
|
|
}
|
|
}
|