blob: 52c4fbb45608a2771412ec9a7f4e098fbb97f844 [file] [log] [blame] [edit]
// 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');
}