blob: 91315d148ca00282b42ee71f120b1014574782a9 [file]
// Copyright (c) 2016, 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.
/// @docImport 'package:analyzer/error/listener.dart';
library;
import 'dart:math';
import 'package:_fe_analyzer_shared/src/base/analyzer_public_api.dart';
import 'package:_fe_analyzer_shared/src/scanner/characters.dart';
import 'customized_codes.dart';
/// Inserts the given [arguments] into [pattern].
///
/// format('Hello, {0}!', ['John']) = 'Hello, John!'
/// format('{0} are you {1}ing?', ['How', 'do']) = 'How are you doing?'
/// format('{0} are you {1}ing?', ['What', 'read']) =
/// 'What are you reading?'
String formatList(String pattern, List<Object?>? arguments) {
if (arguments == null || arguments.isEmpty) {
assert(
!pattern.contains(new RegExp(r'\{(\d+)\}')),
'Message requires arguments, but none were provided.',
);
return pattern;
}
final List<String> buffer = <String>[];
int from = 0;
int i = 0;
while (i < pattern.length) {
int char = pattern.codeUnitAt(i++);
if (char == $OPEN_CURLY_BRACKET) {
int to = i - 1; // End of substring before brace.
int number = 0;
while (i < pattern.length) {
char = pattern.codeUnitAt(i++);
int digit = char ^ $0;
if (digit <= 9) {
number = number * 10 + digit;
} else if (char == $CLOSE_CURLY_BRACKET) {
int end = i;
// Found {\d*}, check that there is at least one digit.
if (end > to + 2) {
// Found {\d+}.
if (to > from) {
buffer.add(pattern.substring(from, to));
}
buffer.add(arguments[number].toString());
from = i;
}
break;
} else if (char == $OPEN_CURLY_BRACKET) {
// Found next `{` before closing previous. Restart.
to = i - 1;
number = 0;
} else {
break;
}
}
}
}
if (from == 0) {
// No numbers found.
return pattern;
}
if (from < pattern.length) {
buffer.add(pattern.substring(from));
}
return buffer.join();
}
/// An error code associated with an `AnalysisError`.
///
/// Generally, messages should follow the [Guide for Writing
/// Diagnostics](https://github.com/dart-lang/sdk/blob/main/pkg/front_end/lib/src/base/diagnostics.md).
@AnalyzerPublicApi(message: 'exported by package:analyzer/error/error.dart')
abstract class DiagnosticCode {
/// Regular expression for identifying positional arguments in error messages.
static final RegExp _positionalArgumentRegExp = new RegExp(r'\{(\d+)\}');
final String _name;
final String _uniqueName;
final String _problemMessage;
final String? _correctionMessage;
/// Return `true` if diagnostics with this code have documentation for them
/// that has been published.
final bool hasPublishedDocs;
/// Whether this error is caused by an unresolved identifier.
final bool isUnresolvedIdentifier;
/// Initialize a newly created error code to have the given [name]. The
/// message associated with the error will be created from the given
/// [problemMessage] template. The correction associated with the error will
/// be created from the given [correctionMessage] template.
const DiagnosticCode({
String? correctionMessage,
this.hasPublishedDocs = false,
this.isUnresolvedIdentifier = false,
required String name,
required String problemMessage,
required String uniqueName,
}) : _name = name,
_uniqueName = uniqueName,
_correctionMessage = correctionMessage,
_problemMessage = problemMessage;
/// The template used to create the correction to be displayed for this
/// diagnostic, or `null` if there is no correction information for this
/// diagnostic. The correction should indicate how the user can fix the
/// diagnostic.
String? get correctionMessage =>
customizedCorrections[lowerCaseUniqueName] ?? _correctionMessage;
@Deprecated("Use 'diagnosticSeverity' instead")
DiagnosticSeverity get errorSeverity => severity;
/// Whether a finding of this diagnostic is ignorable via comments such as
/// `// ignore:` or `// ignore_for_file:`.
bool get isIgnorable => severity != DiagnosticSeverity.ERROR;
/// The name of the error code, converted to all lower case.
String get lowerCaseName => _name.toLowerCase();
/// The unique name of this error code, converted to all lower case.
String get lowerCaseUniqueName => _uniqueName.toLowerCase();
/// The name of the error code.
///
/// Deprecated. Please use [lowerCaseName] instead so that names are matched
/// in a case insensitive fashion.
@Deprecated('Please use lowerCaseName')
String get name => _name;
int get numParameters {
int result = 0;
String? correctionMessage = _correctionMessage;
for (String s in [
_problemMessage,
if (correctionMessage != null) correctionMessage,
]) {
for (RegExpMatch match in _positionalArgumentRegExp.allMatches(s)) {
result = max(result, int.parse(match[1]!) + 1);
}
}
return result;
}
/// The template used to create the problem message to be displayed for this
/// diagnostic. The problem message should indicate what is wrong and why it
/// is wrong.
String get problemMessage =>
customizedMessages[lowerCaseUniqueName] ?? _problemMessage;
/// The severity of the diagnostic.
DiagnosticSeverity get severity;
/// The type of the error.
DiagnosticType get type;
/// The unique name of this error code.
///
/// Deprecated. Please use [lowerCaseUniqueName] instead so that names are
/// matched in a case insensitive fashion.
@Deprecated('Please use lowerCaseUniqueName')
String get uniqueName => _uniqueName;
/// Return a URL that can be used to access documentation for diagnostics with
/// this code, or `null` if there is no published documentation.
String? get url {
if (hasPublishedDocs) {
return 'https://dart.dev/diagnostics/${lowerCaseName}';
}
return null;
}
@override
String toString() => lowerCaseUniqueName;
}
abstract class DiagnosticCodeImpl extends DiagnosticCode {
@override
final DiagnosticType type;
const DiagnosticCodeImpl({
super.correctionMessage,
super.hasPublishedDocs = false,
super.isUnresolvedIdentifier = false,
required super.name,
required super.problemMessage,
required this.type,
required super.uniqueName,
});
@override
DiagnosticSeverity get severity => type.severity;
}
/// The severity of an [DiagnosticCode].
@AnalyzerPublicApi(message: 'exported by package:analyzer/error/error.dart')
class DiagnosticSeverity implements Comparable<DiagnosticSeverity> {
/// The severity representing a non-error. This is never used for any error
/// code, but is useful for clients.
static const DiagnosticSeverity NONE = const DiagnosticSeverity(
'NONE',
0,
" ",
"none",
);
/// The severity representing an informational level analysis issue.
static const DiagnosticSeverity INFO = const DiagnosticSeverity(
'INFO',
1,
"I",
"info",
);
/// The severity representing a warning. Warnings can become errors if the
/// `-Werror` command line flag is specified.
static const DiagnosticSeverity WARNING = const DiagnosticSeverity(
'WARNING',
2,
"W",
"warning",
);
/// The severity representing an error.
static const DiagnosticSeverity ERROR = const DiagnosticSeverity(
'ERROR',
3,
"E",
"error",
);
static const List<DiagnosticSeverity> values = const [
NONE,
INFO,
WARNING,
ERROR,
];
final String name;
final int ordinal;
/// The name of the severity used when producing machine output.
final String machineCode;
/// The name of the severity used when producing readable output.
final String displayName;
const DiagnosticSeverity(
this.name,
this.ordinal,
this.machineCode,
this.displayName,
);
@override
int get hashCode => ordinal;
@override
int compareTo(DiagnosticSeverity other) => ordinal - other.ordinal;
/// Return the severity constant that represents the greatest severity.
DiagnosticSeverity max(DiagnosticSeverity severity) =>
this.ordinal >= severity.ordinal ? this : severity;
@override
String toString() => name;
}
///
/// The type of a [DiagnosticCode].
///
@AnalyzerPublicApi(message: 'exported by package:analyzer/error/error.dart')
class DiagnosticType implements Comparable<DiagnosticType> {
/// Task (todo) comments in user code.
static const DiagnosticType TODO = const DiagnosticType(
'TODO',
0,
DiagnosticSeverity.INFO,
);
/// Extra analysis run over the code to follow best practices, which are not
/// in the Dart Language Specification.
static const DiagnosticType HINT = const DiagnosticType(
'HINT',
1,
DiagnosticSeverity.INFO,
);
/// Compile-time errors are errors that preclude execution. A compile time
/// error must be reported by a Dart compiler before the erroneous code is
/// executed.
static const DiagnosticType COMPILE_TIME_ERROR = const DiagnosticType(
'COMPILE_TIME_ERROR',
2,
DiagnosticSeverity.ERROR,
);
/// Checked mode compile-time errors are errors that preclude execution in
/// checked mode.
static const DiagnosticType CHECKED_MODE_COMPILE_TIME_ERROR =
const DiagnosticType(
'CHECKED_MODE_COMPILE_TIME_ERROR',
3,
DiagnosticSeverity.ERROR,
);
/// Static warnings are those warnings reported by the static checker. They
/// have no effect on execution. Static warnings must be provided by Dart
/// compilers used during development.
static const DiagnosticType STATIC_WARNING = const DiagnosticType(
'STATIC_WARNING',
4,
DiagnosticSeverity.WARNING,
);
/// Syntactic errors are errors produced as a result of input that does not
/// conform to the grammar.
static const DiagnosticType SYNTACTIC_ERROR = const DiagnosticType(
'SYNTACTIC_ERROR',
6,
DiagnosticSeverity.ERROR,
);
/// Lint warnings describe style and best practice recommendations that can be
/// used to formalize a project's style guidelines.
static const DiagnosticType LINT = const DiagnosticType(
'LINT',
7,
DiagnosticSeverity.INFO,
);
static const List<DiagnosticType> values = const [
TODO,
HINT,
COMPILE_TIME_ERROR,
CHECKED_MODE_COMPILE_TIME_ERROR,
STATIC_WARNING,
SYNTACTIC_ERROR,
LINT,
];
/// The name of this error type.
final String name;
/// The ordinal value of the error type.
final int ordinal;
/// The severity of this type of error.
final DiagnosticSeverity severity;
/// Initialize a newly created error type to have the given [name] and
/// [severity].
const DiagnosticType(this.name, this.ordinal, this.severity);
String get displayName => name.toLowerCase().replaceAll('_', ' ');
@override
int get hashCode => ordinal;
@override
int compareTo(DiagnosticType other) => ordinal - other.ordinal;
@override
String toString() => name;
}