| // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| /// The messages in this file should follow the [Guide for Writing |
| /// Diagnostics](../../../../front_end/lib/src/base/diagnostics.md). |
| /// |
| /// Other things to keep in mind: |
| /// |
| /// An INFO message should always be preceded by a non-INFO message, and the |
| /// INFO messages are additional details about the preceding non-INFO |
| /// message. For example, consider duplicated elements. First report a WARNING |
| /// or ERROR about the duplicated element, and then report an INFO about the |
| /// location of the existing element. |
| library; |
| |
| import '../commandline_options.dart'; |
| import '../options.dart'; |
| import 'invariant.dart' show failedAt; |
| import 'spannable.dart' show currentElementSpannable; |
| |
| /// Keys for the [MessageTemplate]s. |
| enum MessageKind { |
| compilerCrashed, |
| complexReturningNsm, |
| complexThrowingNsm, |
| directlyThrowingNsm, |
| generic, |
| hiddenHints, |
| hiddenWarnings, |
| hiddenWarningsHints, |
| invalidMetadata, |
| invalidMetadataGeneric, |
| jsPlaceholderCapture, |
| nativeNonInstanceInNonNativeClass, |
| nonNativeExternal, |
| pleaseReportTheCrash, |
| preamble, |
| runtimeTypeToString, |
| stringExpected, |
| wrongArgumentForJS, |
| wrongArgumentForJSFirst, |
| wrongArgumentForJSSecond, |
| wrongArgumentForJSInterceptorConstant, |
| } |
| |
| /// A message template for an error, warning, hint or info message generated |
| /// by the compiler. Each template is associated with a [MessageKind] that |
| /// uniquely identifies the message template. |
| // TODO(johnniwinther): For Infos, consider adding a reference to the |
| // error/warning/hint that they belong to. |
| class MessageTemplate { |
| final MessageKind kind; |
| |
| /// Should describe what is wrong and why. |
| final String template; |
| |
| /// Should describe how to fix the problem. Elided when using --terse option. |
| final String? howToFix; |
| |
| /// Examples will be checked by |
| /// pkg/compiler/test/message_kind_test.dart. |
| /// |
| /// An example is a String containing the example source code. |
| final List<String>? examples; |
| |
| /// Additional options needed for the examples to work. |
| final List<String> options; |
| |
| const MessageTemplate( |
| this.kind, |
| this.template, { |
| this.howToFix, |
| this.examples, |
| this.options = const <String>[], |
| }); |
| |
| /// All templates used by the compiler. |
| /// |
| /// The map is complete mapping from [MessageKind] to their corresponding |
| /// [MessageTemplate]. |
| static const Map<MessageKind, MessageTemplate> templates = { |
| /// Do not use this. It is here for legacy and debugging. It violates item |
| /// 4 of the guide lines for error messages in the beginning of the file. |
| MessageKind.generic: MessageTemplate(MessageKind.generic, '#{text}'), |
| |
| MessageKind.stringExpected: MessageTemplate( |
| MessageKind.stringExpected, |
| "Expected a 'String', but got an instance of '#{type}'.", |
| ), |
| |
| MessageKind.jsPlaceholderCapture: MessageTemplate( |
| MessageKind.jsPlaceholderCapture, |
| "JS code must not use '#' placeholders inside functions.", |
| howToFix: |
| "Use an immediately called JavaScript function to capture the" |
| " the placeholder values as JavaScript function parameters.", |
| ), |
| |
| MessageKind.wrongArgumentForJS: MessageTemplate( |
| MessageKind.wrongArgumentForJS, |
| "JS expression must take two or more arguments.", |
| ), |
| |
| MessageKind.wrongArgumentForJSFirst: MessageTemplate( |
| MessageKind.wrongArgumentForJSFirst, |
| "JS expression must take two or more arguments.", |
| ), |
| |
| MessageKind.wrongArgumentForJSSecond: MessageTemplate( |
| MessageKind.wrongArgumentForJSSecond, |
| "JS second argument must be a string literal.", |
| ), |
| |
| MessageKind.wrongArgumentForJSInterceptorConstant: MessageTemplate( |
| MessageKind.wrongArgumentForJSInterceptorConstant, |
| "Argument for 'JS_INTERCEPTOR_CONSTANT' must be a type constant.", |
| ), |
| |
| MessageKind.invalidMetadata: MessageTemplate( |
| MessageKind.invalidMetadata, |
| "A metadata annotation must be either a reference to a compile-time " |
| "constant variable or a call to a constant constructor.", |
| howToFix: |
| "Try using a different constant value or referencing it through a " |
| "constant variable.", |
| examples: ['@Object main() {}', '@print main() {}'], |
| ), |
| |
| MessageKind.invalidMetadataGeneric: MessageTemplate( |
| MessageKind.invalidMetadataGeneric, |
| "A metadata annotation using a constant constructor cannot use type " |
| "arguments.", |
| howToFix: |
| "Try removing the type arguments or referencing the constant " |
| "through a constant variable.", |
| examples: [ |
| ''' |
| class C<T> { |
| const C(); |
| } |
| @C<int>() main() {} |
| ''', |
| ], |
| ), |
| |
| MessageKind.compilerCrashed: MessageTemplate( |
| MessageKind.compilerCrashed, |
| "The compiler crashed when compiling this element.", |
| ), |
| |
| MessageKind.pleaseReportTheCrash: MessageTemplate( |
| MessageKind.pleaseReportTheCrash, |
| ''' |
| The compiler is broken. |
| |
| When compiling the above element, the compiler crashed. It is not |
| possible to tell if this is caused by a problem in your program or |
| not. Regardless, the compiler should not crash. |
| |
| The Dart team would greatly appreciate if you would take a moment to |
| report this problem at http://dartbug.com/new. |
| |
| Please include the following information: |
| |
| * the name and version of your operating system, |
| |
| * the Dart SDK build number (#{buildId}), and |
| |
| * the entire message you see here (including the full stack trace |
| below as well as the source location above). |
| ''', |
| ), |
| |
| MessageKind.hiddenWarningsHints: MessageTemplate( |
| MessageKind.hiddenWarningsHints, |
| "#{warnings} warning(s) and #{hints} hint(s) suppressed in #{uri}.", |
| ), |
| |
| MessageKind.hiddenWarnings: MessageTemplate( |
| MessageKind.hiddenWarnings, |
| "#{warnings} warning(s) suppressed in #{uri}.", |
| ), |
| |
| MessageKind.hiddenHints: MessageTemplate( |
| MessageKind.hiddenHints, |
| "#{hints} hint(s) suppressed in #{uri}.", |
| ), |
| |
| MessageKind.preamble: MessageTemplate( |
| MessageKind.preamble, |
| "When run on the command-line, the compiled output might" |
| " require a preamble file located in:\n" |
| " <sdk>/lib/_internal/js_runtime/lib/preambles.", |
| ), |
| |
| MessageKind.directlyThrowingNsm: MessageTemplate( |
| MessageKind.directlyThrowingNsm, |
| "This 'noSuchMethod' implementation is guaranteed to throw an " |
| "exception. The generated code will be smaller if it is " |
| "rewritten.", |
| howToFix: |
| "Rewrite to " |
| "'noSuchMethod(Invocation i) => super.noSuchMethod(i);'.", |
| ), |
| |
| MessageKind.complexThrowingNsm: MessageTemplate( |
| MessageKind.complexThrowingNsm, |
| "This 'noSuchMethod' implementation is guaranteed to throw an " |
| "exception. The generated code will be smaller and the compiler " |
| "will be able to perform more optimizations if it is rewritten.", |
| howToFix: |
| "Rewrite to " |
| "'noSuchMethod(Invocation i) => super.noSuchMethod(i);'.", |
| ), |
| |
| MessageKind.complexReturningNsm: MessageTemplate( |
| MessageKind.complexReturningNsm, |
| "Overriding 'noSuchMethod' causes the compiler to generate " |
| "more code and prevents the compiler from doing some optimizations.", |
| howToFix: "Consider removing this 'noSuchMethod' implementation.", |
| ), |
| |
| MessageKind.runtimeTypeToString: MessageTemplate( |
| MessageKind.runtimeTypeToString, |
| "Using '.runtimeType.toString()' causes the compiler to generate " |
| "more code because it needs to preserve type arguments on " |
| "generic classes, even if they are not necessary elsewhere.", |
| howToFix: |
| "If used only for debugging, consider using option " |
| "${Flags.laxRuntimeTypeToString} to reduce the code size " |
| "impact.", |
| ), |
| |
| MessageKind.nonNativeExternal: MessageTemplate( |
| MessageKind.nonNativeExternal, |
| "Non-native external members must be js-interop.", |
| howToFix: |
| "Try removing the 'external' keyword, making it 'native', or " |
| "annotating the function as a js-interop function.", |
| ), |
| |
| MessageKind.nativeNonInstanceInNonNativeClass: MessageTemplate( |
| MessageKind.nativeNonInstanceInNonNativeClass, |
| "Native non-instance members are only allowed in native classes.", |
| ), |
| }; // End of TEMPLATES. |
| |
| @override |
| String toString() => template; |
| |
| Message message(Map<String, String> arguments, DiagnosticOptions? options) { |
| // [options] is nullable for testing. |
| // TODO(sra): Provide a testing version of [DiagnosticOptions] to allow |
| // [options] to be non-nullable, and use composition in [CompilerOptions] |
| // rather than inheritance. |
| return Message(this, arguments, options?.terseDiagnostics ?? false); |
| } |
| |
| bool get hasHowToFix => howToFix != null; |
| } |
| |
| class Message { |
| final MessageTemplate template; |
| final Map<String, String> arguments; |
| final bool terse; |
| late String message = _computeMessage(); |
| |
| Message(this.template, this.arguments, this.terse) { |
| assert(message != ''); // Force message formating in 'dart2js_developer'. |
| } |
| |
| MessageKind get kind => template.kind; |
| |
| String _computeMessage() { |
| message = template.template; |
| arguments.forEach((String key, String value) { |
| message = message.replaceAll('#{$key}', value); |
| }); |
| assert( |
| kind == MessageKind.generic || !message.contains(RegExp(r'#\{.+\}')), |
| failedAt( |
| currentElementSpannable, |
| 'Missing arguments in error message: "$message"', |
| ), |
| ); |
| if (!terse && template.hasHowToFix) { |
| String howToFix = template.howToFix!; |
| arguments.forEach((String key, String value) { |
| howToFix = howToFix.replaceAll('#{$key}', value); |
| }); |
| message = '$message\n$howToFix'; |
| } |
| return message; |
| } |
| |
| @override |
| String toString() => message; |
| |
| @override |
| bool operator ==(other) { |
| if (other is! Message) return false; |
| return (template == other.template) && (toString() == other.toString()); |
| } |
| |
| @override |
| int get hashCode => throw UnsupportedError('Message.hashCode'); |
| } |