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

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

Issue 988483006: Add widget to display errors, and report dependency_graph errors correctly (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 5 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
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. 5 /// Summarizes the information produced by the checker.
6 library dev_compiler.src.report; 6 library dev_compiler.src.report;
7 7
8 import 'dart:math' show max; 8 import 'dart:math' show max;
9 9
10 import 'package:analyzer/src/generated/source.dart' show Source;
11 import 'package:logging/logging.dart';
10 import 'package:path/path.dart' as path; 12 import 'package:path/path.dart' as path;
11 import 'package:source_span/source_span.dart'; 13 import 'package:source_span/source_span.dart';
12 import 'package:analyzer/src/generated/ast.dart';
13 import 'package:analyzer/src/generated/source.dart' show Source;
14 import 'package:logging/logging.dart';
15 14
16 import 'info.dart'; 15 import 'info.dart';
17 import 'utils.dart'; 16 import 'utils.dart';
17 import 'summary.dart';
18
19 /// A message (error or warning) produced by the dev_compiler and it's location
20 /// information.
21 ///
22 /// Currently the location information includes only the offsets within a file
23 /// where the error occurs. This is used in the context of a [CheckerReporter],
24 /// where the current file is being tracked.
25 abstract class Message {
26 // Message description.
27 final String message;
28
29 /// Log level. This is a placeholder for severity.
30 final Level level;
31
32 /// Offset where the error message begins in the tracked source file.
33 final int begin;
34
35 /// Offset where the error message ends in the tracked source file.
36 final int end;
37
38 const Message(this.message, this.level, this.begin, this.end);
39 }
40
41 /// Like [Message], but with a precomputed source span.
42 abstract class MessageWithSpan implements Message {
43 final String message;
44
45 final Level level;
46
47 final SourceSpan span;
48
49 int get begin => span.start.offset;
50 int get end => span.end.offset;
51
52 const MessageWithSpan(this.message, this.level, this.span);
53 }
18 54
19 // Interface used to report error messages from the checker. 55 // Interface used to report error messages from the checker.
20 abstract class CheckerReporter { 56 abstract class CheckerReporter {
21 /// Called when starting to process a library. 57 /// Called when starting to process a library.
22 void enterLibrary(LibraryInfo info); 58 void enterLibrary(Uri uri);
23 void leaveLibrary(); 59 void leaveLibrary();
24 60
61 /// Called when starting to process an HTML source file.
62 void enterHtml(Uri uri);
63 void leaveHtml();
64
25 /// Called when starting to process a source. All subsequent log entries must 65 /// Called when starting to process a source. All subsequent log entries must
26 /// belong to this source until the next call to enterSource. 66 /// belong to this source until the next call to enterSource.
27 void enterSource(Source source); 67 void enterSource(Source source);
28 void leaveSource(); 68 void leaveSource();
69 void log(Message message);
29 70
30 void log(StaticInfo info); 71 // Called in server-mode.
31 72 void clearLibrary(Uri uri);
32 // TODO(sigmund): merge this and [log] 73 void clearHtml(Uri uri);
33 void logAnalyzerError(String message, Level level, int begin, int end); 74 void clearAll();
34 } 75 }
35 76
36 final _checkerLogger = new Logger('dev_compiler.checker'); 77 final _checkerLogger = new Logger('dev_compiler.checker');
37 78
38 /// Simple reporter that logs checker messages as they are seen. 79 /// Simple reporter that logs checker messages as they are seen.
39 class LogReporter implements CheckerReporter { 80 class LogReporter implements CheckerReporter {
40 final bool useColors; 81 final bool useColors;
41 SourceFile _file; 82 SourceFile _file;
42 Source _current; 83 Source _current;
43 84
44 LogReporter([this.useColors = false]); 85 LogReporter([this.useColors = false]);
45 86
46 void enterLibrary(LibraryInfo info) {} 87 void enterLibrary(Uri uri) {}
47 void leaveLibrary() {} 88 void leaveLibrary() {}
48 89
90 void enterHtml(Uri uri) {}
91 void leaveHtml() {}
92
49 void enterSource(Source source) { 93 void enterSource(Source source) {
50 _file = new SourceFile(source.contents.data, url: source.uri); 94 _file = new SourceFile(source.contents.data, url: source.uri);
51 _current = source; 95 _current = source;
52 } 96 }
53 97
54 void leaveSource() { 98 void leaveSource() {
55 _file = null; 99 _file = null;
56 _current = null; 100 _current = null;
57 } 101 }
58 102
59 void log(StaticInfo info) { 103 void log(Message message) {
60 assert((info.node as dynamic).root.element.source == _current); 104 if (message is StaticInfo) {
61 final span = _spanForNode(_file, info.node); 105 assert((message.node as dynamic).root.element.source == _current);
62 final color = useColors ? colorOf(info.level.name) : null; 106 }
63 _checkerLogger.log(info.level, span.message(info.message, color: color)); 107 // TODO(sigmund): convert to use span information from AST (issue #73)
108 final span = message is MessageWithSpan
109 ? message.span
110 : _file.span(message.begin, message.end);
111 final level = message.level;
112 final color = useColors ? colorOf(level.name) : null;
113 _checkerLogger.log(level, span.message(message.message, color: color));
64 } 114 }
65 115
66 void logAnalyzerError(String message, Level level, int begin, int end) { 116 void clearLibrary(Uri uri) {}
67 var span = _file.span(begin, end); 117 void clearHtml(Uri uri) {}
68 final color = useColors ? colorOf(level.name) : null; 118 void clearAll() {}
69 _checkerLogger.log(
70 level, span.message('[from analyzer]: ${message}', color: color));
71 }
72 } 119 }
73 120
74 /// A reporter that gathers all the information in a [GlobalSummary]. 121 /// A reporter that gathers all the information in a [GlobalSummary].
75 class SummaryReporter implements CheckerReporter { 122 class SummaryReporter implements CheckerReporter {
76 GlobalSummary result = new GlobalSummary(); 123 GlobalSummary result = new GlobalSummary();
77 LibrarySummary _currentLibrary; 124 IndividualSummary _current;
78 SourceFile _file; 125 SourceFile _file;
79 126
80 clear() { 127 void enterLibrary(Uri uri) {
81 result = new GlobalSummary(); 128 var container;
82 }
83
84 void enterLibrary(LibraryInfo lib) {
85 var libKey = '${lib.library.source.uri}';
86 var libSummary = _currentLibrary = new LibrarySummary(libKey);
87
88 var uri = lib.library.source.uri;
89 if (uri.scheme == 'package') { 129 if (uri.scheme == 'package') {
90 var pname = path.split(uri.path)[0]; 130 var pname = path.split(uri.path)[0];
91 result.packages.putIfAbsent(pname, () => new PackageSummary(pname)); 131 result.packages.putIfAbsent(pname, () => new PackageSummary(pname));
92 if (result.packages[pname].libraries[libKey] != null) { 132 container = result.packages[pname].libraries;
93 print('ERROR: duplicate ${libKey}');
94 }
95 result.packages[pname].libraries[libKey] = libSummary;
96 } else if (uri.scheme == 'dart') { 133 } else if (uri.scheme == 'dart') {
97 if (result.system[libKey] != null) { 134 container = result.system;
98 print('ERROR: duplicate ${libKey}');
99 }
100 result.system[libKey] = libSummary;
101 } else { 135 } else {
102 if (result.loose[libKey] != null) { 136 container = result.loose;
103 print('ERROR: duplicate ${libKey}');
104 }
105 result.loose[libKey] = libSummary;
106 } 137 }
138 _current = container.putIfAbsent('$uri', () => new LibrarySummary('$uri'));
107 } 139 }
108 140
109 void leaveLibrary() { 141 void leaveLibrary() {
110 _currentLibrary = null; 142 _current = null;
143 }
144
145 void enterHtml(Uri uri) {
146 _current = result.loose.putIfAbsent('$uri', () => new HtmlSummary('$uri'));
147 }
148
149 void leaveHtml() {
150 _current = null;
111 } 151 }
112 152
113 void enterSource(Source source) { 153 void enterSource(Source source) {
114 _file = new SourceFile(source.contents.data, url: source.uri); 154 _file = new SourceFile(source.contents.data, url: source.uri);
115 _currentLibrary.lines += _file.lines; 155 if (_current is LibrarySummary) {
156 (_current as LibrarySummary).lines += _file.lines;
157 }
116 } 158 }
117 159
118 void leaveSource() { 160 void leaveSource() {
119 _file = null; 161 _file = null;
120 } 162 }
121 163
122 void log(StaticInfo info) { 164 void log(Message message) {
123 assert(_file != null); 165 assert(message is MessageWithSpan || _file != null);
124 var span = _spanForNode(_file, info.node); 166 // TODO(sigmund): convert to use span information from AST (issue #73)
125 _currentLibrary.messages.add(new MessageSummary('${info.runtimeType}', 167 final span = message is MessageWithSpan
126 info.level.name.toLowerCase(), span, info.message)); 168 ? message.span
169 : _file.span(message.begin, message.end);
170 _current.messages.add(new MessageSummary('${message.runtimeType}',
171 message.level.name.toLowerCase(), span, message.message));
127 } 172 }
128 173
129 void logAnalyzerError(String message, Level level, int begin, int end) { 174 void clearLibrary(Uri uri) {
130 var span = _file.span(begin, end); 175 enterLibrary(uri);
131 _currentLibrary.messages.add(new MessageSummary( 176 _current.messages.clear();
132 'AnalyzerError', level.name.toLowerCase(), span, message)); 177 (_current as LibrarySummary).lines = 0;
178 leaveLibrary();
179 }
180
181 void clearHtml(Uri uri) {
182 HtmlSummary htmlSummary = result.loose['$uri'];
183 if (htmlSummary != null) htmlSummary.messages.clear();
184 }
185
186 clearAll() {
187 result = new GlobalSummary();
133 } 188 }
134 } 189 }
135 190
136 /// Summary information computed by the DDC checker.
Siggi Cherem (dart-lang) 2015/03/07 02:43:01 note: these deletions are actually code that moved
137 abstract class Summary {
138 Map toJsonMap();
139
140 void accept(SummaryVisitor visitor);
141 }
142
143 /// Summary for the entire program.
144 class GlobalSummary implements Summary {
145 /// Summary from the system libaries.
146 final Map<String, LibrarySummary> system = <String, LibrarySummary>{};
147
148 /// Summary for libraries in packages.
149 final Map<String, PackageSummary> packages = <String, PackageSummary>{};
150
151 /// Summary for loose files
152 // TODO(sigmund): consider inferring the package from the pubspec instead?
153 final Map<String, LibrarySummary> loose = <String, LibrarySummary>{};
154
155 GlobalSummary();
156
157 Map toJsonMap() => {
158 'system': system.values.map((l) => l.toJsonMap()).toList(),
159 'packages': packages.values.map((p) => p.toJsonMap()).toList(),
160 'loose': loose.values.map((l) => l.toJsonMap()).toList(),
161 };
162
163 void accept(SummaryVisitor visitor) => visitor.visitGlobal(this);
164
165 static GlobalSummary parse(Map json) {
166 var res = new GlobalSummary();
167 json['system'].map(LibrarySummary.parse).forEach((l) {
168 res.system[l.name] = l;
169 });
170 json['packages'].map(PackageSummary.parse).forEach((p) {
171 res.packages[p.name] = p;
172 });
173 json['loose'].map(LibrarySummary.parse).forEach((l) {
174 res.loose[l.name] = l;
175 });
176 return res;
177 }
178 }
179
180 /// A summary of a package.
181 class PackageSummary implements Summary {
182 final String name;
183 final Map<String, LibrarySummary> libraries = <String, LibrarySummary>{};
184
185 PackageSummary(this.name);
186
187 Map toJsonMap() => {
188 'package_name': name,
189 'libraries': libraries.values.map((l) => l.toJsonMap()).toList(),
190 };
191
192 void accept(SummaryVisitor visitor) => visitor.visitPackage(this);
193
194 static PackageSummary parse(Map json) {
195 var res = new PackageSummary(json['package_name']);
196 json['libraries'].map(LibrarySummary.parse).forEach((l) {
197 res.libraries[l.name] = l;
198 });
199 return res;
200 }
201 }
202
203 /// A summary at the level of a library.
204 class LibrarySummary implements Summary {
205 /// Name of the library.
206 final String name;
207
208 /// All messages collected for the library.
209 final List<MessageSummary> messages;
210
211 /// Total lines of code (including all parts of the library).
212 int lines;
213
214 LibrarySummary(this.name, [List<MessageSummary> messages, this.lines = 0])
215 : messages = messages == null ? <MessageSummary>[] : messages;
216
217 Map toJsonMap() => {
218 'library_name': name,
219 'messages': messages.map((m) => m.toJsonMap()).toList(),
220 'lines': lines,
221 };
222
223 void accept(SummaryVisitor visitor) => visitor.visitLibrary(this);
224
225 static LibrarySummary parse(Map json) => new LibrarySummary(
226 json['library_name'], json['messages'].map(MessageSummary.parse).toList(),
227 json['lines']);
228 }
229
230 /// A single message produced by the checker.
231 class MessageSummary implements Summary {
232 /// The kind of message, currently the name of the StaticInfo type.
233 final String kind;
234
235 /// Level (error, warning, etc).
236 final String level;
237
238 /// Location where the error is reported.
239 final SourceSpan span;
240 final String message;
241
242 MessageSummary(this.kind, this.level, this.span, this.message);
243
244 Map toJsonMap() => {
245 'kind': kind,
246 'level': level,
247 'message': message,
248 'url': '${span.sourceUrl}',
249 'start': span.start.offset,
250 'end': span.end.offset,
251 'text': span.text,
252 };
253
254 void accept(SummaryVisitor visitor) => visitor.visitMessage(this);
255
256 static MessageSummary parse(Map json) {
257 var start = new SourceLocation(json['start'], sourceUrl: json['url']);
258 var end = new SourceLocation(json['end'], sourceUrl: json['url']);
259 var span = new SourceSpanBase(start, end, json['text']);
260 return new MessageSummary(
261 json['kind'], json['level'], span, json['message']);
262 }
263 }
264
265 /// A visitor of the [Summary] hierarchy.
266 abstract class SummaryVisitor {
267 void visitGlobal(GlobalSummary global);
268 void visitPackage(PackageSummary package);
269 void visitLibrary(LibrarySummary lib);
270 void visitMessage(MessageSummary message);
271 }
272
273 /// A recursive [SummaryVisitor] that visits summaries on a top-down fashion.
274 class RecursiveSummaryVisitor implements SummaryVisitor {
275 void visitGlobal(GlobalSummary global) {
276 for (var lib in global.system.values) {
277 lib.accept(this);
278 }
279 for (var package in global.packages.values) {
280 package.accept(this);
281 }
282 for (var lib in global.loose.values) {
283 lib.accept(this);
284 }
285 }
286
287 void visitPackage(PackageSummary package) {
288 for (var lib in package.libraries.values) {
289 lib.accept(this);
290 }
291 }
292 void visitLibrary(LibrarySummary lib) {
293 for (var msg in lib.messages) {
294 msg.accept(this);
295 }
296 }
297 void visitMessage(MessageSummary message) {}
298 }
299
300 /// Produces a string representation of the summary. 191 /// Produces a string representation of the summary.
301 String summaryToString(GlobalSummary summary) { 192 String summaryToString(GlobalSummary summary) {
302 var counter = new _Counter(); 193 var counter = new _Counter();
303 summary.accept(counter); 194 summary.accept(counter);
304 195
305 var table = new _Table(); 196 var table = new _Table();
306 // Declare columns and add header 197 // Declare columns and add header
307 table.declareColumn('package'); 198 table.declareColumn('package');
308 table.declareColumn('AnalyzerError', abbreviate: true); 199 table.declareColumn('AnalyzerError', abbreviate: true);
309 infoTypes.forEach((type) => table.declareColumn('$type', abbreviate: true)); 200 infoTypes.forEach((type) => table.declareColumn('$type', abbreviate: true));
(...skipping 174 matching lines...) Expand 10 before | Expand all | Expand 10 after
484 375
485 visitMessage(MessageSummary message) { 376 visitMessage(MessageSummary message) {
486 var kind = message.kind; 377 var kind = message.kind;
487 errorCount.putIfAbsent(currentPackage, () => <String, int>{}); 378 errorCount.putIfAbsent(currentPackage, () => <String, int>{});
488 errorCount[currentPackage].putIfAbsent(kind, () => 0); 379 errorCount[currentPackage].putIfAbsent(kind, () => 0);
489 errorCount[currentPackage][kind]++; 380 errorCount[currentPackage][kind]++;
490 totals.putIfAbsent(kind, () => 0); 381 totals.putIfAbsent(kind, () => 0);
491 totals[kind]++; 382 totals[kind]++;
492 } 383 }
493 } 384 }
494
495 /// Returns a [SourceSpan] in [file] for the offsets of [node].
496 // TODO(sigmund): convert to use span information from AST (issue #73)
497 SourceSpan _spanForNode(SourceFile file, AstNode node) {
498 final begin = node is AnnotatedNode
499 ? node.firstTokenAfterCommentAndMetadata.offset
500 : node.offset;
501 return file.span(begin, node.end);
502 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698