Chromium Code Reviews| 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) { |
|
sigurdm
2015/10/05 13:45:34
Why is this not a method on `Spannable`?
Johnni Winther
2015/10/06 08:04:32
Because Spannable is only a tagging interface.
|
| + // 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); |