Index: pkg/analyzer/lib/error/listener.dart |
diff --git a/pkg/analyzer/lib/error/listener.dart b/pkg/analyzer/lib/error/listener.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..1f75fcb1b1f2366688cb53b5f61b1e00c3517311 |
--- /dev/null |
+++ b/pkg/analyzer/lib/error/listener.dart |
@@ -0,0 +1,322 @@ |
+// 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. |
+ |
+library analyzer.error.listener; |
+ |
+import 'dart:collection'; |
+ |
+import 'package:analyzer/dart/ast/ast.dart' show AstNode; |
+import 'package:analyzer/dart/ast/token.dart'; |
+import 'package:analyzer/dart/element/element.dart'; |
+import 'package:analyzer/dart/element/type.dart'; |
+import 'package:analyzer/error/error.dart'; |
+import 'package:analyzer/src/dart/element/type.dart'; |
+import 'package:analyzer/src/generated/source.dart'; |
+import 'package:source_span/source_span.dart'; |
+ |
+/** |
+ * An object that listen for [AnalysisError]s being produced by the analysis |
+ * engine. |
+ */ |
+abstract class AnalysisErrorListener { |
+ /** |
+ * An error listener that ignores errors that are reported to it. |
+ */ |
+ static final AnalysisErrorListener NULL_LISTENER = new _NullErrorListener(); |
+ |
+ /** |
+ * This method is invoked when an [error] has been found by the analysis |
+ * engine. |
+ */ |
+ void onError(AnalysisError error); |
+} |
+ |
+/** |
+ * An [AnalysisErrorListener] that keeps track of whether any error has been |
+ * reported to it. |
+ */ |
+class BooleanErrorListener implements AnalysisErrorListener { |
+ /** |
+ * A flag indicating whether an error has been reported to this listener. |
+ */ |
+ bool _errorReported = false; |
+ |
+ /** |
+ * Return `true` if an error has been reported to this listener. |
+ */ |
+ bool get errorReported => _errorReported; |
+ |
+ @override |
+ void onError(AnalysisError error) { |
+ _errorReported = true; |
+ } |
+} |
+ |
+/** |
+ * An object used to create analysis errors and report then to an error |
+ * listener. |
+ */ |
+class ErrorReporter { |
+ /** |
+ * The error listener to which errors will be reported. |
+ */ |
+ final AnalysisErrorListener _errorListener; |
+ |
+ /** |
+ * The default source to be used when reporting errors. |
+ */ |
+ final Source _defaultSource; |
+ |
+ /** |
+ * The source to be used when reporting errors. |
+ */ |
+ Source _source; |
+ |
+ /** |
+ * Initialize a newly created error reporter that will report errors to the |
+ * given [_errorListener]. Errors will be reported against the |
+ * [_defaultSource] unless another source is provided later. |
+ */ |
+ ErrorReporter(this._errorListener, this._defaultSource) { |
+ if (_errorListener == null) { |
+ throw new ArgumentError("An error listener must be provided"); |
+ } else if (_defaultSource == null) { |
+ throw new ArgumentError("A default source must be provided"); |
+ } |
+ this._source = _defaultSource; |
+ } |
+ |
+ Source get source => _source; |
+ |
+ /** |
+ * Set the source to be used when reporting errors to the given [source]. |
+ * Setting the source to `null` will cause the default source to be used. |
+ */ |
+ void set source(Source source) { |
+ this._source = source ?? _defaultSource; |
+ } |
+ |
+ /** |
+ * Creates an error with properties with the given [errorCode] and |
+ * [arguments]. The [node] is used to compute the location of the error. |
+ */ |
+ AnalysisErrorWithProperties newErrorWithProperties( |
+ ErrorCode errorCode, AstNode node, List<Object> arguments) => |
+ new AnalysisErrorWithProperties( |
+ _source, node.offset, node.length, errorCode, arguments); |
+ |
+ /** |
+ * Report the given [error]. |
+ */ |
+ void reportError(AnalysisError error) { |
+ _errorListener.onError(error); |
+ } |
+ |
+ /** |
+ * Report an error with the given [errorCode] and [arguments]. The [element] |
+ * is used to compute the location of the error. |
+ */ |
+ void reportErrorForElement(ErrorCode errorCode, Element element, |
+ [List<Object> arguments]) { |
+ int length = 0; |
+ if (element is ImportElement) { |
+ length = 6; // 'import'.length |
+ } else if (element is ExportElement) { |
+ length = 6; // 'export'.length |
+ } else { |
+ length = element.nameLength; |
+ } |
+ reportErrorForOffset(errorCode, element.nameOffset, length, arguments); |
+ } |
+ |
+ /** |
+ * Report an error with the given [errorCode] and [arguments]. |
+ * The [node] is used to compute the location of the error. |
+ * |
+ * If the arguments contain the names of two or more types, the method |
+ * [reportTypeErrorForNode] should be used and the types |
+ * themselves (rather than their names) should be passed as arguments. |
+ */ |
+ void reportErrorForNode(ErrorCode errorCode, AstNode node, |
+ [List<Object> arguments]) { |
+ reportErrorForOffset(errorCode, node.offset, node.length, arguments); |
+ } |
+ |
+ /** |
+ * Report an error with the given [errorCode] and [arguments]. The location of |
+ * the error is specified by the given [offset] and [length]. |
+ */ |
+ void reportErrorForOffset(ErrorCode errorCode, int offset, int length, |
+ [List<Object> arguments]) { |
+ _errorListener.onError( |
+ new AnalysisError(_source, offset, length, errorCode, arguments)); |
+ } |
+ |
+ /** |
+ * Report an error with the given [errorCode] and [arguments]. The location of |
+ * the error is specified by the given [span]. |
+ */ |
+ void reportErrorForSpan(ErrorCode errorCode, SourceSpan span, |
+ [List<Object> arguments]) { |
+ reportErrorForOffset(errorCode, span.start.offset, span.length, arguments); |
+ } |
+ |
+ /** |
+ * Report an error with the given [errorCode] and [arguments]. The [token] is |
+ * used to compute the location of the error. |
+ */ |
+ void reportErrorForToken(ErrorCode errorCode, Token token, |
+ [List<Object> arguments]) { |
+ reportErrorForOffset(errorCode, token.offset, token.length, arguments); |
+ } |
+ |
+ /** |
+ * Report an error with the given [errorCode] and [arguments]. The [node] is |
+ * used to compute the location of the error. The arguments are expected to |
+ * contain two or more types. Convert the types into strings by using the |
+ * display names of the types, unless there are two or more types with the |
+ * same names, in which case the extended display names of the types will be |
+ * used in order to clarify the message. |
+ * |
+ * If there are not two or more types in the argument list, the method |
+ * [reportErrorForNode] should be used instead. |
+ */ |
+ void reportTypeErrorForNode( |
+ ErrorCode errorCode, AstNode node, List<Object> arguments) { |
+ _convertTypeNames(arguments); |
+ reportErrorForOffset(errorCode, node.offset, node.length, arguments); |
+ } |
+ |
+ /** |
+ * Given an array of [arguments] that is expected to contain two or more |
+ * types, convert the types into strings by using the display names of the |
+ * types, unless there are two or more types with the same names, in which |
+ * case the extended display names of the types will be used in order to |
+ * clarify the message. |
+ */ |
+ void _convertTypeNames(List<Object> arguments) { |
+ String displayName(DartType type) { |
+ if (type is FunctionType) { |
+ String name = type.name; |
+ if (name != null && name.length > 0) { |
+ StringBuffer buffer = new StringBuffer(); |
+ buffer.write(name); |
+ (type as TypeImpl).appendTo(buffer); |
+ return buffer.toString(); |
+ } |
+ } |
+ return type.displayName; |
+ } |
+ |
+ if (_hasEqualTypeNames(arguments)) { |
+ int count = arguments.length; |
+ for (int i = 0; i < count; i++) { |
+ Object argument = arguments[i]; |
+ if (argument is DartType) { |
+ Element element = argument.element; |
+ if (element == null) { |
+ arguments[i] = displayName(argument); |
+ } else { |
+ arguments[i] = |
+ element.getExtendedDisplayName(displayName(argument)); |
+ } |
+ } |
+ } |
+ } else { |
+ int count = arguments.length; |
+ for (int i = 0; i < count; i++) { |
+ Object argument = arguments[i]; |
+ if (argument is DartType) { |
+ arguments[i] = displayName(argument); |
+ } |
+ } |
+ } |
+ } |
+ |
+ /** |
+ * Return `true` if the given array of [arguments] contains two or more types |
+ * with the same display name. |
+ */ |
+ bool _hasEqualTypeNames(List<Object> arguments) { |
+ int count = arguments.length; |
+ HashSet<String> typeNames = new HashSet<String>(); |
+ for (int i = 0; i < count; i++) { |
+ Object argument = arguments[i]; |
+ if (argument is DartType && !typeNames.add(argument.displayName)) { |
+ return true; |
+ } |
+ } |
+ return false; |
+ } |
+} |
+ |
+/** |
+ * An error listener that will record the errors that are reported to it in a |
+ * way that is appropriate for caching those errors within an analysis context. |
+ */ |
+class RecordingErrorListener implements AnalysisErrorListener { |
+ /** |
+ * A map of sets containing the errors that were collected, keyed by each |
+ * source. |
+ */ |
+ Map<Source, HashSet<AnalysisError>> _errors = |
+ new HashMap<Source, HashSet<AnalysisError>>(); |
+ |
+ /** |
+ * Return the errors collected by the listener. |
+ */ |
+ List<AnalysisError> get errors { |
+ int numEntries = _errors.length; |
+ if (numEntries == 0) { |
+ return AnalysisError.NO_ERRORS; |
+ } |
+ List<AnalysisError> resultList = new List<AnalysisError>(); |
+ for (HashSet<AnalysisError> errors in _errors.values) { |
+ resultList.addAll(errors); |
+ } |
+ return resultList; |
+ } |
+ |
+ /** |
+ * Add all of the errors recorded by the given [listener] to this listener. |
+ */ |
+ void addAll(RecordingErrorListener listener) { |
+ for (AnalysisError error in listener.errors) { |
+ onError(error); |
+ } |
+ } |
+ |
+ /** |
+ * Return the errors collected by the listener for the given [source]. |
+ */ |
+ List<AnalysisError> getErrorsForSource(Source source) { |
+ HashSet<AnalysisError> errorsForSource = _errors[source]; |
+ if (errorsForSource == null) { |
+ return AnalysisError.NO_ERRORS; |
+ } else { |
+ return new List.from(errorsForSource); |
+ } |
+ } |
+ |
+ @override |
+ void onError(AnalysisError error) { |
+ Source source = error.source; |
+ HashSet<AnalysisError> errorsForSource = _errors[source]; |
+ if (_errors[source] == null) { |
+ errorsForSource = new HashSet<AnalysisError>(); |
+ _errors[source] = errorsForSource; |
+ } |
+ errorsForSource.add(error); |
+ } |
+} |
+ |
+/** |
+ * An [AnalysisErrorListener] that ignores error. |
+ */ |
+class _NullErrorListener implements AnalysisErrorListener { |
+ @override |
+ void onError(AnalysisError event) { |
+ // Ignore errors |
+ } |
+} |