// 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/error/listener.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'; import 'package:analyzer/src/generated/source.dart'; export 'package:_fe_analyzer_shared/src/base/errors.dart' show ErrorCode, ErrorSeverity, ErrorType; export 'package:analyzer/src/error/error_code_values.g.dart'; /// The lazy initialized map from [ErrorCode.uniqueName] to the [ErrorCode] /// instance. final HashMap _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 _computeUniqueNameToCodeMap() { var result = HashMap(); 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. static const List NO_ERRORS = []; /// A [Comparator] that sorts by the name of the file that the [AnalysisError] /// was found. static Comparator 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. static Comparator 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 _contextMessages; /// 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. AnalysisError(this.source, int offset, int length, this.errorCode, [List? arguments, List contextMessages = const []]) : _contextMessages = contextMessages { assert( (arguments ?? const []).length == errorCode.numParameters, 'Message $errorCode requires ${errorCode.numParameters} ' 'argument${errorCode.numParameters == 1 ? '' : 's'}, but ' '${(arguments ?? const []).length} ' 'argument${(arguments ?? const []).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); } /// Initialize a newly created analysis error with given values. AnalysisError.forValues(this.source, int offset, int length, this.errorCode, String message, this._correctionMessage, {List contextMessages = const []}) : _contextMessages = contextMessages { _problemMessage = DiagnosticMessageImpl( filePath: source.fullName, length: length, message: message, offset: offset, url: null); } @override List 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. static List mergeLists(List> errorLists) { Set errors = HashSet(); for (List errorList in errorLists) { errors.addAll(errorList); } return errors.toList(); } }