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

Side by Side Diff: lib/src/report.dart

Issue 1788973002: Remove code that requires whole-program compile (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: merged Created 4 years, 9 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 unified diff | Download patch
« no previous file with comments | « lib/src/options.dart ('k') | lib/src/report/html_gen.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 /// Summarizes the information produced by the checker.
6
7 import 'dart:math' show max;
8
9 import 'package:analyzer/src/generated/engine.dart' show AnalysisContext; 5 import 'package:analyzer/src/generated/engine.dart' show AnalysisContext;
10 import 'package:analyzer/src/generated/error.dart'; 6 import 'package:analyzer/src/generated/error.dart';
11 import 'package:logging/logging.dart'; 7 import 'package:logging/logging.dart';
12 import 'package:path/path.dart' as path; 8 import 'package:path/path.dart' as path;
13 import 'package:source_span/source_span.dart';
14
15 import 'utils.dart'; 9 import 'utils.dart';
16 import 'summary.dart';
17 10
18 final _checkerLogger = new Logger('dev_compiler.checker'); 11 final _checkerLogger = new Logger('dev_compiler.checker');
19 12
20 /// Collects errors, and then sorts them and sends them 13 /// Collects errors, and then sorts them and sends them
21 class ErrorCollector implements AnalysisErrorListener { 14 class ErrorCollector implements AnalysisErrorListener {
22 final AnalysisErrorListener listener; 15 final AnalysisErrorListener listener;
23 final List<AnalysisError> _errors = []; 16 final List<AnalysisError> _errors = [];
24 17
25 ErrorCollector(this.listener); 18 ErrorCollector(this.listener);
26 19
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
94 _checkerLogger.log(level, text); 87 _checkerLogger.log(level, text);
95 } 88 }
96 } 89 }
97 90
98 // TODO(jmesserly): remove log levels, instead just use severity. 91 // TODO(jmesserly): remove log levels, instead just use severity.
99 const _severityToLevel = const { 92 const _severityToLevel = const {
100 ErrorSeverity.ERROR: Level.SEVERE, 93 ErrorSeverity.ERROR: Level.SEVERE,
101 ErrorSeverity.WARNING: Level.WARNING, 94 ErrorSeverity.WARNING: Level.WARNING,
102 ErrorSeverity.INFO: Level.INFO 95 ErrorSeverity.INFO: Level.INFO
103 }; 96 };
104
105 /// A reporter that gathers all the information in a [GlobalSummary].
106 class SummaryReporter implements AnalysisErrorListener {
107 GlobalSummary result = new GlobalSummary();
108 final Level _level;
109 final AnalysisContext _context;
110
111 SummaryReporter(this._context, [this._level = Level.ALL]);
112
113 IndividualSummary _getIndividualSummary(Uri uri) {
114 if (uri.path.endsWith('.html')) {
115 return result.loose.putIfAbsent('$uri', () => new HtmlSummary('$uri'));
116 }
117
118 var container;
119 if (uri.scheme == 'package') {
120 var pname = path.split(uri.path)[0];
121 result.packages.putIfAbsent(pname, () => new PackageSummary(pname));
122 container = result.packages[pname].libraries;
123 } else if (uri.scheme == 'dart') {
124 container = result.system;
125 } else {
126 container = result.loose;
127 }
128 return container.putIfAbsent('$uri', () => new LibrarySummary('$uri'));
129 }
130
131 void onError(AnalysisError error) {
132 // Only summarize messages per configured logging level
133 var code = error.errorCode;
134 if (_severityToLevel[code.errorSeverity] < _level) return;
135
136 var span = _toSpan(_context, error);
137 var summary = _getIndividualSummary(error.source.uri);
138 if (summary is LibrarySummary) {
139 summary.recordSourceLines(error.source.uri, () {
140 // TODO(jmesserly): parsing is serious overkill for this.
141 // Should be cached, but still.
142 // On the other hand, if we are going to parse, we could get a much
143 // better source lines of code estimate by excluding things like
144 // comments, blank lines, and closing braces.
145 var unit = _context.parseCompilationUnit(error.source);
146 return unit.lineInfo.getLocation(unit.endToken.end).lineNumber;
147 });
148 }
149 summary.messages.add(new MessageSummary(errorCodeName(code),
150 code.errorSeverity.displayName, span, error.message));
151 }
152
153 // TODO(jmesserly): fix to not depend on SourceSpan. This will be really slow
154 // because it will reload source text from disk, for every single message...
155 SourceSpanWithContext _toSpan(AnalysisContext context, AnalysisError error) {
156 var source = error.source;
157 var lineInfo = context.computeLineInfo(source);
158 var content = context.getContents(source).data;
159 var start = error.offset;
160 var end = start + error.length;
161 return createSpanHelper(lineInfo, start, end, source, content);
162 }
163
164 void clearLibrary(Uri uri) {
165 (_getIndividualSummary(uri) as LibrarySummary).clear();
166 }
167
168 void clearHtml(Uri uri) {
169 HtmlSummary htmlSummary = result.loose['$uri'];
170 if (htmlSummary != null) htmlSummary.messages.clear();
171 }
172 }
173
174 /// Produces a string representation of the summary.
175 String summaryToString(GlobalSummary summary) {
176 var counter = new _Counter();
177 summary.accept(counter);
178
179 var table = new _Table();
180 // Declare columns and add header
181 table.declareColumn('package');
182 table.declareColumn('AnalyzerError', abbreviate: true);
183
184 var activeInfoTypes = counter.totals.keys;
185 activeInfoTypes.forEach((t) => table.declareColumn(t, abbreviate: true));
186 table.declareColumn('LinesOfCode', abbreviate: true);
187 table.addHeader();
188
189 // Add entries for each package
190 appendCount(count) => table.addEntry(count == null ? 0 : count);
191 for (var package in counter.errorCount.keys) {
192 appendCount(package);
193 appendCount(counter.errorCount[package]['AnalyzerError']);
194 activeInfoTypes.forEach((t) => appendCount(counter.errorCount[package][t]));
195 appendCount(counter.linesOfCode[package]);
196 }
197
198 // Add totals, percents and a new header for quick reference
199 table.addEmptyRow();
200 table.addHeader();
201 table.addEntry('total');
202 appendCount(counter.totals['AnalyzerError']);
203 activeInfoTypes.forEach((t) => appendCount(counter.totals[t]));
204 appendCount(counter.totalLinesOfCode);
205
206 appendPercent(count, total) {
207 if (count == null) count = 0;
208 var value = (count * 100 / total).toStringAsFixed(2);
209 table.addEntry(value);
210 }
211
212 var totalLOC = counter.totalLinesOfCode;
213 table.addEntry('%');
214 appendPercent(counter.totals['AnalyzerError'], totalLOC);
215 activeInfoTypes.forEach((t) => appendPercent(counter.totals[t], totalLOC));
216 appendCount(100);
217
218 return table.toString();
219 }
220
221 /// Helper class to combine all the information in table form.
222 class _Table {
223 int _totalColumns = 0;
224 int get totalColumns => _totalColumns;
225
226 /// Abbreviations, used to make headers shorter.
227 Map<String, String> abbreviations = {};
228
229 /// Width of each column.
230 List<int> widths = <int>[];
231
232 /// The header for each column (`header.length == totalColumns`).
233 List header = [];
234
235 /// Each row on the table. Note that all rows have the same size
236 /// (`rows[*].length == totalColumns`).
237 List<List> rows = [];
238
239 /// Whether we started adding entries. Indicates that no more columns can be
240 /// added.
241 bool _sealed = false;
242
243 /// Current row being built by [addEntry].
244 List _currentRow;
245
246 /// Add a column with the given [name].
247 void declareColumn(String name, {bool abbreviate: false}) {
248 assert(!_sealed);
249 var headerName = name;
250 if (abbreviate) {
251 // abbreviate the header by using only the capital initials.
252 headerName = name.replaceAll(new RegExp('[a-z]'), '');
253 while (abbreviations[headerName] != null) headerName = "$headerName'";
254 abbreviations[headerName] = name;
255 }
256 widths.add(max(5, headerName.length + 1) as int);
257 header.add(headerName);
258 _totalColumns++;
259 }
260
261 /// Add an entry in the table, creating a new row each time [totalColumns]
262 /// entries are added.
263 void addEntry(entry) {
264 if (_currentRow == null) {
265 _sealed = true;
266 _currentRow = [];
267 }
268 int pos = _currentRow.length;
269 assert(pos < _totalColumns);
270
271 widths[pos] = max(widths[pos], '$entry'.length + 1);
272 _currentRow.add('$entry');
273
274 if (pos + 1 == _totalColumns) {
275 rows.add(_currentRow);
276 _currentRow = [];
277 }
278 }
279
280 /// Add an empty row to divide sections of the table.
281 void addEmptyRow() {
282 var emptyRow = [];
283 for (int i = 0; i < _totalColumns; i++) {
284 emptyRow.add('-' * widths[i]);
285 }
286 rows.add(emptyRow);
287 }
288
289 /// Enter the header titles. OK to do so more than once in long tables.
290 void addHeader() {
291 rows.add(header);
292 }
293
294 /// Generates a string representation of the table to print on a terminal.
295 // TODO(sigmund): add also a .csv format
296 String toString() {
297 var sb = new StringBuffer();
298 sb.write('\n');
299 for (var row in rows) {
300 for (int i = 0; i < _totalColumns; i++) {
301 var entry = row[i];
302 // Align first column to the left, everything else to the right.
303 sb.write(
304 i == 0 ? entry.padRight(widths[i]) : entry.padLeft(widths[i] + 1));
305 }
306 sb.write('\n');
307 }
308 sb.write('\nWhere:\n');
309 for (var id in abbreviations.keys) {
310 sb.write(' $id:'.padRight(7));
311 sb.write(' ${abbreviations[id]}\n');
312 }
313 return sb.toString();
314 }
315 }
316
317 /// An example visitor that counts the number of errors per package and total.
318 class _Counter extends RecursiveSummaryVisitor {
319 String _currentPackage;
320 String get currentPackage =>
321 _currentPackage != null ? _currentPackage : "*other*";
322 var sb = new StringBuffer();
323 Map<String, Map<String, int>> errorCount = <String, Map<String, int>>{};
324 Map<String, int> linesOfCode = <String, int>{};
325 Map<String, int> totals = <String, int>{};
326 int totalLinesOfCode = 0;
327
328 void visitGlobal(GlobalSummary global) {
329 if (!global.system.isEmpty) {
330 for (var lib in global.system.values) {
331 lib.accept(this);
332 }
333 }
334
335 if (!global.packages.isEmpty) {
336 for (var lib in global.packages.values) {
337 lib.accept(this);
338 }
339 }
340
341 if (!global.loose.isEmpty) {
342 for (var lib in global.loose.values) {
343 lib.accept(this);
344 }
345 }
346 }
347
348 void visitPackage(PackageSummary package) {
349 _currentPackage = package.name;
350 super.visitPackage(package);
351 _currentPackage = null;
352 }
353
354 void visitLibrary(LibrarySummary lib) {
355 super.visitLibrary(lib);
356 linesOfCode.putIfAbsent(currentPackage, () => 0);
357 linesOfCode[currentPackage] += lib.lines;
358 totalLinesOfCode += lib.lines;
359 }
360
361 visitMessage(MessageSummary message) {
362 var kind = message.kind;
363 errorCount.putIfAbsent(currentPackage, () => <String, int>{});
364 errorCount[currentPackage].putIfAbsent(kind, () => 0);
365 errorCount[currentPackage][kind]++;
366 totals.putIfAbsent(kind, () => 0);
367 totals[kind]++;
368 }
369 }
OLDNEW
« no previous file with comments | « lib/src/options.dart ('k') | lib/src/report/html_gen.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698