Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(13)

Unified Diff: pkg/analyzer_cli/lib/src/error_formatter.dart

Issue 2793813002: Fix an issue with duplicate reported analysis errors. (Closed)
Patch Set: Merge branch 'master' into fix_duplicate_errors Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « pkg/analyzer_cli/lib/src/driver.dart ('k') | pkg/analyzer_cli/lib/src/error_severity.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 bac7f7ce80ad1137acd8a1517ac13209d41eeb88..086edfc52198b2e9f5af0d98d673b0745b35fcbf 100644
--- a/pkg/analyzer_cli/lib/src/error_formatter.dart
+++ b/pkg/analyzer_cli/lib/src/error_formatter.dart
@@ -12,47 +12,36 @@ import 'package:analyzer_cli/src/options.dart';
import 'package:path/path.dart' as path;
/// Returns the given error's severity.
-ProcessedSeverity _identity(AnalysisError error) =>
+ProcessedSeverity _severityIdentity(AnalysisError error) =>
new ProcessedSeverity(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 ProcessedSeverity _SeverityProcessor(AnalysisError error);
+typedef ProcessedSeverity SeverityProcessor(AnalysisError error);
/// Analysis statistics counter.
class AnalysisStats {
/// The total number of diagnostics sent to [formatErrors].
- int unfilteredCount;
+ int unfilteredCount = 0;
- int errorCount;
- int hintCount;
- int lintCount;
- int warnCount;
+ int errorCount = 0;
+ int hintCount = 0;
+ int lintCount = 0;
+ int warnCount = 0;
- AnalysisStats() {
- init();
- }
+ AnalysisStats();
/// The total number of diagnostics reported to the user.
int get filteredCount => errorCount + warnCount + hintCount + lintCount;
- /// (Re)set initial values.
- void init() {
- unfilteredCount = 0;
- errorCount = 0;
- hintCount = 0;
- lintCount = 0;
- warnCount = 0;
- }
-
/// Print statistics to [out].
void print(StringSink out) {
- var hasErrors = errorCount != 0;
- var hasWarns = warnCount != 0;
- var hasHints = hintCount != 0;
- var hasLints = lintCount != 0;
+ bool hasErrors = errorCount != 0;
+ bool hasWarns = warnCount != 0;
+ bool hasHints = hintCount != 0;
+ bool hasLints = lintCount != 0;
bool hasContent = false;
if (hasErrors) {
out.write(errorCount);
@@ -73,26 +62,22 @@ class AnalysisStats {
out.write(_pluralize("warning", warnCount));
hasContent = true;
}
- if (hasHints) {
+ if (hasLints) {
if (hasContent) {
- if (!hasLints) {
- out.write(' and ');
- } else {
- out.write(", ");
- }
+ out.write(hasHints ? ', ' : ' and ');
}
- out.write(hintCount);
+ out.write(lintCount);
out.write(' ');
- out.write(_pluralize("hint", hintCount));
+ out.write(_pluralize("lint", lintCount));
hasContent = true;
}
- if (hasLints) {
+ if (hasHints) {
if (hasContent) {
out.write(" and ");
}
- out.write(lintCount);
+ out.write(hintCount);
out.write(' ');
- out.write(_pluralize("lint", lintCount));
+ out.write(_pluralize("hint", hintCount));
hasContent = true;
}
if (hasContent) {
@@ -107,166 +92,110 @@ class AnalysisStats {
///
/// The two format options are a user consumable format and a machine consumable
/// format.
-class 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);
-
+abstract class ErrorFormatter {
final StringSink out;
final CommandLineOptions options;
final AnalysisStats stats;
-
- final _SeverityProcessor processSeverity;
-
- AnsiLogger ansi;
+ SeverityProcessor _severityProcessor;
ErrorFormatter(this.out, this.options, this.stats,
- [this.processSeverity = _identity]) {
- ansi = new AnsiLogger(this.options.color);
+ {SeverityProcessor severityProcessor}) {
+ _severityProcessor =
+ severityProcessor == null ? _severityIdentity : severityProcessor;
}
/// Compute the severity for this [error] or `null` if this error should be
/// filtered.
- ErrorSeverity computeSeverity(AnalysisError error) =>
- processSeverity(error)?.severity;
-
- void formatError(
- Map<AnalysisError, LineInfo> errorToLine, AnalysisError error) {
- Source source = error.source;
- LineInfo_Location location = errorToLine[error].getLocation(error.offset);
- int length = error.length;
-
- ProcessedSeverity processedSeverity = processSeverity(error);
- ErrorSeverity severity = processedSeverity.severity;
-
- if (options.machineFormat) {
- if (!processedSeverity.overridden) {
- if (severity == ErrorSeverity.WARNING && options.warningsAreFatal) {
- severity = ErrorSeverity.ERROR;
- }
- }
- 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();
- } else {
- // Get display name.
- String errorType = severity.displayName;
-
- // Translate INFOs into LINTS and HINTS.
- if (severity == ErrorSeverity.INFO) {
- if (error.errorCode.type == ErrorType.HINT ||
- error.errorCode.type == ErrorType.LINT) {
- errorType = error.errorCode.type.displayName;
- }
- }
-
- final int errLength = ErrorSeverity.WARNING.displayName.length;
- final int indent = errLength + 5;
-
- // warning • 'foo' is not a bar at lib/foo.dart:1:2 • foo_warning
- String message = error.message;
- // Remove any terminating '.' from the end of the message.
- if (message.endsWith('.')) {
- message = message.substring(0, message.length - 1);
- }
- String issueColor =
- (severity == ErrorSeverity.ERROR || severity == ErrorSeverity.WARNING)
- ? ansi.red
- : '';
- out.write(' $issueColor${errorType.padLeft(errLength)}${ansi.none} '
- '${ansi.bullet} ${ansi.bold}$message${ansi.none} ');
- String sourceName;
- if (source.uriKind == UriKind.DART_URI) {
- sourceName = source.uri.toString();
- } else if (source.uriKind == UriKind.PACKAGE_URI) {
- sourceName = _relative(source.fullName);
- if (sourceName == source.fullName) {
- // If we weren't able to shorten the path name, use the package: version.
- sourceName = source.uri.toString();
- }
- } else {
- sourceName = _relative(source.fullName);
- }
- out.write('at $sourceName');
- out.write(':${location.lineNumber}:${location.columnNumber} ');
- out.write('${ansi.bullet} ${error.errorCode.name.toLowerCase()}');
- out.writeln();
-
- // If verbose, also print any associated correction.
- if (options.verbose && error.correction != null) {
- out.writeln('${' '.padLeft(indent)}${error.correction}');
- }
- }
- }
+ ErrorSeverity _computeSeverity(AnalysisError error) =>
+ _severityProcessor(error)?.severity;
void formatErrors(List<AnalysisErrorInfo> errorInfos) {
stats.unfilteredCount += errorInfos.length;
- var errors = new List<AnalysisError>();
- var errorToLine = new Map<AnalysisError, LineInfo>();
+ List<AnalysisError> errors = new List<AnalysisError>();
+ Map<AnalysisError, LineInfo> errorToLine =
+ new Map<AnalysisError, LineInfo>();
for (AnalysisErrorInfo errorInfo in errorInfos) {
for (AnalysisError error in errorInfo.errors) {
- if (computeSeverity(error) != null) {
+ if (_computeSeverity(error) != null) {
errors.add(error);
errorToLine[error] = errorInfo.lineInfo;
}
}
}
- // Sort errors.
- errors.sort((AnalysisError error1, AnalysisError error2) {
- // Severity.
- ErrorSeverity severity1 = computeSeverity(error1);
- ErrorSeverity severity2 = computeSeverity(error2);
- int compare = severity2.compareTo(severity1);
- if (compare != 0) {
- return compare;
- }
- // Path.
- compare = Comparable.compare(error1.source.fullName.toLowerCase(),
- error2.source.fullName.toLowerCase());
- if (compare != 0) {
- return compare;
- }
- // Offset.
- return error1.offset - error2.offset;
- });
- // Format errors.
+
for (AnalysisError error in errors) {
- ProcessedSeverity processedSeverity = processSeverity(error);
- ErrorSeverity severity = processedSeverity.severity;
- if (severity == ErrorSeverity.ERROR) {
+ formatError(errorToLine, error);
+ }
+ }
+
+ 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;
+
+ ProcessedSeverity processedSeverity = _severityProcessor(error);
+ ErrorSeverity severity = processedSeverity.severity;
+
+ if (!processedSeverity.overridden) {
+ if (severity == ErrorSeverity.WARNING && options.warningsAreFatal) {
+ severity = ErrorSeverity.ERROR;
+ }
+ }
+
+ if (severity == ErrorSeverity.ERROR) {
+ stats.errorCount++;
+ } else if (severity == ErrorSeverity.WARNING) {
+ // Only treat a warning as an error if it's not been set by a processor.
+ if (!processedSeverity.overridden && options.warningsAreFatal) {
stats.errorCount++;
- } else if (severity == ErrorSeverity.WARNING) {
- // Only treat a warning as an error if it's not been set by a processor.
- if (!processedSeverity.overridden && options.warningsAreFatal) {
- stats.errorCount++;
- } else {
- stats.warnCount++;
- }
- } else if (error.errorCode.type == ErrorType.HINT) {
- stats.hintCount++;
- } else if (error.errorCode.type == ErrorType.LINT) {
- stats.lintCount++;
+ } else {
+ stats.warnCount++;
}
- formatError(errorToLine, error);
+ } 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) {
+ static String _escapeForMachineMode(String input) {
StringBuffer result = new StringBuffer();
for (int c in input.codeUnits) {
if (c == _newline) {
@@ -282,12 +211,181 @@ class ErrorFormatter {
}
return result.toString();
}
+
+ void flush() {}
+}
+
+class HumanErrorFormatter extends ErrorFormatter {
+ AnsiLogger ansi;
+
+ // This is a Set in order to de-dup CLI errors.
+ Set<CLIError> batchedErrors = new Set();
+
+ HumanErrorFormatter(
+ StringSink out, CommandLineOptions options, AnalysisStats stats,
+ {SeverityProcessor severityProcessor})
+ : super(out, options, stats, severityProcessor: severityProcessor) {
+ ansi = new AnsiLogger(this.options.color);
+ }
+
+ void formatError(
+ Map<AnalysisError, LineInfo> errorToLine, AnalysisError error) {
+ Source source = error.source;
+ LineInfo_Location location = errorToLine[error].getLocation(error.offset);
+
+ ProcessedSeverity processedSeverity = _severityProcessor(error);
+ ErrorSeverity severity = processedSeverity.severity;
+
+ // Get display name; translate INFOs into LINTS and HINTS.
+ String errorType = severity.displayName;
+ if (severity == ErrorSeverity.INFO) {
+ if (error.errorCode.type == ErrorType.HINT ||
+ error.errorCode.type == ErrorType.LINT) {
+ errorType = error.errorCode.type.displayName;
+ }
+ }
+
+ // warning • 'foo' is not a bar at lib/foo.dart:1:2 • foo_warning
+ String message = error.message;
+ // Remove any terminating '.' from the end of the message.
+ if (message.endsWith('.')) {
+ message = message.substring(0, message.length - 1);
+ }
+ String sourcePath;
+ if (source.uriKind == UriKind.DART_URI) {
+ sourcePath = source.uri.toString();
+ } else if (source.uriKind == UriKind.PACKAGE_URI) {
+ sourcePath = _relative(source.fullName);
+ if (sourcePath == source.fullName) {
+ // If we weren't able to shorten the path name, use the package: version.
+ sourcePath = source.uri.toString();
+ }
+ } else {
+ sourcePath = _relative(source.fullName);
+ }
+
+ batchedErrors.add(new CLIError(
+ severity: errorType,
+ sourcePath: sourcePath,
+ offset: error.offset,
+ line: location.lineNumber,
+ column: location.columnNumber,
+ message: message,
+ errorCode: error.errorCode.name.toLowerCase(),
+ correction: error.correction,
+ ));
+ }
+
+ void flush() {
+ // sort
+ List<CLIError> sortedErrors = batchedErrors.toList()..sort();
+
+ // print
+ 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,
+};
+
+/// 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,
+ });
+
+ bool get isError => severity == 'error';
+ bool get isWarning => severity == 'warning';
+ bool get isLint => severity == 'lint';
+ bool get isHint => severity == 'hint';
+
+ @override
+ int get hashCode =>
+ severity.hashCode ^ sourcePath.hashCode ^ errorCode.hashCode ^ offset;
+
+ @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;
+ }
}
/// A severity with awareness of whether it was overridden by a processor.
class ProcessedSeverity {
- ErrorSeverity severity;
- bool overridden;
+ final ErrorSeverity severity;
+ final bool overridden;
ProcessedSeverity(this.severity, [this.overridden = false]);
}
« no previous file with comments | « pkg/analyzer_cli/lib/src/driver.dart ('k') | pkg/analyzer_cli/lib/src/error_severity.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698