| // 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/fasta/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 dart2js.messages; |
| |
| import '../commandline_options.dart'; |
| import '../options.dart'; |
| import 'invariant.dart' show failedAt; |
| import 'spannable.dart' show CURRENT_ELEMENT_SPANNABLE; |
| |
| /// Keys for the [MessageTemplate]s. |
| enum MessageKind { |
| COMPILER_CRASHED, |
| COMPLEX_RETURNING_NSM, |
| COMPLEX_THROWING_NSM, |
| DIRECTLY_THROWING_NSM, |
| GENERIC, |
| HIDDEN_HINTS, |
| HIDDEN_WARNINGS, |
| HIDDEN_WARNINGS_HINTS, |
| INVALID_METADATA, |
| INVALID_METADATA_GENERIC, |
| JS_PLACEHOLDER_CAPTURE, |
| NATIVE_NON_INSTANCE_IN_NON_NATIVE_CLASS, |
| NON_NATIVE_EXTERNAL, |
| PLEASE_REPORT_THE_CRASH, |
| PREAMBLE, |
| RUNTIME_TYPE_TO_STRING, |
| STRING_EXPECTED, |
| WRONG_ARGUMENT_FOR_JS, |
| WRONG_ARGUMENT_FOR_JS_FIRST, |
| WRONG_ARGUMENT_FOR_JS_SECOND, |
| WRONG_ARGUMENT_FOR_JS_INTERCEPTOR_CONSTANT, |
| // TODO(32557): Remove these when issue 32557 is fixed. |
| SWITCH_CASE_FORBIDDEN, |
| SWITCH_CASE_VALUE_OVERRIDES_EQUALS, |
| SWITCH_CASE_TYPES_NOT_EQUAL, |
| SWITCH_CASE_TYPES_NOT_EQUAL_CASE, |
| } |
| |
| /// 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 either a String containing the example source code or a Map |
| /// from filenames to source code. In the latter case, the filename for the |
| /// main library code must be 'main.dart'. |
| final List? 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.STRING_EXPECTED: MessageTemplate(MessageKind.STRING_EXPECTED, |
| "Expected a 'String', but got an instance of '#{type}'."), |
| |
| MessageKind.JS_PLACEHOLDER_CAPTURE: MessageTemplate( |
| MessageKind.JS_PLACEHOLDER_CAPTURE, |
| "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.WRONG_ARGUMENT_FOR_JS: MessageTemplate( |
| MessageKind.WRONG_ARGUMENT_FOR_JS, |
| "JS expression must take two or more arguments."), |
| |
| MessageKind.WRONG_ARGUMENT_FOR_JS_FIRST: MessageTemplate( |
| MessageKind.WRONG_ARGUMENT_FOR_JS_FIRST, |
| "JS expression must take two or more arguments."), |
| |
| MessageKind.WRONG_ARGUMENT_FOR_JS_SECOND: MessageTemplate( |
| MessageKind.WRONG_ARGUMENT_FOR_JS_SECOND, |
| "JS second argument must be a string literal."), |
| |
| MessageKind.WRONG_ARGUMENT_FOR_JS_INTERCEPTOR_CONSTANT: MessageTemplate( |
| MessageKind.WRONG_ARGUMENT_FOR_JS_INTERCEPTOR_CONSTANT, |
| "Argument for 'JS_INTERCEPTOR_CONSTANT' must be a type constant."), |
| |
| MessageKind.INVALID_METADATA: MessageTemplate( |
| MessageKind.INVALID_METADATA, |
| "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.INVALID_METADATA_GENERIC: MessageTemplate( |
| MessageKind.INVALID_METADATA_GENERIC, |
| "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.COMPILER_CRASHED: MessageTemplate(MessageKind.COMPILER_CRASHED, |
| "The compiler crashed when compiling this element."), |
| |
| MessageKind.PLEASE_REPORT_THE_CRASH: |
| MessageTemplate(MessageKind.PLEASE_REPORT_THE_CRASH, ''' |
| 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.HIDDEN_WARNINGS_HINTS: MessageTemplate( |
| MessageKind.HIDDEN_WARNINGS_HINTS, |
| "#{warnings} warning(s) and #{hints} hint(s) suppressed in #{uri}."), |
| |
| MessageKind.HIDDEN_WARNINGS: MessageTemplate(MessageKind.HIDDEN_WARNINGS, |
| "#{warnings} warning(s) suppressed in #{uri}."), |
| |
| MessageKind.HIDDEN_HINTS: MessageTemplate( |
| MessageKind.HIDDEN_HINTS, "#{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.DIRECTLY_THROWING_NSM: MessageTemplate( |
| MessageKind.DIRECTLY_THROWING_NSM, |
| "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.COMPLEX_THROWING_NSM: MessageTemplate( |
| MessageKind.COMPLEX_THROWING_NSM, |
| "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.COMPLEX_RETURNING_NSM: MessageTemplate( |
| MessageKind.COMPLEX_RETURNING_NSM, |
| "Overriding 'noSuchMethod' causes the compiler to generate " |
| "more code and prevents the compiler from doing some optimizations.", |
| howToFix: "Consider removing this 'noSuchMethod' implementation."), |
| |
| MessageKind.RUNTIME_TYPE_TO_STRING: MessageTemplate( |
| MessageKind.RUNTIME_TYPE_TO_STRING, |
| "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.NON_NATIVE_EXTERNAL: MessageTemplate( |
| MessageKind.NON_NATIVE_EXTERNAL, |
| "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.NATIVE_NON_INSTANCE_IN_NON_NATIVE_CLASS: MessageTemplate( |
| MessageKind.NATIVE_NON_INSTANCE_IN_NON_NATIVE_CLASS, |
| "Native non-instance members are only allowed in native classes."), |
| |
| // TODO(32557): Remove these when issue 32557 is fixed. |
| MessageKind.SWITCH_CASE_VALUE_OVERRIDES_EQUALS: MessageTemplate( |
| MessageKind.SWITCH_CASE_VALUE_OVERRIDES_EQUALS, |
| "'case' expression type '#{type}' overrides 'operator =='."), |
| MessageKind.SWITCH_CASE_FORBIDDEN: MessageTemplate( |
| MessageKind.SWITCH_CASE_FORBIDDEN, |
| "'case' expression may not be of type '#{type}'."), |
| MessageKind.SWITCH_CASE_TYPES_NOT_EQUAL: MessageTemplate( |
| MessageKind.SWITCH_CASE_TYPES_NOT_EQUAL, |
| "'case' expressions do not all have type '#{type}'."), |
| MessageKind.SWITCH_CASE_TYPES_NOT_EQUAL_CASE: MessageTemplate( |
| MessageKind.SWITCH_CASE_TYPES_NOT_EQUAL_CASE, |
| "'case' expression of type '#{type}'."), |
| }; // 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(CURRENT_ELEMENT_SPANNABLE, |
| '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'); |
| } |