Index: pkg/compiler/lib/src/compiler.dart |
diff --git a/pkg/compiler/lib/src/compiler.dart b/pkg/compiler/lib/src/compiler.dart |
index 6ebb7a56ba782611176b0a4fd6e79451fc10195c..5df0af3c5812b6ca112e299d66a14df9155beb58 100644 |
--- a/pkg/compiler/lib/src/compiler.dart |
+++ b/pkg/compiler/lib/src/compiler.dart |
@@ -44,7 +44,10 @@ import 'dart_types.dart' show |
Types; |
import 'deferred_load.dart' show DeferredLoadTask, OutputUnit; |
import 'diagnostics/code_location.dart'; |
-import 'diagnostics/diagnostic_listener.dart'; |
+import 'diagnostics/diagnostic_listener.dart' show |
+ DiagnosticMessage, |
+ DiagnosticOptions, |
+ DiagnosticReporter; |
import 'diagnostics/invariant.dart' show |
invariant, |
REPORT_EXCESS_RESOLUTION; |
@@ -139,13 +142,14 @@ import 'util/util.dart' show |
import 'world.dart' show |
World; |
-abstract class Compiler extends DiagnosticListener { |
+abstract class Compiler { |
final Stopwatch totalCompileTime = new Stopwatch(); |
int nextFreeClassId = 0; |
World world; |
Types types; |
_CompilerCoreTypes _coreTypes; |
+ _CompilerDiagnosticReporter _reporter; |
_CompilerResolution _resolution; |
_CompilerParsing _parsing; |
@@ -248,14 +252,6 @@ abstract class Compiler extends DiagnosticListener { |
/// If `true`, warnings and hints not from user code are reported. |
final bool showPackageWarnings; |
- /// `true` if the last diagnostic was filtered, in which case the |
- /// accompanying info message should be filtered as well. |
- bool lastDiagnosticWasFiltered = false; |
- |
- /// Map containing information about the warnings and hints that have been |
- /// suppressed for each library. |
- Map<Uri, SuppressionInfo> suppressedWarnings = <Uri, SuppressionInfo>{}; |
- |
final bool suppressWarnings; |
final bool fatalWarnings; |
@@ -287,7 +283,6 @@ abstract class Compiler extends DiagnosticListener { |
Tracer tracer; |
CompilerTask measuredTask; |
- Element _currentElement; |
LibraryElement coreLibrary; |
LibraryElement asyncLibrary; |
@@ -318,6 +313,7 @@ abstract class Compiler extends DiagnosticListener { |
ClassElement get iterableClass => _coreTypes.iterableClass; |
ClassElement get streamClass => _coreTypes.streamClass; |
+ DiagnosticReporter get reporter => _reporter; |
CoreTypes get coreTypes => _coreTypes; |
Resolution get resolution => _resolution; |
Parsing get parsing => _parsing; |
@@ -378,49 +374,7 @@ abstract class Compiler extends DiagnosticListener { |
fromEnvironment(String name) => null; |
- Element get currentElement => _currentElement; |
- |
- String tryToString(object) { |
- try { |
- return object.toString(); |
- } catch (_) { |
- return '<exception in toString()>'; |
- } |
- } |
- |
- /** |
- * Perform an operation, [f], returning the return value from [f]. If an |
- * error occurs then report it as having occurred during compilation of |
- * [element]. Can be nested. |
- */ |
- withCurrentElement(Element element, f()) { |
- Element old = currentElement; |
- _currentElement = element; |
- try { |
- return f(); |
- } on SpannableAssertionFailure catch (ex) { |
- if (!hasCrashed) { |
- reportAssertionFailure(ex); |
- pleaseReportCrash(); |
- } |
- hasCrashed = true; |
- rethrow; |
- } on StackOverflowError { |
- // We cannot report anything useful in this case, because we |
- // do not have enough stack space. |
- rethrow; |
- } catch (ex) { |
- if (hasCrashed) rethrow; |
- try { |
- unhandledExceptionOnElement(element); |
- } catch (doubleFault) { |
- // Ignoring exceptions in exception handling. |
- } |
- rethrow; |
- } finally { |
- _currentElement = old; |
- } |
- } |
+ Element get currentElement => _reporter.currentElement; |
List<CompilerTask> tasks; |
ScannerTask scanner; |
@@ -464,6 +418,8 @@ abstract class Compiler extends DiagnosticListener { |
bool enabledInvokeOn = false; |
bool hasIsolateSupport = false; |
+ bool get hasCrashed => _reporter.hasCrashed; |
+ |
Stopwatch progress; |
bool get shouldPrintProgress { |
@@ -487,8 +443,6 @@ abstract class Compiler extends DiagnosticListener { |
compilationFailedInternal = value; |
} |
- bool hasCrashed = false; |
- |
/// Set by the backend if real reflection is detected in use of dart:mirrors. |
bool disableTypeInferenceForMirrors = false; |
@@ -549,6 +503,11 @@ abstract class Compiler extends DiagnosticListener { |
world = new World(this); |
// TODO(johnniwinther): Initialize core types in [initializeCoreClasses] and |
// make its field final. |
+ _reporter = new _CompilerDiagnosticReporter(this, |
+ new DiagnosticOptions( |
+ suppressWarnings: suppressWarnings, |
+ terseDiagnostics: terseDiagnostics, |
+ showPackageWarnings: showPackageWarnings)); |
_parsing = new _CompilerParsing(this); |
_resolution = new _CompilerResolution(this); |
_coreTypes = new _CompilerCoreTypes(_resolution); |
@@ -616,118 +575,15 @@ abstract class Compiler extends DiagnosticListener { |
int getNextFreeClassId() => nextFreeClassId++; |
void unimplemented(Spannable spannable, String methodName) { |
- internalError(spannable, "$methodName not implemented."); |
- } |
- |
- internalError(Spannable node, reason) { |
- String message = tryToString(reason); |
- reportDiagnosticInternal( |
- createMessage(node, MessageKind.GENERIC, {'text': message}), |
- const <DiagnosticMessage>[], |
- api.Diagnostic.CRASH); |
- throw 'Internal Error: $message'; |
- } |
- |
- void unhandledExceptionOnElement(Element element) { |
- if (hasCrashed) return; |
- hasCrashed = true; |
- reportDiagnostic( |
- createMessage(element, MessageKind.COMPILER_CRASHED), |
- const <DiagnosticMessage>[], |
- api.Diagnostic.CRASH); |
- pleaseReportCrash(); |
- } |
- |
- void pleaseReportCrash() { |
- print( |
- MessageTemplate.TEMPLATES[MessageKind.PLEASE_REPORT_THE_CRASH] |
- .message({'buildId': buildId})); |
- } |
- |
- SourceSpan spanFromSpannable(Spannable node) { |
- // TODO(johnniwinther): Disallow `node == null` ? |
- if (node == null) return null; |
- if (node == CURRENT_ELEMENT_SPANNABLE) { |
- node = currentElement; |
- } else if (node == NO_LOCATION_SPANNABLE) { |
- if (currentElement == null) return null; |
- node = currentElement; |
- } |
- if (node is SourceSpan) { |
- return node; |
- } else if (node is Node) { |
- return spanFromNode(node); |
- } else if (node is TokenPair) { |
- return spanFromTokens(node.begin, node.end); |
- } else if (node is Token) { |
- return spanFromTokens(node, node); |
- } else if (node is HInstruction) { |
- return spanFromHInstruction(node); |
- } else if (node is Element) { |
- return spanFromElement(node); |
- } else if (node is MetadataAnnotation) { |
- Uri uri = node.annotatedElement.compilationUnit.script.resourceUri; |
- return spanFromTokens(node.beginToken, node.endToken, uri); |
- } else if (node is Local) { |
- Local local = node; |
- return spanFromElement(local.executableContext); |
- } else { |
- throw 'No error location.'; |
- } |
- } |
- |
- Element _elementFromHInstruction(HInstruction instruction) { |
- return instruction.sourceElement is Element |
- ? instruction.sourceElement : null; |
- } |
- |
- /// Finds the approximate [Element] for [node]. [currentElement] is used as |
- /// the default value. |
- Element elementFromSpannable(Spannable node) { |
- Element element; |
- if (node is Element) { |
- element = node; |
- } else if (node is HInstruction) { |
- element = _elementFromHInstruction(node); |
- } else if (node is MetadataAnnotation) { |
- element = node.annotatedElement; |
- } |
- return element != null ? element : currentElement; |
- } |
- |
- void log(message) { |
- Message msg = MessageTemplate.TEMPLATES[MessageKind.GENERIC] |
- .message({'text': '$message'}); |
- reportDiagnostic( |
- new DiagnosticMessage(null, null, msg), |
- const <DiagnosticMessage>[], |
- api.Diagnostic.VERBOSE_INFO); |
+ reporter.internalError(spannable, "$methodName not implemented."); |
} |
Future<bool> run(Uri uri) { |
totalCompileTime.start(); |
- return new Future.sync(() => runCompiler(uri)).catchError((error) { |
- try { |
- if (!hasCrashed) { |
- hasCrashed = true; |
- if (error is SpannableAssertionFailure) { |
- reportAssertionFailure(error); |
- } else { |
- reportDiagnostic( |
- createMessage( |
- new SourceSpan(uri, 0, 0), |
- MessageKind.COMPILER_CRASHED), |
- const <DiagnosticMessage>[], |
- api.Diagnostic.CRASH); |
- } |
- pleaseReportCrash(); |
- } |
- } catch (doubleFault) { |
- // Ignoring exceptions in exception handling. |
- } |
- throw error; |
- }).whenComplete(() { |
+ return new Future.sync(() => runCompiler(uri)) |
+ .catchError((error) => _reporter.onError(uri, error)) |
+ .whenComplete(() { |
tracer.close(); |
totalCompileTime.stop(); |
}).then((_) { |
@@ -866,7 +722,7 @@ abstract class Compiler extends DiagnosticListener { |
if (loadedLibraries.containsLibrary(uri)) { |
Set<String> importChains = |
computeImportChainsFor(loadedLibraries, Uri.parse('dart:io')); |
- reportInfo(NO_LOCATION_SPANNABLE, |
+ reporter.reportInfo(NO_LOCATION_SPANNABLE, |
MessageKind.DISALLOWED_LIBRARY_IMPORT, |
{'uri': uri, |
'importChain': importChains.join( |
@@ -883,7 +739,7 @@ abstract class Compiler extends DiagnosticListener { |
if (importsMirrorsLibrary && !backend.supportsReflection) { |
Set<String> importChains = |
computeImportChainsFor(loadedLibraries, Uris.dart_mirrors); |
- reportErrorMessage( |
+ reporter.reportErrorMessage( |
NO_LOCATION_SPANNABLE, |
MessageKind.MIRRORS_LIBRARY_NOT_SUPPORT_BY_BACKEND, |
{'importChain': importChains.join( |
@@ -891,7 +747,7 @@ abstract class Compiler extends DiagnosticListener { |
} else if (importsMirrorsLibrary && !enableExperimentalMirrors) { |
Set<String> importChains = |
computeImportChainsFor(loadedLibraries, Uris.dart_mirrors); |
- reportWarningMessage( |
+ reporter.reportWarningMessage( |
NO_LOCATION_SPANNABLE, |
MessageKind.IMPORT_EXPERIMENTAL_MIRRORS, |
{'importChain': importChains.join( |
@@ -925,10 +781,10 @@ abstract class Compiler extends DiagnosticListener { |
Element findRequiredElement(LibraryElement library, String name) { |
var element = library.find(name); |
if (element == null) { |
- internalError(library, |
+ reporter.internalError(library, |
"The library '${library.canonicalUri}' does not contain required " |
"element: '$name'."); |
- } |
+ } |
return element; |
} |
@@ -983,7 +839,7 @@ abstract class Compiler extends DiagnosticListener { |
_coreTypes.iterableClass = lookupCoreClass('Iterable'); |
_coreTypes.symbolClass = lookupCoreClass('Symbol'); |
if (!missingCoreClasses.isEmpty) { |
- internalError( |
+ reporter.internalError( |
coreLibrary, |
'dart:core library does not contain required classes: ' |
'$missingCoreClasses'); |
@@ -1020,16 +876,16 @@ abstract class Compiler extends DiagnosticListener { |
return new Future.sync(() { |
if (librariesToAnalyzeWhenRun != null) { |
return Future.forEach(librariesToAnalyzeWhenRun, (libraryUri) { |
- log('Analyzing $libraryUri ($buildId)'); |
+ reporter.log('Analyzing $libraryUri ($buildId)'); |
return libraryLoader.loadLibrary(libraryUri); |
}); |
} |
}).then((_) { |
if (uri != null) { |
if (analyzeOnly) { |
- log('Analyzing $uri ($buildId)'); |
+ reporter.log('Analyzing $uri ($buildId)'); |
} else { |
- log('Compiling $uri ($buildId)'); |
+ reporter.log('Compiling $uri ($buildId)'); |
} |
return libraryLoader.loadLibrary(uri).then((LibraryElement library) { |
mainApp = library; |
@@ -1063,7 +919,7 @@ abstract class Compiler extends DiagnosticListener { |
if (main is ErroneousElement) { |
errorElement = main; |
} else { |
- internalError(main, 'Problem with ${Identifiers.main}.'); |
+ reporter.internalError(main, 'Problem with ${Identifiers.main}.'); |
} |
mainFunction = backend.helperForBadMain(); |
} else if (!main.isFunction) { |
@@ -1091,7 +947,7 @@ abstract class Compiler extends DiagnosticListener { |
} |
if (mainFunction == null) { |
if (errorElement == null && !analyzeOnly && !analyzeAll) { |
- internalError(mainApp, "Problem with '${Identifiers.main}'."); |
+ reporter.internalError(mainApp, "Problem with '${Identifiers.main}'."); |
} else { |
mainFunction = errorElement; |
} |
@@ -1099,7 +955,7 @@ abstract class Compiler extends DiagnosticListener { |
if (errorElement != null && |
errorElement.isSynthesized && |
!mainApp.isSynthesized) { |
- reportWarningMessage( |
+ reporter.reportWarningMessage( |
errorElement, errorElement.messageKind, |
errorElement.messageArguments); |
} |
@@ -1116,7 +972,7 @@ abstract class Compiler extends DiagnosticListener { |
Uri libraryUri, |
{bool skipLibraryWithPartOfTag: true}) { |
assert(analyzeMain); |
- log('Analyzing $libraryUri ($buildId)'); |
+ reporter.log('Analyzing $libraryUri ($buildId)'); |
return libraryLoader.loadLibrary(libraryUri).then((LibraryElement library) { |
var compilationUnit = library.compilationUnit; |
if (skipLibraryWithPartOfTag && compilationUnit.partTag != null) { |
@@ -1124,7 +980,7 @@ abstract class Compiler extends DiagnosticListener { |
} |
fullyEnqueueLibrary(library, enqueuer.resolution); |
emptyQueue(enqueuer.resolution); |
- enqueuer.resolution.logSummary(log); |
+ enqueuer.resolution.logSummary(reporter.log); |
return library; |
}); |
} |
@@ -1144,7 +1000,7 @@ abstract class Compiler extends DiagnosticListener { |
phase = PHASE_RESOLVING; |
if (analyzeAll) { |
libraryLoader.libraries.forEach((LibraryElement library) { |
- log('Enqueuing ${library.canonicalUri}'); |
+ reporter.log('Enqueuing ${library.canonicalUri}'); |
fullyEnqueueLibrary(library, enqueuer.resolution); |
}); |
} else if (analyzeMain && mainApp != null) { |
@@ -1154,30 +1010,11 @@ abstract class Compiler extends DiagnosticListener { |
// that are not pulled in by a particular element. |
backend.enqueueHelpers(enqueuer.resolution, globalDependencies); |
resolveLibraryMetadata(); |
- log('Resolving...'); |
+ reporter.log('Resolving...'); |
processQueue(enqueuer.resolution, mainFunction); |
- enqueuer.resolution.logSummary(log); |
+ enqueuer.resolution.logSummary(reporter.log); |
- if (!showPackageWarnings && !suppressWarnings) { |
- suppressedWarnings.forEach((Uri uri, SuppressionInfo info) { |
- MessageKind kind = MessageKind.HIDDEN_WARNINGS_HINTS; |
- if (info.warnings == 0) { |
- kind = MessageKind.HIDDEN_HINTS; |
- } else if (info.hints == 0) { |
- kind = MessageKind.HIDDEN_WARNINGS; |
- } |
- MessageTemplate template = MessageTemplate.TEMPLATES[kind]; |
- Message message = template.message( |
- {'warnings': info.warnings, |
- 'hints': info.hints, |
- 'uri': uri}, |
- terseDiagnostics); |
- reportDiagnostic( |
- new DiagnosticMessage(null, null, message), |
- const <DiagnosticMessage>[], |
- api.Diagnostic.HINT); |
- }); |
- } |
+ _reporter.reportSuppressedMessagesSummary(); |
if (compilationFailed){ |
if (!generateCodeWithCompileTimeErrors) return; |
@@ -1208,14 +1045,14 @@ abstract class Compiler extends DiagnosticListener { |
deferredLoadTask.onResolutionComplete(mainFunction); |
- log('Inferring types...'); |
+ reporter.log('Inferring types...'); |
typesTask.onResolutionComplete(mainFunction); |
if (stopAfterTypeInference) return; |
backend.onTypeInferenceComplete(); |
- log('Compiling...'); |
+ reporter.log('Compiling...'); |
phase = PHASE_COMPILING; |
backend.onCodegenStart(); |
// TODO(johnniwinther): Move these to [CodegenEnqueuer]. |
@@ -1228,7 +1065,7 @@ abstract class Compiler extends DiagnosticListener { |
}); |
} |
processQueue(enqueuer.codegen, mainFunction); |
- enqueuer.codegen.logSummary(log); |
+ enqueuer.codegen.logSummary(reporter.log); |
int programSize = backend.assembleProgram(); |
@@ -1277,7 +1114,7 @@ abstract class Compiler extends DiagnosticListener { |
*/ |
void emptyQueue(Enqueuer world) { |
world.forEach((WorkItem work) { |
- withCurrentElement(work.element, () { |
+ reporter.withCurrentElement(work.element, () { |
world.applyImpact(work.element, work.run(this, world)); |
}); |
}); |
@@ -1319,7 +1156,7 @@ abstract class Compiler extends DiagnosticListener { |
checkQueues() { |
for (Enqueuer world in [enqueuer.resolution, enqueuer.codegen]) { |
world.forEach((WorkItem work) { |
- internalError(work.element, "Work list is not empty."); |
+ reporter.internalError(work.element, "Work list is not empty."); |
}); |
} |
if (!REPORT_EXCESS_RESOLUTION) return; |
@@ -1342,9 +1179,9 @@ abstract class Compiler extends DiagnosticListener { |
resolved.remove(e); |
} |
} |
- log('Excess resolution work: ${resolved.length}.'); |
+ reporter.log('Excess resolution work: ${resolved.length}.'); |
for (Element e in resolved) { |
- reportWarningMessage(e, |
+ reporter.reportWarningMessage(e, |
MessageKind.GENERIC, |
{'text': 'Warning: $e resolved but not compiled.'}); |
} |
@@ -1386,7 +1223,8 @@ abstract class Compiler extends DiagnosticListener { |
// TODO(ahe): Add structured diagnostics to the compiler API and |
// use it to separate this from the --verbose option. |
if (phase == PHASE_RESOLVING) { |
- log('Resolved ${enqueuer.resolution.resolvedElements.length} ' |
+ reporter.log( |
+ 'Resolved ${enqueuer.resolution.resolvedElements.length} ' |
'elements.'); |
progress.reset(); |
} |
@@ -1405,195 +1243,61 @@ abstract class Compiler extends DiagnosticListener { |
if (shouldPrintProgress) { |
// TODO(ahe): Add structured diagnostics to the compiler API and |
// use it to separate this from the --verbose option. |
- log('Compiled ${enqueuer.codegen.generatedCode.length} methods.'); |
+ reporter.log( |
+ 'Compiled ${enqueuer.codegen.generatedCode.length} methods.'); |
progress.reset(); |
} |
return backend.codegen(work); |
} |
- DiagnosticMessage createMessage( |
- Spannable spannable, |
- MessageKind messageKind, |
- [Map arguments = const {}]) { |
- SourceSpan span = spanFromSpannable(spannable); |
- MessageTemplate template = MessageTemplate.TEMPLATES[messageKind]; |
- Message message = template.message(arguments, terseDiagnostics); |
- return new DiagnosticMessage(span, spannable, message); |
- } |
+ void reportDiagnostic(DiagnosticMessage message, |
+ List<DiagnosticMessage> infos, |
+ api.Diagnostic kind); |
- void reportError( |
- DiagnosticMessage message, |
- [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]) { |
- reportDiagnosticInternal(message, infos, api.Diagnostic.ERROR); |
+ void reportCrashInUserCode(String message, exception, stackTrace) { |
+ _reporter.onCrashInUserCode(message, exception, stackTrace); |
} |
- void reportWarning( |
- DiagnosticMessage message, |
- [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]) { |
- reportDiagnosticInternal(message, infos, api.Diagnostic.WARNING); |
+ /** |
+ * Translates the [resolvedUri] into a readable URI. |
+ * |
+ * The [importingLibrary] holds the library importing [resolvedUri] or |
+ * [:null:] if [resolvedUri] is loaded as the main library. The |
+ * [importingLibrary] is used to grant access to internal libraries from |
+ * platform libraries and patch libraries. |
+ * |
+ * If the [resolvedUri] is not accessible from [importingLibrary], this method |
+ * is responsible for reporting errors. |
+ * |
+ * See [LibraryLoader] for terminology on URIs. |
+ */ |
+ Uri translateResolvedUri(LibraryElement importingLibrary, |
+ Uri resolvedUri, Spannable spannable) { |
+ unimplemented(importingLibrary, 'Compiler.translateResolvedUri'); |
+ return null; |
} |
- void reportHint( |
- DiagnosticMessage message, |
- [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]) { |
- reportDiagnosticInternal(message, infos, api.Diagnostic.HINT); |
+ /** |
+ * Reads the script specified by the [readableUri]. |
+ * |
+ * See [LibraryLoader] for terminology on URIs. |
+ */ |
+ Future<Script> readScript(Spannable node, Uri readableUri) { |
+ unimplemented(node, 'Compiler.readScript'); |
+ return null; |
} |
- @deprecated |
- void reportInfo(Spannable node, MessageKind messageKind, |
- [Map arguments = const {}]) { |
- reportDiagnosticInternal( |
- createMessage(node, messageKind, arguments), |
- const <DiagnosticMessage>[], |
- api.Diagnostic.INFO); |
+ /// Compatible with [readScript] and used by [LibraryLoader] to create |
+ /// synthetic scripts to recover from read errors and bad URIs. |
+ Future<Script> synthesizeScript(Spannable node, Uri readableUri) { |
+ unimplemented(node, 'Compiler.synthesizeScript'); |
+ return null; |
} |
- void reportDiagnosticInternal(DiagnosticMessage message, |
- List<DiagnosticMessage> infos, |
- api.Diagnostic kind) { |
- if (!showPackageWarnings && message.spannable != NO_LOCATION_SPANNABLE) { |
- switch (kind) { |
- case api.Diagnostic.WARNING: |
- case api.Diagnostic.HINT: |
- Element element = elementFromSpannable(message.spannable); |
- if (!inUserCode(element, assumeInUserCode: true)) { |
- Uri uri = getCanonicalUri(element); |
- SuppressionInfo info = |
- suppressedWarnings.putIfAbsent(uri, () => new SuppressionInfo()); |
- if (kind == api.Diagnostic.WARNING) { |
- info.warnings++; |
- } else { |
- info.hints++; |
- } |
- lastDiagnosticWasFiltered = true; |
- return; |
- } |
- break; |
- case api.Diagnostic.INFO: |
- if (lastDiagnosticWasFiltered) { |
- return; |
- } |
- break; |
- } |
- } |
- lastDiagnosticWasFiltered = false; |
- reportDiagnostic(message, infos, kind); |
- } |
- |
- void reportDiagnostic(DiagnosticMessage message, |
- List<DiagnosticMessage> infos, |
- api.Diagnostic kind); |
- |
- void reportAssertionFailure(SpannableAssertionFailure ex) { |
- String message = (ex.message != null) ? tryToString(ex.message) |
- : tryToString(ex); |
- reportDiagnosticInternal( |
- createMessage(ex.node, MessageKind.GENERIC, {'text': message}), |
- const <DiagnosticMessage>[], |
- api.Diagnostic.CRASH); |
- } |
- |
- SourceSpan spanFromTokens(Token begin, Token end, [Uri uri]) { |
- if (begin == null || end == null) { |
- // TODO(ahe): We can almost always do better. Often it is only |
- // end that is null. Otherwise, we probably know the current |
- // URI. |
- throw 'Cannot find tokens to produce error message.'; |
- } |
- if (uri == null && currentElement != null) { |
- uri = currentElement.compilationUnit.script.resourceUri; |
- } |
- return new SourceSpan.fromTokens(uri, begin, end); |
- } |
- |
- SourceSpan spanFromNode(Node node) { |
- return spanFromTokens(node.getBeginToken(), node.getEndToken()); |
- } |
- |
- SourceSpan spanFromElement(Element element) { |
- if (element != null && element.sourcePosition != null) { |
- return element.sourcePosition; |
- } |
- while (element != null && element.isSynthesized) { |
- element = element.enclosingElement; |
- } |
- if (element != null && |
- element.sourcePosition == null && |
- !element.isLibrary && |
- !element.isCompilationUnit) { |
- // Sometimes, the backend fakes up elements that have no |
- // position. So we use the enclosing element instead. It is |
- // not a good error location, but cancel really is "internal |
- // error" or "not implemented yet", so the vicinity is good |
- // enough for now. |
- element = element.enclosingElement; |
- // TODO(ahe): I plan to overhaul this infrastructure anyways. |
- } |
- if (element == null) { |
- element = currentElement; |
- } |
- if (element == null) { |
- return null; |
- } |
- |
- if (element.sourcePosition != null) { |
- return element.sourcePosition; |
- } |
- Token position = element.position; |
- Uri uri = element.compilationUnit.script.resourceUri; |
- return (position == null) |
- ? new SourceSpan(uri, 0, 0) |
- : spanFromTokens(position, position, uri); |
- } |
- |
- SourceSpan spanFromHInstruction(HInstruction instruction) { |
- Element element = _elementFromHInstruction(instruction); |
- if (element == null) element = currentElement; |
- SourceInformation position = instruction.sourceInformation; |
- if (position == null) return spanFromElement(element); |
- return position.sourceSpan; |
- } |
- |
- /** |
- * Translates the [resolvedUri] into a readable URI. |
- * |
- * The [importingLibrary] holds the library importing [resolvedUri] or |
- * [:null:] if [resolvedUri] is loaded as the main library. The |
- * [importingLibrary] is used to grant access to internal libraries from |
- * platform libraries and patch libraries. |
- * |
- * If the [resolvedUri] is not accessible from [importingLibrary], this method |
- * is responsible for reporting errors. |
- * |
- * See [LibraryLoader] for terminology on URIs. |
- */ |
- Uri translateResolvedUri(LibraryElement importingLibrary, |
- Uri resolvedUri, Spannable spannable) { |
- unimplemented(importingLibrary, 'Compiler.translateResolvedUri'); |
- return null; |
- } |
- |
- /** |
- * Reads the script specified by the [readableUri]. |
- * |
- * See [LibraryLoader] for terminology on URIs. |
- */ |
- Future<Script> readScript(Spannable node, Uri readableUri) { |
- unimplemented(node, 'Compiler.readScript'); |
- return null; |
- } |
- |
- /// Compatible with [readScript] and used by [LibraryLoader] to create |
- /// synthetic scripts to recover from read errors and bad URIs. |
- Future<Script> synthesizeScript(Spannable node, Uri readableUri) { |
- unimplemented(node, 'Compiler.synthesizeScript'); |
- return null; |
- } |
- |
- Element lookupElementIn(ScopeContainerElement container, String name) { |
- Element element = container.localLookup(name); |
- if (element == null) { |
- throw 'Could not find $name in $container'; |
+ Element lookupElementIn(ScopeContainerElement container, String name) { |
+ Element element = container.localLookup(name); |
+ if (element == null) { |
+ throw 'Could not find $name in $container'; |
} |
return element; |
} |
@@ -1627,19 +1331,19 @@ abstract class Compiler extends DiagnosticListener { |
if (member.isErroneous) return; |
if (member.isFunction) { |
if (!enqueuer.resolution.hasBeenResolved(member)) { |
- reportHintMessage( |
+ reporter.reportHintMessage( |
member, MessageKind.UNUSED_METHOD, {'name': member.name}); |
} |
} else if (member.isClass) { |
if (!member.isResolved) { |
- reportHintMessage( |
+ reporter.reportHintMessage( |
member, MessageKind.UNUSED_CLASS, {'name': member.name}); |
} else { |
member.forEachLocalMember(checkLive); |
} |
} else if (member.isTypedef) { |
if (!member.isResolved) { |
- reportHintMessage( |
+ reporter.reportHintMessage( |
member, MessageKind.UNUSED_TYPEDEF, {'name': member.name}); |
} |
} |
@@ -1869,6 +1573,350 @@ class _CompilerCoreTypes implements CoreTypes { |
} |
} |
+class _CompilerDiagnosticReporter extends DiagnosticReporter { |
+ final Compiler compiler; |
+ final DiagnosticOptions options; |
+ |
+ Element _currentElement; |
+ bool hasCrashed = false; |
+ |
+ /// `true` if the last diagnostic was filtered, in which case the |
+ /// accompanying info message should be filtered as well. |
+ bool lastDiagnosticWasFiltered = false; |
+ |
+ /// Map containing information about the warnings and hints that have been |
+ /// suppressed for each library. |
+ Map<Uri, SuppressionInfo> suppressedWarnings = <Uri, SuppressionInfo>{}; |
+ |
+ _CompilerDiagnosticReporter(this.compiler, this.options); |
+ |
+ Element get currentElement => _currentElement; |
+ |
+ DiagnosticMessage createMessage( |
+ Spannable spannable, |
+ MessageKind messageKind, |
+ [Map arguments = const {}]) { |
+ SourceSpan span = spanFromSpannable(spannable); |
+ MessageTemplate template = MessageTemplate.TEMPLATES[messageKind]; |
+ Message message = template.message(arguments, options.terseDiagnostics); |
+ return new DiagnosticMessage(span, spannable, message); |
+ } |
+ |
+ void reportError( |
+ DiagnosticMessage message, |
+ [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]) { |
+ reportDiagnosticInternal(message, infos, api.Diagnostic.ERROR); |
+ } |
+ |
+ void reportWarning( |
+ DiagnosticMessage message, |
+ [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]) { |
+ reportDiagnosticInternal(message, infos, api.Diagnostic.WARNING); |
+ } |
+ |
+ void reportHint( |
+ DiagnosticMessage message, |
+ [List<DiagnosticMessage> infos = const <DiagnosticMessage>[]]) { |
+ reportDiagnosticInternal(message, infos, api.Diagnostic.HINT); |
+ } |
+ |
+ @deprecated |
+ void reportInfo(Spannable node, MessageKind messageKind, |
+ [Map arguments = const {}]) { |
+ reportDiagnosticInternal( |
+ createMessage(node, messageKind, arguments), |
+ const <DiagnosticMessage>[], |
+ api.Diagnostic.INFO); |
+ } |
+ |
+ void reportDiagnosticInternal(DiagnosticMessage message, |
+ List<DiagnosticMessage> infos, |
+ api.Diagnostic kind) { |
+ if (!options.showPackageWarnings && |
+ message.spannable != NO_LOCATION_SPANNABLE) { |
+ switch (kind) { |
+ case api.Diagnostic.WARNING: |
+ case api.Diagnostic.HINT: |
+ Element element = elementFromSpannable(message.spannable); |
+ if (!compiler.inUserCode(element, assumeInUserCode: true)) { |
+ Uri uri = compiler.getCanonicalUri(element); |
+ SuppressionInfo info = |
+ suppressedWarnings.putIfAbsent(uri, () => new SuppressionInfo()); |
+ if (kind == api.Diagnostic.WARNING) { |
+ info.warnings++; |
+ } else { |
+ info.hints++; |
+ } |
+ lastDiagnosticWasFiltered = true; |
+ return; |
+ } |
+ break; |
+ case api.Diagnostic.INFO: |
+ if (lastDiagnosticWasFiltered) { |
+ return; |
+ } |
+ break; |
+ } |
+ } |
+ lastDiagnosticWasFiltered = false; |
+ reportDiagnostic(message, infos, kind); |
+ } |
+ |
+ void reportDiagnostic(DiagnosticMessage message, |
+ List<DiagnosticMessage> infos, |
+ api.Diagnostic kind) { |
+ compiler.reportDiagnostic(message, infos, kind); |
+ } |
+ |
+ /** |
+ * Perform an operation, [f], returning the return value from [f]. If an |
+ * error occurs then report it as having occurred during compilation of |
+ * [element]. Can be nested. |
+ */ |
+ withCurrentElement(Element element, f()) { |
+ Element old = currentElement; |
+ _currentElement = element; |
+ try { |
+ return f(); |
+ } on SpannableAssertionFailure catch (ex) { |
+ if (!hasCrashed) { |
+ reportAssertionFailure(ex); |
+ pleaseReportCrash(); |
+ } |
+ hasCrashed = true; |
+ rethrow; |
+ } on StackOverflowError { |
+ // We cannot report anything useful in this case, because we |
+ // do not have enough stack space. |
+ rethrow; |
+ } catch (ex) { |
+ if (hasCrashed) rethrow; |
+ try { |
+ unhandledExceptionOnElement(element); |
+ } catch (doubleFault) { |
+ // Ignoring exceptions in exception handling. |
+ } |
+ rethrow; |
+ } finally { |
+ _currentElement = old; |
+ } |
+ } |
+ |
+ void reportAssertionFailure(SpannableAssertionFailure ex) { |
+ String message = (ex.message != null) ? tryToString(ex.message) |
+ : tryToString(ex); |
+ reportDiagnosticInternal( |
+ createMessage(ex.node, MessageKind.GENERIC, {'text': message}), |
+ const <DiagnosticMessage>[], |
+ api.Diagnostic.CRASH); |
+ } |
+ |
+ SourceSpan spanFromTokens(Token begin, Token end, [Uri uri]) { |
+ if (begin == null || end == null) { |
+ // TODO(ahe): We can almost always do better. Often it is only |
+ // end that is null. Otherwise, we probably know the current |
+ // URI. |
+ throw 'Cannot find tokens to produce error message.'; |
+ } |
+ if (uri == null && currentElement != null) { |
+ uri = currentElement.compilationUnit.script.resourceUri; |
+ } |
+ return new SourceSpan.fromTokens(uri, begin, end); |
+ } |
+ |
+ SourceSpan spanFromNode(Node node) { |
+ return spanFromTokens(node.getBeginToken(), node.getEndToken()); |
+ } |
+ |
+ SourceSpan spanFromElement(Element element) { |
+ if (element != null && element.sourcePosition != null) { |
+ return element.sourcePosition; |
+ } |
+ while (element != null && element.isSynthesized) { |
+ element = element.enclosingElement; |
+ } |
+ if (element != null && |
+ element.sourcePosition == null && |
+ !element.isLibrary && |
+ !element.isCompilationUnit) { |
+ // Sometimes, the backend fakes up elements that have no |
+ // position. So we use the enclosing element instead. It is |
+ // not a good error location, but cancel really is "internal |
+ // error" or "not implemented yet", so the vicinity is good |
+ // enough for now. |
+ element = element.enclosingElement; |
+ // TODO(ahe): I plan to overhaul this infrastructure anyways. |
+ } |
+ if (element == null) { |
+ element = currentElement; |
+ } |
+ if (element == null) { |
+ return null; |
+ } |
+ |
+ if (element.sourcePosition != null) { |
+ return element.sourcePosition; |
+ } |
+ Token position = element.position; |
+ Uri uri = element.compilationUnit.script.resourceUri; |
+ return (position == null) |
+ ? new SourceSpan(uri, 0, 0) |
+ : spanFromTokens(position, position, uri); |
+ } |
+ |
+ SourceSpan spanFromHInstruction(HInstruction instruction) { |
+ Element element = _elementFromHInstruction(instruction); |
+ if (element == null) element = currentElement; |
+ SourceInformation position = instruction.sourceInformation; |
+ if (position == null) return spanFromElement(element); |
+ return position.sourceSpan; |
+ } |
+ |
+ SourceSpan spanFromSpannable(Spannable node) { |
+ // TODO(johnniwinther): Disallow `node == null` ? |
+ if (node == null) return null; |
+ if (node == CURRENT_ELEMENT_SPANNABLE) { |
+ node = currentElement; |
+ } else if (node == NO_LOCATION_SPANNABLE) { |
+ if (currentElement == null) return null; |
+ node = currentElement; |
+ } |
+ if (node is SourceSpan) { |
+ return node; |
+ } else if (node is Node) { |
+ return spanFromNode(node); |
+ } else if (node is TokenPair) { |
+ return spanFromTokens(node.begin, node.end); |
+ } else if (node is Token) { |
+ return spanFromTokens(node, node); |
+ } else if (node is HInstruction) { |
+ return spanFromHInstruction(node); |
+ } else if (node is Element) { |
+ return spanFromElement(node); |
+ } else if (node is MetadataAnnotation) { |
+ Uri uri = node.annotatedElement.compilationUnit.script.resourceUri; |
+ return spanFromTokens(node.beginToken, node.endToken, uri); |
+ } else if (node is Local) { |
+ Local local = node; |
+ return spanFromElement(local.executableContext); |
+ } else { |
+ throw 'No error location.'; |
+ } |
+ } |
+ |
+ Element _elementFromHInstruction(HInstruction instruction) { |
+ return instruction.sourceElement is Element |
+ ? instruction.sourceElement : null; |
+ } |
+ |
+ internalError(Spannable node, reason) { |
+ String message = tryToString(reason); |
+ reportDiagnosticInternal( |
+ createMessage(node, MessageKind.GENERIC, {'text': message}), |
+ const <DiagnosticMessage>[], |
+ api.Diagnostic.CRASH); |
+ throw 'Internal Error: $message'; |
+ } |
+ |
+ void unhandledExceptionOnElement(Element element) { |
+ if (hasCrashed) return; |
+ hasCrashed = true; |
+ reportDiagnostic( |
+ createMessage(element, MessageKind.COMPILER_CRASHED), |
+ const <DiagnosticMessage>[], |
+ api.Diagnostic.CRASH); |
+ pleaseReportCrash(); |
+ } |
+ |
+ void pleaseReportCrash() { |
+ print( |
+ MessageTemplate.TEMPLATES[MessageKind.PLEASE_REPORT_THE_CRASH] |
+ .message({'buildId': compiler.buildId})); |
+ } |
+ |
+ /// Finds the approximate [Element] for [node]. [currentElement] is used as |
+ /// the default value. |
+ Element elementFromSpannable(Spannable node) { |
+ Element element; |
+ if (node is Element) { |
+ element = node; |
+ } else if (node is HInstruction) { |
+ element = _elementFromHInstruction(node); |
+ } else if (node is MetadataAnnotation) { |
+ element = node.annotatedElement; |
+ } |
+ return element != null ? element : currentElement; |
+ } |
+ |
+ void log(message) { |
+ Message msg = MessageTemplate.TEMPLATES[MessageKind.GENERIC] |
+ .message({'text': '$message'}); |
+ reportDiagnostic( |
+ new DiagnosticMessage(null, null, msg), |
+ const <DiagnosticMessage>[], |
+ api.Diagnostic.VERBOSE_INFO); |
+ } |
+ |
+ String tryToString(object) { |
+ try { |
+ return object.toString(); |
+ } catch (_) { |
+ return '<exception in toString()>'; |
+ } |
+ } |
+ |
+ onError(Uri uri, error) { |
+ try { |
+ if (!hasCrashed) { |
+ hasCrashed = true; |
+ if (error is SpannableAssertionFailure) { |
+ reportAssertionFailure(error); |
+ } else { |
+ reportDiagnostic( |
+ createMessage( |
+ new SourceSpan(uri, 0, 0), |
+ MessageKind.COMPILER_CRASHED), |
+ const <DiagnosticMessage>[], |
+ api.Diagnostic.CRASH); |
+ } |
+ pleaseReportCrash(); |
+ } |
+ } catch (doubleFault) { |
+ // Ignoring exceptions in exception handling. |
+ } |
+ throw error; |
+ } |
+ |
+ void onCrashInUserCode(String message, exception, stackTrace) { |
+ hasCrashed = true; |
+ print('$message: ${tryToString(exception)}'); |
+ print(tryToString(stackTrace)); |
+ } |
+ |
+ void reportSuppressedMessagesSummary() { |
+ if (!options.showPackageWarnings && !options.suppressWarnings) { |
+ suppressedWarnings.forEach((Uri uri, SuppressionInfo info) { |
+ MessageKind kind = MessageKind.HIDDEN_WARNINGS_HINTS; |
+ if (info.warnings == 0) { |
+ kind = MessageKind.HIDDEN_HINTS; |
+ } else if (info.hints == 0) { |
+ kind = MessageKind.HIDDEN_WARNINGS; |
+ } |
+ MessageTemplate template = MessageTemplate.TEMPLATES[kind]; |
+ Message message = template.message( |
+ {'warnings': info.warnings, |
+ 'hints': info.hints, |
+ 'uri': uri}, |
+ options.terseDiagnostics); |
+ reportDiagnostic( |
+ new DiagnosticMessage(null, null, message), |
+ const <DiagnosticMessage>[], |
+ api.Diagnostic.HINT); |
+ }); |
+ } |
+ } |
+} |
+ |
// TODO(johnniwinther): Move [ResolverTask] here. |
class _CompilerResolution implements Resolution { |
final Compiler compiler; |
@@ -1876,7 +1924,7 @@ class _CompilerResolution implements Resolution { |
_CompilerResolution(this.compiler); |
@override |
- DiagnosticListener get listener => compiler; |
+ DiagnosticReporter get reporter => compiler.reporter; |
@override |
Parsing get parsing => compiler.parsing; |
@@ -1923,7 +1971,7 @@ class _CompilerParsing implements Parsing { |
_CompilerParsing(this.compiler); |
@override |
- DiagnosticListener get listener => compiler; |
+ DiagnosticReporter get reporter => compiler.reporter; |
@override |
measure(f()) => compiler.parser.measure(f); |