Chromium Code Reviews| Index: pkg/analyzer_cli/lib/src/error_formatter.dart |
| diff --git a/pkg/analyzer_cli/lib/src/error_formatter.dart b/pkg/analyzer_cli/lib/src/error_formatter.dart |
| index e431778c1940b0cd2061dd2d361501e58a47a954..cb023c4131edf1ddc6060ca5ddededdc69510967 100644 |
| --- a/pkg/analyzer_cli/lib/src/error_formatter.dart |
| +++ b/pkg/analyzer_cli/lib/src/error_formatter.dart |
| @@ -11,12 +11,26 @@ import 'package:analyzer_cli/src/ansi.dart'; |
| import 'package:analyzer_cli/src/options.dart'; |
| import 'package:path/path.dart' as path; |
| +final Map<String, int> _severityCompare = { |
| + 'error': 5, |
| + 'warning': 4, |
| + 'info': 3, |
| + 'lint': 2, |
| + 'hint': 1, |
| +}; |
| + |
| +String _pluralize(String word, int count) => count == 1 ? word : word + "s"; |
| + |
| +/// Given an absolute path, return a relative path if the file is contained in |
| +/// the current directory; return the original path otherwise. |
| +String _relative(String file) { |
| + return file.startsWith(path.current) ? path.relative(file) : file; |
| +} |
| + |
| /// Returns the given error's severity. |
| ErrorSeverity _severityIdentity(AnalysisError error) => |
| error.errorCode.errorSeverity; |
| -String _pluralize(String word, int count) => count == 1 ? word : word + "s"; |
| - |
| /// Returns desired severity for the given [error] (or `null` if it's to be |
| /// suppressed). |
| typedef ErrorSeverity SeverityProcessor(AnalysisError error); |
| @@ -88,6 +102,64 @@ class AnalysisStats { |
| } |
| } |
| +/// An [AnalysisError] with line and column information. |
| +class CLIError implements Comparable<CLIError> { |
| + final String severity; |
| + final String sourcePath; |
| + final int offset; |
| + final int line; |
| + final int column; |
| + final String message; |
| + final String errorCode; |
| + final String correction; |
| + |
| + CLIError({ |
| + this.severity, |
| + this.sourcePath, |
| + this.offset, |
| + this.line, |
| + this.column, |
| + this.message, |
| + this.errorCode, |
| + this.correction, |
| + }); |
| + |
| + @override |
| + int get hashCode => |
| + severity.hashCode ^ sourcePath.hashCode ^ errorCode.hashCode ^ offset; |
| + bool get isError => severity == 'error'; |
| + bool get isHint => severity == 'hint'; |
| + bool get isLint => severity == 'lint'; |
| + |
| + bool get isWarning => severity == 'warning'; |
| + |
| + @override |
| + bool operator ==(other) { |
| + if (other is! CLIError) return false; |
| + |
| + return severity == other.severity && |
| + sourcePath == other.sourcePath && |
| + errorCode == other.errorCode && |
| + offset == other.offset; |
| + } |
| + |
| + @override |
| + int compareTo(CLIError other) { |
| + // severity |
| + int compare = |
| + _severityCompare[other.severity] - _severityCompare[this.severity]; |
| + if (compare != 0) return compare; |
| + |
| + // path |
| + compare = Comparable.compare( |
| + this.sourcePath.toLowerCase(), other.sourcePath.toLowerCase()); |
| + if (compare != 0) return compare; |
| + |
| + // offset |
| + return this.offset - other.offset; |
| + } |
| +} |
| + |
| /// Helper for formatting [AnalysisError]s. |
| /// |
| /// The two format options are a user consumable format and a machine consumable |
| @@ -104,10 +176,11 @@ abstract class ErrorFormatter { |
| severityProcessor == null ? _severityIdentity : severityProcessor; |
| } |
| - /// Compute the severity for this [error] or `null` if this error should be |
| - /// filtered. |
| - ErrorSeverity _computeSeverity(AnalysisError error) => |
| - _severityProcessor(error); |
| + /// Call to write any batched up errors from [formatErrors]. |
| + void flush(); |
| + |
| + void formatError( |
| + Map<AnalysisError, LineInfo> errorToLine, AnalysisError error); |
| void formatErrors(List<AnalysisErrorInfo> errorInfos) { |
| stats.unfilteredCount += errorInfos.length; |
| @@ -129,78 +202,10 @@ abstract class ErrorFormatter { |
| } |
| } |
| - void formatError( |
| - Map<AnalysisError, LineInfo> errorToLine, AnalysisError error); |
| - |
| - /// Call to write any batched up errors from [formatErrors]. |
| - void flush(); |
| -} |
| - |
| -class MachineErrorFormatter extends ErrorFormatter { |
| - static final int _pipeCodeUnit = '|'.codeUnitAt(0); |
| - static final int _slashCodeUnit = '\\'.codeUnitAt(0); |
| - static final int _newline = '\n'.codeUnitAt(0); |
| - static final int _return = '\r'.codeUnitAt(0); |
| - |
| - MachineErrorFormatter( |
| - StringSink out, CommandLineOptions options, AnalysisStats stats, |
| - {SeverityProcessor severityProcessor}) |
| - : super(out, options, stats, severityProcessor: severityProcessor); |
| - |
| - void formatError( |
| - Map<AnalysisError, LineInfo> errorToLine, AnalysisError error) { |
| - Source source = error.source; |
| - LineInfo_Location location = errorToLine[error].getLocation(error.offset); |
| - int length = error.length; |
| - |
| - ErrorSeverity severity = _severityProcessor(error); |
| - |
| - if (severity == ErrorSeverity.ERROR) { |
| - stats.errorCount++; |
| - } else if (severity == ErrorSeverity.WARNING) { |
| - stats.warnCount++; |
| - } else if (error.errorCode.type == ErrorType.HINT) { |
| - stats.hintCount++; |
| - } else if (error.errorCode.type == ErrorType.LINT) { |
| - stats.lintCount++; |
| - } |
| - |
| - out.write(severity); |
| - out.write('|'); |
| - out.write(error.errorCode.type); |
| - out.write('|'); |
| - out.write(error.errorCode.name); |
| - out.write('|'); |
| - out.write(_escapeForMachineMode(source.fullName)); |
| - out.write('|'); |
| - out.write(location.lineNumber); |
| - out.write('|'); |
| - out.write(location.columnNumber); |
| - out.write('|'); |
| - out.write(length); |
| - out.write('|'); |
| - out.write(_escapeForMachineMode(error.message)); |
| - out.writeln(); |
| - } |
| - |
| - static String _escapeForMachineMode(String input) { |
| - StringBuffer result = new StringBuffer(); |
| - for (int c in input.codeUnits) { |
| - if (c == _newline) { |
| - result.write(r'\n'); |
| - } else if (c == _return) { |
| - result.write(r'\r'); |
| - } else { |
| - if (c == _slashCodeUnit || c == _pipeCodeUnit) { |
| - result.write('\\'); |
| - } |
| - result.writeCharCode(c); |
| - } |
| - } |
| - return result.toString(); |
| - } |
| - |
| - void flush() {} |
| + /// Compute the severity for this [error] or `null` if this error should be |
| + /// filtered. |
| + ErrorSeverity _computeSeverity(AnalysisError error) => |
| + _severityProcessor(error); |
| } |
| class HumanErrorFormatter extends ErrorFormatter { |
| @@ -216,6 +221,45 @@ class HumanErrorFormatter extends ErrorFormatter { |
| ansi = new AnsiLogger(this.options.color); |
| } |
| + void flush() { |
| + // sort |
| + List<CLIError> sortedErrors = batchedErrors.toList()..sort(); |
| + |
| + for (CLIError error in sortedErrors) { |
| + if (error.isError) { |
| + stats.errorCount++; |
| + } else if (error.isWarning) { |
| + stats.warnCount++; |
| + } else if (error.isLint) { |
| + stats.lintCount++; |
| + } else if (error.isHint) { |
| + stats.hintCount++; |
| + } |
| + |
| + // warning • 'foo' is not a bar at lib/foo.dart:1:2 • foo_warning |
| + String issueColor = (error.isError == ErrorSeverity.ERROR || |
| + error.isWarning == ErrorSeverity.WARNING) |
| + ? ansi.red |
| + : ''; |
| + out.write(' $issueColor${error.severity}${ansi.none} ' |
| + '${ansi.bullet} ${ansi.bold}${error.message}${ansi.none} '); |
| + out.write('at ${error.sourcePath}'); |
| + out.write(':${error.line}:${error.column} '); |
| + out.write('${ansi.bullet} ${error.errorCode}'); |
| + out.writeln(); |
| + |
| + // If verbose, also print any associated correction. |
| + if (options.verbose && error.correction != null) { |
| + out.writeln( |
| + '${' '.padLeft(error.severity.length + 2)}${error.correction}'); |
| + } |
| + } |
| + |
| + // clear out batched errors |
| + batchedErrors.clear(); |
| + } |
| + |
| void formatError( |
| Map<AnalysisError, LineInfo> errorToLine, AnalysisError error) { |
| Source source = error.source; |
| @@ -262,115 +306,71 @@ class HumanErrorFormatter extends ErrorFormatter { |
| correction: error.correction, |
| )); |
| } |
| - |
| - void flush() { |
| - // sort |
| - List<CLIError> sortedErrors = batchedErrors.toList()..sort(); |
| - |
| - for (CLIError error in sortedErrors) { |
| - if (error.isError) { |
| - stats.errorCount++; |
| - } else if (error.isWarning) { |
| - stats.warnCount++; |
| - } else if (error.isLint) { |
| - stats.lintCount++; |
| - } else if (error.isHint) { |
| - stats.hintCount++; |
| - } |
| - |
| - // warning • 'foo' is not a bar at lib/foo.dart:1:2 • foo_warning |
| - String issueColor = (error.isError == ErrorSeverity.ERROR || |
| - error.isWarning == ErrorSeverity.WARNING) |
| - ? ansi.red |
| - : ''; |
| - out.write(' $issueColor${error.severity}${ansi.none} ' |
| - '${ansi.bullet} ${ansi.bold}${error.message}${ansi.none} '); |
| - out.write('at ${error.sourcePath}'); |
| - out.write(':${error.line}:${error.column} '); |
| - out.write('${ansi.bullet} ${error.errorCode}'); |
| - out.writeln(); |
| - |
| - // If verbose, also print any associated correction. |
| - if (options.verbose && error.correction != null) { |
| - out.writeln( |
| - '${' '.padLeft(error.severity.length + 2)}${error.correction}'); |
| - } |
| - } |
| - |
| - // clear out batched errors |
| - batchedErrors.clear(); |
| - } |
| } |
| -final Map<String, int> _severityCompare = { |
| - 'error': 5, |
| - 'warning': 4, |
| - 'info': 3, |
| - 'lint': 2, |
| - 'hint': 1, |
| -}; |
| +class MachineErrorFormatter extends ErrorFormatter { |
| + static final int _pipeCodeUnit = '|'.codeUnitAt(0); |
| + static final int _slashCodeUnit = '\\'.codeUnitAt(0); |
| + static final int _newline = '\n'.codeUnitAt(0); |
| + static final int _return = '\r'.codeUnitAt(0); |
| -/// An [AnalysisError] with line and column information. |
| -class CLIError implements Comparable<CLIError> { |
| - final String severity; |
| - final String sourcePath; |
| - final int offset; |
| - final int line; |
| - final int column; |
| - final String message; |
| - final String errorCode; |
| - final String correction; |
| + MachineErrorFormatter( |
| + StringSink out, CommandLineOptions options, AnalysisStats stats, |
| + {SeverityProcessor severityProcessor}) |
| + : super(out, options, stats, severityProcessor: severityProcessor); |
| - CLIError({ |
| - this.severity, |
| - this.sourcePath, |
| - this.offset, |
| - this.line, |
| - this.column, |
| - this.message, |
| - this.errorCode, |
| - this.correction, |
| - }); |
| + void flush() {} |
| - bool get isError => severity == 'error'; |
| - bool get isWarning => severity == 'warning'; |
| - bool get isLint => severity == 'lint'; |
| - bool get isHint => severity == 'hint'; |
| + void formatError( |
| + Map<AnalysisError, LineInfo> errorToLine, AnalysisError error) { |
| + Source source = error.source; |
| + LineInfo_Location location = errorToLine[error].getLocation(error.offset); |
| + int length = error.length; |
| - @override |
| - int get hashCode => |
| - severity.hashCode ^ sourcePath.hashCode ^ errorCode.hashCode ^ offset; |
| + ErrorSeverity severity = _severityProcessor(error); |
| - @override |
| - bool operator ==(other) { |
| - if (other is! CLIError) return false; |
| + if (severity == ErrorSeverity.ERROR) { |
| + stats.errorCount++; |
| + } else if (severity == ErrorSeverity.WARNING) { |
| + stats.warnCount++; |
| + } else if (error.errorCode.type == ErrorType.HINT) { |
| + stats.hintCount++; |
| + } else if (error.errorCode.type == ErrorType.LINT) { |
| + stats.lintCount++; |
| + } |
| - return severity == other.severity && |
| - sourcePath == other.sourcePath && |
| - errorCode == other.errorCode && |
| - offset == other.offset; |
| + out.write(severity); |
| + out.write('|'); |
| + out.write(error.errorCode.type); |
| + out.write('|'); |
| + out.write(error.errorCode.name); |
| + out.write('|'); |
| + out.write(_escapeForMachineMode(source.fullName)); |
| + out.write('|'); |
| + out.write(location.lineNumber); |
| + out.write('|'); |
| + out.write(location.columnNumber); |
| + out.write('|'); |
| + out.write(length); |
| + out.write('|'); |
| + out.write(_escapeForMachineMode(error.message)); |
| + out.writeln(); |
| } |
| - @override |
| - int compareTo(CLIError other) { |
| - // severity |
| - int compare = |
| - _severityCompare[other.severity] - _severityCompare[this.severity]; |
| - if (compare != 0) return compare; |
| - |
| - // path |
| - compare = Comparable.compare( |
| - this.sourcePath.toLowerCase(), other.sourcePath.toLowerCase()); |
| - if (compare != 0) return compare; |
| - |
| - // offset |
| - return this.offset - other.offset; |
| + static String _escapeForMachineMode(String input) { |
| + StringBuffer result = new StringBuffer(); |
| + for (int c in input.codeUnits) { |
| + if (c == _newline) { |
| + result.write(r'\n'); |
| + } else if (c == _return) { |
| + result.write(r'\r'); |
| + } else { |
| + if (c == _slashCodeUnit || c == _pipeCodeUnit) { |
| + result.write('\\'); |
| + } |
| + result.writeCharCode(c); |
| + } |
| + } |
| + return result.toString(); |
| } |
| } |
| - |
| -/// Given an absolute path, return a relative path if the file is contained in |
| -/// the current directory; return the original path otherwise. |
| -String _relative(String file) { |
| - return file.startsWith(path.current) ? path.relative(file) : file; |
| -} |