OLD | NEW |
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/ast.dart' show AstNode, CompilationUnit; | |
11 import 'package:analyzer/src/generated/engine.dart' show AnalysisContext; | 10 import 'package:analyzer/src/generated/engine.dart' show AnalysisContext; |
12 import 'package:analyzer/src/generated/source.dart' show Source; | 11 import 'package:analyzer/src/generated/error.dart'; |
13 import 'package:logging/logging.dart'; | 12 import 'package:logging/logging.dart'; |
14 import 'package:path/path.dart' as path; | 13 import 'package:path/path.dart' as path; |
15 import 'package:source_span/source_span.dart'; | 14 import 'package:source_span/source_span.dart'; |
16 | 15 |
17 import 'info.dart'; | |
18 import 'utils.dart'; | 16 import 'utils.dart'; |
19 import 'summary.dart'; | 17 import 'summary.dart'; |
20 | 18 |
21 /// A message (error or warning) produced by the dev_compiler and it's location | 19 final _checkerLogger = new Logger('dev_compiler.checker'); |
22 /// information. | |
23 /// | |
24 /// Currently the location information includes only the offsets within a file | |
25 /// where the error occurs. This is used in the context of a [CheckerReporter], | |
26 /// where the current file is being tracked. | |
27 class Message { | |
28 // Message description. | |
29 final String message; | |
30 | 20 |
31 /// Log level. This is a placeholder for severity. | 21 SourceSpanWithContext _toSpan(AnalysisContext context, AnalysisError error) { |
32 final Level level; | 22 var source = error.source; |
| 23 var lineInfo = context.computeLineInfo(source); |
| 24 var content = context.getContents(source).data; |
| 25 var start = error.offset; |
| 26 var end = start + error.length; |
| 27 return createSpanHelper(lineInfo, start, end, source, content); |
| 28 } |
| 29 /// Simple reporter that logs checker messages as they are seen. |
| 30 class LogReporter implements AnalysisErrorListener { |
| 31 final AnalysisContext _context; |
| 32 final bool useColors; |
33 | 33 |
34 /// Offset where the error message begins in the tracked source file. | 34 LogReporter(this._context, {this.useColors: false}); |
35 final int begin; | |
36 | 35 |
37 /// Offset where the error message ends in the tracked source file. | 36 void onError(AnalysisError error) { |
38 final int end; | 37 var level = _severityToLevel[error.errorCode.errorSeverity]; |
39 | 38 |
40 const Message(this.message, this.level, this.begin, this.end); | 39 // Upgrade analyzer warnings to errors. |
| 40 // TODO(jmesserly: reconcile this... |
| 41 if (!error.errorCode.name.startsWith('dev_compiler.') && |
| 42 level == Level.WARNING) { |
| 43 level = Level.SEVERE; |
| 44 } |
| 45 |
| 46 var color = useColors ? colorOf(level.name) : null; |
| 47 |
| 48 // TODO(jmesserly): figure out what to do with the error's name. |
| 49 var text = '[${errorCodeName(error.errorCode)}] ' + error.message; |
| 50 text = _toSpan(_context, error).message(text, color: color); |
| 51 |
| 52 // TODO(jmesserly): just print these instead of sending through logger? |
| 53 _checkerLogger.log(level, text); |
| 54 } |
41 } | 55 } |
42 | 56 |
43 // Interface used to report error messages from the checker. | 57 // TODO(jmesserly): remove log levels, instead just use severity. |
44 abstract class CheckerReporter { | 58 const _severityToLevel = const { |
45 void log(Message message); | 59 ErrorSeverity.ERROR: Level.SEVERE, |
46 } | 60 ErrorSeverity.WARNING: Level.WARNING, |
47 | 61 ErrorSeverity.INFO: Level.INFO |
48 // Interface used to report error messages from the compiler. | 62 }; |
49 abstract class CompilerReporter extends CheckerReporter { | |
50 final AnalysisContext _context; | |
51 CompilationUnit _unit; | |
52 Source _unitSource; | |
53 | |
54 CompilerReporter(this._context); | |
55 | |
56 /// Called when starting to process a library. | |
57 void enterLibrary(Uri uri); | |
58 void leaveLibrary(); | |
59 | |
60 /// Called when starting to process an HTML source file. | |
61 void enterHtml(Uri uri); | |
62 void leaveHtml(); | |
63 | |
64 /// Called when starting to process a source. All subsequent log entries must | |
65 /// belong to this source until the next call to enterSource. | |
66 void enterCompilationUnit(CompilationUnit unit, [Source source]) { | |
67 _unit = unit; | |
68 _unitSource = source; | |
69 } | |
70 void leaveCompilationUnit() { | |
71 _unit = null; | |
72 _unitSource = null; | |
73 } | |
74 | |
75 // Called in server-mode. | |
76 void clearLibrary(Uri uri); | |
77 void clearHtml(Uri uri); | |
78 void clearAll(); | |
79 | |
80 SourceSpanWithContext _createSpan(int start, int end) => | |
81 createSpan(_context, _unit, start, end, _unitSource); | |
82 } | |
83 | |
84 final _checkerLogger = new Logger('dev_compiler.checker'); | |
85 | |
86 /// Simple reporter that logs checker messages as they are seen. | |
87 class LogReporter extends CompilerReporter { | |
88 final bool useColors; | |
89 Source _current; | |
90 | |
91 LogReporter(AnalysisContext context, {this.useColors: false}) | |
92 : super(context); | |
93 | |
94 void enterLibrary(Uri uri) {} | |
95 void leaveLibrary() {} | |
96 | |
97 void enterHtml(Uri uri) {} | |
98 void leaveHtml() {} | |
99 | |
100 void log(Message message) { | |
101 if (message is StaticInfo) { | |
102 assert(message.node.root == _unit); | |
103 } | |
104 // TODO(sigmund): convert to use span information from AST (issue #73) | |
105 final span = _createSpan(message.begin, message.end); | |
106 final level = message.level; | |
107 final color = useColors ? colorOf(level.name) : null; | |
108 final text = '[${message.runtimeType}] ${message.message}'; | |
109 _checkerLogger.log(level, span.message(text, color: color)); | |
110 } | |
111 | |
112 void clearLibrary(Uri uri) {} | |
113 void clearHtml(Uri uri) {} | |
114 void clearAll() {} | |
115 } | |
116 | 63 |
117 /// A reporter that gathers all the information in a [GlobalSummary]. | 64 /// A reporter that gathers all the information in a [GlobalSummary]. |
118 class SummaryReporter extends CompilerReporter { | 65 class SummaryReporter implements AnalysisErrorListener { |
119 GlobalSummary result = new GlobalSummary(); | 66 GlobalSummary result = new GlobalSummary(); |
120 IndividualSummary _current; | |
121 final Level _level; | 67 final Level _level; |
| 68 final AnalysisContext _context; |
122 | 69 |
123 SummaryReporter(AnalysisContext context, [this._level = Level.ALL]) | 70 SummaryReporter(this._context, [this._level = Level.ALL]); |
124 : super(context); | |
125 | 71 |
126 void enterLibrary(Uri uri) { | 72 IndividualSummary _getIndividualSummary(Uri uri) { |
| 73 if (uri.path.endsWith('.html')) { |
| 74 return result.loose.putIfAbsent('$uri', () => new HtmlSummary('$uri')); |
| 75 } |
| 76 |
127 var container; | 77 var container; |
128 if (uri.scheme == 'package') { | 78 if (uri.scheme == 'package') { |
129 var pname = path.split(uri.path)[0]; | 79 var pname = path.split(uri.path)[0]; |
130 result.packages.putIfAbsent(pname, () => new PackageSummary(pname)); | 80 result.packages.putIfAbsent(pname, () => new PackageSummary(pname)); |
131 container = result.packages[pname].libraries; | 81 container = result.packages[pname].libraries; |
132 } else if (uri.scheme == 'dart') { | 82 } else if (uri.scheme == 'dart') { |
133 container = result.system; | 83 container = result.system; |
134 } else { | 84 } else { |
135 container = result.loose; | 85 container = result.loose; |
136 } | 86 } |
137 _current = container.putIfAbsent('$uri', () => new LibrarySummary('$uri')); | 87 return container.putIfAbsent('$uri', () => new LibrarySummary('$uri')); |
138 } | 88 } |
139 | 89 |
140 void leaveLibrary() { | 90 void onError(AnalysisError error) { |
141 _current = null; | 91 // Only summarize messages per configured logging level |
142 } | 92 var code = error.errorCode; |
| 93 if (_severityToLevel[code.errorSeverity] < _level) return; |
143 | 94 |
144 void enterHtml(Uri uri) { | 95 var span = _toSpan(_context, error); |
145 _current = result.loose.putIfAbsent('$uri', () => new HtmlSummary('$uri')); | 96 var summary = _getIndividualSummary(error.source.uri); |
146 } | 97 if (summary is LibrarySummary) { |
147 | 98 summary.countSourceLines(_context, error.source); |
148 void leaveHtml() { | |
149 _current = null; | |
150 } | |
151 | |
152 @override | |
153 void enterCompilationUnit(CompilationUnit unit, [Source source]) { | |
154 super.enterCompilationUnit(unit, source); | |
155 if (_current is LibrarySummary) { | |
156 int lines = _unit.lineInfo.getLocation(_unit.endToken.end).lineNumber; | |
157 (_current as LibrarySummary).lines += lines; | |
158 } | 99 } |
159 } | 100 summary.messages.add(new MessageSummary( |
160 | 101 errorCodeName(code), code.errorSeverity.displayName, span, error.message
)); |
161 void log(Message message) { | |
162 // Only summarize messages per configured logging level | |
163 if (message.level < _level) return; | |
164 final span = _createSpan(message.begin, message.end); | |
165 _current.messages.add(new MessageSummary('${message.runtimeType}', | |
166 message.level.name.toLowerCase(), span, message.message)); | |
167 } | 102 } |
168 | 103 |
169 void clearLibrary(Uri uri) { | 104 void clearLibrary(Uri uri) { |
170 enterLibrary(uri); | 105 (_getIndividualSummary(uri) as LibrarySummary).clear(); |
171 _current.messages.clear(); | |
172 (_current as LibrarySummary).lines = 0; | |
173 leaveLibrary(); | |
174 } | 106 } |
175 | 107 |
176 void clearHtml(Uri uri) { | 108 void clearHtml(Uri uri) { |
177 HtmlSummary htmlSummary = result.loose['$uri']; | 109 HtmlSummary htmlSummary = result.loose['$uri']; |
178 if (htmlSummary != null) htmlSummary.messages.clear(); | 110 if (htmlSummary != null) htmlSummary.messages.clear(); |
179 } | 111 } |
180 | |
181 clearAll() { | |
182 result = new GlobalSummary(); | |
183 } | |
184 } | 112 } |
185 | 113 |
186 /// Produces a string representation of the summary. | 114 /// Produces a string representation of the summary. |
187 String summaryToString(GlobalSummary summary) { | 115 String summaryToString(GlobalSummary summary) { |
188 var counter = new _Counter(); | 116 var counter = new _Counter(); |
189 summary.accept(counter); | 117 summary.accept(counter); |
190 | 118 |
191 var table = new _Table(); | 119 var table = new _Table(); |
192 // Declare columns and add header | 120 // Declare columns and add header |
193 table.declareColumn('package'); | 121 table.declareColumn('package'); |
(...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
372 | 300 |
373 visitMessage(MessageSummary message) { | 301 visitMessage(MessageSummary message) { |
374 var kind = message.kind; | 302 var kind = message.kind; |
375 errorCount.putIfAbsent(currentPackage, () => <String, int>{}); | 303 errorCount.putIfAbsent(currentPackage, () => <String, int>{}); |
376 errorCount[currentPackage].putIfAbsent(kind, () => 0); | 304 errorCount[currentPackage].putIfAbsent(kind, () => 0); |
377 errorCount[currentPackage][kind]++; | 305 errorCount[currentPackage][kind]++; |
378 totals.putIfAbsent(kind, () => 0); | 306 totals.putIfAbsent(kind, () => 0); |
379 totals[kind]++; | 307 totals[kind]++; |
380 } | 308 } |
381 } | 309 } |
OLD | NEW |