blob: 28260d6a40ca87e86c752cd8246532cdef0bd2f8 [file] [log] [blame]
// 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 {
/// 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 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();
}
}