| 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);
|
|
|