Index: lib/src/report.dart |
diff --git a/lib/src/report.dart b/lib/src/report.dart |
index d5ed23b6d0338ea9ac6db54359ec50c25c13f30f..5046c3b4ec6f39401fbfc888dd021bea79d424fd 100644 |
--- a/lib/src/report.dart |
+++ b/lib/src/report.dart |
@@ -7,30 +7,71 @@ library dev_compiler.src.report; |
import 'dart:math' show max; |
-import 'package:path/path.dart' as path; |
-import 'package:source_span/source_span.dart'; |
-import 'package:analyzer/src/generated/ast.dart'; |
import 'package:analyzer/src/generated/source.dart' show Source; |
import 'package:logging/logging.dart'; |
+import 'package:path/path.dart' as path; |
+import 'package:source_span/source_span.dart'; |
import 'info.dart'; |
import 'utils.dart'; |
+import 'summary.dart'; |
+ |
+/// A message (error or warning) produced by the dev_compiler and it's location |
+/// information. |
+/// |
+/// Currently the location information includes only the offsets within a file |
+/// where the error occurs. This is used in the context of a [CheckerReporter], |
+/// where the current file is being tracked. |
+abstract class Message { |
+ // Message description. |
+ final String message; |
+ |
+ /// Log level. This is a placeholder for severity. |
+ final Level level; |
+ |
+ /// Offset where the error message begins in the tracked source file. |
+ final int begin; |
+ |
+ /// Offset where the error message ends in the tracked source file. |
+ final int end; |
+ |
+ const Message(this.message, this.level, this.begin, this.end); |
+} |
+ |
+/// Like [Message], but with a precomputed source span. |
+abstract class MessageWithSpan implements Message { |
+ final String message; |
+ |
+ final Level level; |
+ |
+ final SourceSpan span; |
+ |
+ int get begin => span.start.offset; |
+ int get end => span.end.offset; |
+ |
+ const MessageWithSpan(this.message, this.level, this.span); |
+} |
// Interface used to report error messages from the checker. |
abstract class CheckerReporter { |
/// Called when starting to process a library. |
- void enterLibrary(LibraryInfo info); |
+ void enterLibrary(Uri uri); |
void leaveLibrary(); |
+ /// Called when starting to process an HTML source file. |
+ void enterHtml(Uri uri); |
+ void leaveHtml(); |
+ |
/// Called when starting to process a source. All subsequent log entries must |
/// belong to this source until the next call to enterSource. |
void enterSource(Source source); |
void leaveSource(); |
+ void log(Message message); |
- void log(StaticInfo info); |
- |
- // TODO(sigmund): merge this and [log] |
- void logAnalyzerError(String message, Level level, int begin, int end); |
+ // Called in server-mode. |
+ void clearLibrary(Uri uri); |
+ void clearHtml(Uri uri); |
+ void clearAll(); |
} |
final _checkerLogger = new Logger('dev_compiler.checker'); |
@@ -43,9 +84,12 @@ class LogReporter implements CheckerReporter { |
LogReporter([this.useColors = false]); |
- void enterLibrary(LibraryInfo info) {} |
+ void enterLibrary(Uri uri) {} |
void leaveLibrary() {} |
+ void enterHtml(Uri uri) {} |
+ void leaveHtml() {} |
+ |
void enterSource(Source source) { |
_file = new SourceFile(source.contents.data, url: source.uri); |
_current = source; |
@@ -56,245 +100,92 @@ class LogReporter implements CheckerReporter { |
_current = null; |
} |
- void log(StaticInfo info) { |
- assert((info.node as dynamic).root.element.source == _current); |
- final span = _spanForNode(_file, info.node); |
- final color = useColors ? colorOf(info.level.name) : null; |
- _checkerLogger.log(info.level, span.message(info.message, color: color)); |
- } |
- |
- void logAnalyzerError(String message, Level level, int begin, int end) { |
- var span = _file.span(begin, end); |
+ void log(Message message) { |
+ if (message is StaticInfo) { |
+ assert((message.node as dynamic).root.element.source == _current); |
+ } |
+ // TODO(sigmund): convert to use span information from AST (issue #73) |
+ final span = message is MessageWithSpan |
+ ? message.span |
+ : _file.span(message.begin, message.end); |
+ final level = message.level; |
final color = useColors ? colorOf(level.name) : null; |
- _checkerLogger.log( |
- level, span.message('[from analyzer]: ${message}', color: color)); |
+ _checkerLogger.log(level, span.message(message.message, color: color)); |
} |
+ |
+ void clearLibrary(Uri uri) {} |
+ void clearHtml(Uri uri) {} |
+ void clearAll() {} |
} |
/// A reporter that gathers all the information in a [GlobalSummary]. |
class SummaryReporter implements CheckerReporter { |
GlobalSummary result = new GlobalSummary(); |
- LibrarySummary _currentLibrary; |
+ IndividualSummary _current; |
SourceFile _file; |
- clear() { |
- result = new GlobalSummary(); |
- } |
- |
- void enterLibrary(LibraryInfo lib) { |
- var libKey = '${lib.library.source.uri}'; |
- var libSummary = _currentLibrary = new LibrarySummary(libKey); |
- |
- var uri = lib.library.source.uri; |
+ void enterLibrary(Uri uri) { |
+ var container; |
if (uri.scheme == 'package') { |
var pname = path.split(uri.path)[0]; |
result.packages.putIfAbsent(pname, () => new PackageSummary(pname)); |
- if (result.packages[pname].libraries[libKey] != null) { |
- print('ERROR: duplicate ${libKey}'); |
- } |
- result.packages[pname].libraries[libKey] = libSummary; |
+ container = result.packages[pname].libraries; |
} else if (uri.scheme == 'dart') { |
- if (result.system[libKey] != null) { |
- print('ERROR: duplicate ${libKey}'); |
- } |
- result.system[libKey] = libSummary; |
+ container = result.system; |
} else { |
- if (result.loose[libKey] != null) { |
- print('ERROR: duplicate ${libKey}'); |
- } |
- result.loose[libKey] = libSummary; |
+ container = result.loose; |
} |
+ _current = container.putIfAbsent('$uri', () => new LibrarySummary('$uri')); |
} |
void leaveLibrary() { |
- _currentLibrary = null; |
- } |
- |
- void enterSource(Source source) { |
- _file = new SourceFile(source.contents.data, url: source.uri); |
- _currentLibrary.lines += _file.lines; |
+ _current = null; |
} |
- void leaveSource() { |
- _file = null; |
+ void enterHtml(Uri uri) { |
+ _current = result.loose.putIfAbsent('$uri', () => new HtmlSummary('$uri')); |
} |
- void log(StaticInfo info) { |
- assert(_file != null); |
- var span = _spanForNode(_file, info.node); |
- _currentLibrary.messages.add(new MessageSummary('${info.runtimeType}', |
- info.level.name.toLowerCase(), span, info.message)); |
+ void leaveHtml() { |
+ _current = null; |
} |
- void logAnalyzerError(String message, Level level, int begin, int end) { |
- var span = _file.span(begin, end); |
- _currentLibrary.messages.add(new MessageSummary( |
- 'AnalyzerError', level.name.toLowerCase(), span, message)); |
+ void enterSource(Source source) { |
+ _file = new SourceFile(source.contents.data, url: source.uri); |
+ if (_current is LibrarySummary) { |
+ (_current as LibrarySummary).lines += _file.lines; |
+ } |
} |
-} |
- |
-/// 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
|
-abstract class Summary { |
- Map toJsonMap(); |
- void accept(SummaryVisitor visitor); |
-} |
- |
-/// Summary for the entire program. |
-class GlobalSummary implements Summary { |
- /// Summary from the system libaries. |
- final Map<String, LibrarySummary> system = <String, LibrarySummary>{}; |
- |
- /// Summary for libraries in packages. |
- final Map<String, PackageSummary> packages = <String, PackageSummary>{}; |
- |
- /// Summary for loose files |
- // TODO(sigmund): consider inferring the package from the pubspec instead? |
- final Map<String, LibrarySummary> loose = <String, LibrarySummary>{}; |
- |
- GlobalSummary(); |
- |
- Map toJsonMap() => { |
- 'system': system.values.map((l) => l.toJsonMap()).toList(), |
- 'packages': packages.values.map((p) => p.toJsonMap()).toList(), |
- 'loose': loose.values.map((l) => l.toJsonMap()).toList(), |
- }; |
- |
- void accept(SummaryVisitor visitor) => visitor.visitGlobal(this); |
- |
- static GlobalSummary parse(Map json) { |
- var res = new GlobalSummary(); |
- json['system'].map(LibrarySummary.parse).forEach((l) { |
- res.system[l.name] = l; |
- }); |
- json['packages'].map(PackageSummary.parse).forEach((p) { |
- res.packages[p.name] = p; |
- }); |
- json['loose'].map(LibrarySummary.parse).forEach((l) { |
- res.loose[l.name] = l; |
- }); |
- return res; |
+ void leaveSource() { |
+ _file = null; |
} |
-} |
- |
-/// A summary of a package. |
-class PackageSummary implements Summary { |
- final String name; |
- final Map<String, LibrarySummary> libraries = <String, LibrarySummary>{}; |
- |
- PackageSummary(this.name); |
- |
- Map toJsonMap() => { |
- 'package_name': name, |
- 'libraries': libraries.values.map((l) => l.toJsonMap()).toList(), |
- }; |
- void accept(SummaryVisitor visitor) => visitor.visitPackage(this); |
- |
- static PackageSummary parse(Map json) { |
- var res = new PackageSummary(json['package_name']); |
- json['libraries'].map(LibrarySummary.parse).forEach((l) { |
- res.libraries[l.name] = l; |
- }); |
- return res; |
+ void log(Message message) { |
+ assert(message is MessageWithSpan || _file != null); |
+ // TODO(sigmund): convert to use span information from AST (issue #73) |
+ final span = message is MessageWithSpan |
+ ? message.span |
+ : _file.span(message.begin, message.end); |
+ _current.messages.add(new MessageSummary('${message.runtimeType}', |
+ message.level.name.toLowerCase(), span, message.message)); |
} |
-} |
- |
-/// A summary at the level of a library. |
-class LibrarySummary implements Summary { |
- /// Name of the library. |
- final String name; |
- |
- /// All messages collected for the library. |
- final List<MessageSummary> messages; |
- |
- /// Total lines of code (including all parts of the library). |
- int lines; |
- |
- LibrarySummary(this.name, [List<MessageSummary> messages, this.lines = 0]) |
- : messages = messages == null ? <MessageSummary>[] : messages; |
- |
- Map toJsonMap() => { |
- 'library_name': name, |
- 'messages': messages.map((m) => m.toJsonMap()).toList(), |
- 'lines': lines, |
- }; |
- |
- void accept(SummaryVisitor visitor) => visitor.visitLibrary(this); |
- |
- static LibrarySummary parse(Map json) => new LibrarySummary( |
- json['library_name'], json['messages'].map(MessageSummary.parse).toList(), |
- json['lines']); |
-} |
-/// A single message produced by the checker. |
-class MessageSummary implements Summary { |
- /// The kind of message, currently the name of the StaticInfo type. |
- final String kind; |
- |
- /// Level (error, warning, etc). |
- final String level; |
- |
- /// Location where the error is reported. |
- final SourceSpan span; |
- final String message; |
- |
- MessageSummary(this.kind, this.level, this.span, this.message); |
- |
- Map toJsonMap() => { |
- 'kind': kind, |
- 'level': level, |
- 'message': message, |
- 'url': '${span.sourceUrl}', |
- 'start': span.start.offset, |
- 'end': span.end.offset, |
- 'text': span.text, |
- }; |
- |
- void accept(SummaryVisitor visitor) => visitor.visitMessage(this); |
- |
- static MessageSummary parse(Map json) { |
- var start = new SourceLocation(json['start'], sourceUrl: json['url']); |
- var end = new SourceLocation(json['end'], sourceUrl: json['url']); |
- var span = new SourceSpanBase(start, end, json['text']); |
- return new MessageSummary( |
- json['kind'], json['level'], span, json['message']); |
+ void clearLibrary(Uri uri) { |
+ enterLibrary(uri); |
+ _current.messages.clear(); |
+ (_current as LibrarySummary).lines = 0; |
+ leaveLibrary(); |
} |
-} |
- |
-/// A visitor of the [Summary] hierarchy. |
-abstract class SummaryVisitor { |
- void visitGlobal(GlobalSummary global); |
- void visitPackage(PackageSummary package); |
- void visitLibrary(LibrarySummary lib); |
- void visitMessage(MessageSummary message); |
-} |
-/// A recursive [SummaryVisitor] that visits summaries on a top-down fashion. |
-class RecursiveSummaryVisitor implements SummaryVisitor { |
- void visitGlobal(GlobalSummary global) { |
- for (var lib in global.system.values) { |
- lib.accept(this); |
- } |
- for (var package in global.packages.values) { |
- package.accept(this); |
- } |
- for (var lib in global.loose.values) { |
- lib.accept(this); |
- } |
+ void clearHtml(Uri uri) { |
+ HtmlSummary htmlSummary = result.loose['$uri']; |
+ if (htmlSummary != null) htmlSummary.messages.clear(); |
} |
- void visitPackage(PackageSummary package) { |
- for (var lib in package.libraries.values) { |
- lib.accept(this); |
- } |
- } |
- void visitLibrary(LibrarySummary lib) { |
- for (var msg in lib.messages) { |
- msg.accept(this); |
- } |
+ clearAll() { |
+ result = new GlobalSummary(); |
} |
- void visitMessage(MessageSummary message) {} |
} |
/// Produces a string representation of the summary. |
@@ -491,12 +382,3 @@ class _Counter extends RecursiveSummaryVisitor { |
totals[kind]++; |
} |
} |
- |
-/// Returns a [SourceSpan] in [file] for the offsets of [node]. |
-// TODO(sigmund): convert to use span information from AST (issue #73) |
-SourceSpan _spanForNode(SourceFile file, AstNode node) { |
- final begin = node is AnnotatedNode |
- ? node.firstTokenAfterCommentAndMetadata.offset |
- : node.offset; |
- return file.span(begin, node.end); |
-} |