| // Copyright (c) 2016, 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:analyzer/error/error.dart'; |
| import 'package:analyzer/source/error_processor.dart' show ErrorProcessor; |
| import 'package:analyzer/source/line_info.dart'; |
| import 'package:analyzer/src/error/codes.dart'; |
| import 'package:analyzer/src/generated/engine.dart' show AnalysisOptions; |
| import 'package:path/path.dart' as path; |
| |
| class ErrorCollector { |
| final bool _replCompile; |
| final AnalysisOptions _options; |
| SplayTreeMap<AnalysisError, String> _errors; |
| |
| ErrorCollector(this._options, this._replCompile) { |
| _errors = SplayTreeMap<AnalysisError, String>(_compareErrors); |
| } |
| |
| bool get hasFatalErrors => _errors.keys.any(_isFatalError); |
| |
| Iterable<String> get formattedErrors => _errors.values; |
| |
| void add(LineInfo lineInfo, AnalysisError error) { |
| if (_shouldIgnoreError(error)) return; |
| |
| // Skip hints, some like TODOs are not useful. |
| if (_errorSeverity(error).ordinal <= ErrorSeverity.INFO.ordinal) return; |
| |
| _errors[error] = _formatError(lineInfo, error); |
| } |
| |
| void addAll(LineInfo lineInfo, Iterable<AnalysisError> errors) { |
| for (var e in errors) { |
| add(lineInfo, e); |
| } |
| } |
| |
| ErrorSeverity _errorSeverity(AnalysisError error) { |
| var errorCode = error.errorCode; |
| if (errorCode == StrongModeCode.TOP_LEVEL_FUNCTION_LITERAL_BLOCK || |
| errorCode == StrongModeCode.TOP_LEVEL_INSTANCE_GETTER || |
| errorCode == StrongModeCode.TOP_LEVEL_INSTANCE_METHOD) { |
| // These are normally hints, but they should be errors when running DDC, so |
| // that users won't be surprised by behavioral differences between DDC and |
| // dart2js. |
| return ErrorSeverity.ERROR; |
| } |
| |
| // TODO(jmesserly): remove support for customizing error levels via |
| // analysis_options from DDC. (it won't work with --kernel). |
| return ErrorProcessor.getProcessor(_options, error)?.severity ?? |
| errorCode.errorSeverity; |
| } |
| |
| String _formatError(LineInfo lineInfo, AnalysisError error) { |
| var location = lineInfo.getLocation(error.offset); |
| |
| // [warning] 'foo' is not a... (/Users/.../tmp/foo.dart, line 1, col 2) |
| return (StringBuffer() |
| ..write('[${_errorSeverity(error).displayName}] ') |
| ..write(error.message) |
| ..write(' (${path.prettyUri(error.source.uri)}') |
| ..write( |
| ', line ${location.lineNumber}, col ${location.columnNumber})')) |
| .toString(); |
| } |
| |
| bool _shouldIgnoreError(AnalysisError error) { |
| var uri = error.source.uri; |
| if (uri.scheme != 'dart') return false; |
| var sdkLib = uri.pathSegments[0]; |
| if (sdkLib == 'html' || sdkLib == 'svg' || sdkLib == '_interceptors') { |
| var c = error.errorCode; |
| return c == StaticWarningCode.FINAL_NOT_INITIALIZED_CONSTRUCTOR_1 || |
| c == StaticWarningCode.FINAL_NOT_INITIALIZED_CONSTRUCTOR_2 || |
| c == StaticWarningCode.FINAL_NOT_INITIALIZED_CONSTRUCTOR_3_PLUS; |
| } |
| return false; |
| } |
| |
| int _compareErrors(AnalysisError error1, AnalysisError error2) { |
| // severity |
| int compare = _errorSeverity(error2).compareTo(_errorSeverity(error1)); |
| if (compare != 0) return compare; |
| |
| // path |
| compare = Comparable.compare(error1.source.fullName.toLowerCase(), |
| error2.source.fullName.toLowerCase()); |
| if (compare != 0) return compare; |
| |
| // offset |
| compare = error1.offset - error2.offset; |
| if (compare != 0) return compare; |
| |
| // compare message, in worst case. |
| return error1.message.compareTo(error2.message); |
| } |
| |
| bool _isFatalError(AnalysisError e) { |
| if (_errorSeverity(e) != ErrorSeverity.ERROR) return false; |
| |
| // These errors are not fatal in the REPL compile mode as we |
| // allow access to private members across library boundaries |
| // and those accesses will show up as undefined members unless |
| // additional analyzer changes are made to support them. |
| // TODO(jacobr): consider checking that the identifier name |
| // referenced by the error is private. |
| return !_replCompile || |
| (e.errorCode != StaticTypeWarningCode.UNDEFINED_GETTER && |
| e.errorCode != StaticTypeWarningCode.UNDEFINED_SETTER && |
| e.errorCode != StaticTypeWarningCode.UNDEFINED_METHOD); |
| } |
| } |
| |
| const invalidImportDartMirrors = StrongModeCode( |
| ErrorType.COMPILE_TIME_ERROR, |
| 'IMPORT_DART_MIRRORS', |
| 'Cannot import "dart:mirrors" in web applications (https://goo.gl/R1anEs).'); |
| |
| const invalidJSInteger = StrongModeCode( |
| ErrorType.COMPILE_TIME_ERROR, |
| 'INVALID_JS_INTEGER', |
| "The integer literal '{0}' can't be represented exactly in JavaScript. " |
| "The nearest value that can be represented exactly is '{1}'."); |