mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 16:59:47 +00:00
Add documentation for core lints
Change-Id: I3aa040df18982447ec1e3da074547d28838acae9 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/344280 Reviewed-by: Phil Quitslund <pquitslund@google.com> Reviewed-by: Marya Belanger <mbelanger@google.com> Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
parent
121d54f1e0
commit
e5cbde13b9
|
@ -8765,12 +8765,12 @@ CompileTimeErrorCode:
|
|||
The analyzer produces this diagnostic when a member of a class is found
|
||||
that overrides a member from a supertype and the override isn't valid. An
|
||||
override is valid if all of these are true:
|
||||
* It allows all of the arguments allowed by the overridden member.
|
||||
* It doesn't require any arguments that aren't required by the overridden
|
||||
- It allows all of the arguments allowed by the overridden member.
|
||||
- It doesn't require any arguments that aren't required by the overridden
|
||||
member.
|
||||
* The type of every parameter of the overridden member is assignable to the
|
||||
- The type of every parameter of the overridden member is assignable to the
|
||||
corresponding parameter of the override.
|
||||
* The return type of the override is assignable to the return type of the
|
||||
- The return type of the override is assignable to the return type of the
|
||||
overridden member.
|
||||
|
||||
#### Example
|
||||
|
|
|
@ -92,6 +92,17 @@ class DocumentationValidator {
|
|||
// The code has been replaced but is not yet removed.
|
||||
'HintCode.DEPRECATED_MEMBER_USE',
|
||||
|
||||
// Missing support for example files outside of `lib`.
|
||||
'LintCode.avoid_relative_lib_imports',
|
||||
// Missing support for creating an indirect dependency on a package.
|
||||
'LintCode.depend_on_referenced_packages',
|
||||
// Missing support for specifying the name of the test file.
|
||||
'LintCode.file_names',
|
||||
// The lint does nothing.
|
||||
'LintCode.package_prefixed_library_names',
|
||||
// Missing support for YAML files.
|
||||
'LintCode.secure_pubspec_urls',
|
||||
|
||||
//
|
||||
// The following can't currently be verified because the examples aren't
|
||||
// Dart code.
|
||||
|
@ -149,6 +160,10 @@ class DocumentationValidator {
|
|||
var errorClass = classEntry.key;
|
||||
await _validateMessages(errorClass, classEntry.value);
|
||||
}
|
||||
for (var classEntry in lintMessages.entries) {
|
||||
var errorClass = classEntry.key;
|
||||
await _validateMessages(errorClass, classEntry.value);
|
||||
}
|
||||
ErrorClassInfo? errorClassIncludingCfeMessages;
|
||||
for (var errorClass in errorClasses) {
|
||||
if (errorClass.includeCfeMessages) {
|
||||
|
@ -271,7 +286,6 @@ class DocumentationValidator {
|
|||
if (errorCodeInfo.isRemoved) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var docs = parseErrorCodeDocumentation(
|
||||
'$className.$errorName', errorCodeInfo.documentation);
|
||||
if (docs != null) {
|
||||
|
@ -291,7 +305,11 @@ class DocumentationValidator {
|
|||
firstExample = exampleSnippets[0];
|
||||
}
|
||||
for (int i = 0; i < exampleSnippets.length; i++) {
|
||||
await _validateSnippet('example', i, exampleSnippets[i]);
|
||||
_SnippetData snippet = exampleSnippets[i];
|
||||
if (className == 'LintCode') {
|
||||
snippet.lintCode = codeName;
|
||||
}
|
||||
await _validateSnippet('example', i, snippet);
|
||||
}
|
||||
|
||||
List<_SnippetData> fixesSnippets =
|
||||
|
@ -301,6 +319,9 @@ class DocumentationValidator {
|
|||
if (firstExample != null) {
|
||||
snippet.auxiliaryFiles.addAll(firstExample.auxiliaryFiles);
|
||||
}
|
||||
if (className == 'LintCode') {
|
||||
snippet.lintCode = codeName;
|
||||
}
|
||||
await _validateSnippet('fixes', i, snippet);
|
||||
}
|
||||
}
|
||||
|
@ -426,6 +447,7 @@ class _SnippetData {
|
|||
final Map<String, String> auxiliaryFiles;
|
||||
final List<String> experiments;
|
||||
final String? languageVersion;
|
||||
String? lintCode;
|
||||
|
||||
_SnippetData(this.content, this.offset, this.length, this.auxiliaryFiles,
|
||||
this.experiments, this.languageVersion);
|
||||
|
@ -457,10 +479,19 @@ class _SnippetTest extends PubPackageResolutionTest {
|
|||
@override
|
||||
void setUp() {
|
||||
super.setUp();
|
||||
_createAnalysisOptionsFile();
|
||||
_createAuxiliaryFiles(snippet.auxiliaryFiles);
|
||||
addTestFile(snippet.content);
|
||||
}
|
||||
|
||||
void _createAnalysisOptionsFile() {
|
||||
var lintCode = snippet.lintCode;
|
||||
if (lintCode != null) {
|
||||
writeTestPackageAnalysisOptionsFile(
|
||||
AnalysisOptionsFileConfig(lints: [lintCode]));
|
||||
}
|
||||
}
|
||||
|
||||
void _createAuxiliaryFiles(Map<String, String> auxiliaryFiles) {
|
||||
var packageConfigBuilder = PackageConfigFileBuilder();
|
||||
for (String uriStr in auxiliaryFiles.keys) {
|
||||
|
|
|
@ -10611,12 +10611,12 @@ _The setter '{1}.{0}' ('{2}') isn't a valid override of '{3}.{0}' ('{4}')._
|
|||
The analyzer produces this diagnostic when a member of a class is found
|
||||
that overrides a member from a supertype and the override isn't valid. An
|
||||
override is valid if all of these are true:
|
||||
* It allows all of the arguments allowed by the overridden member.
|
||||
* It doesn't require any arguments that aren't required by the overridden
|
||||
- It allows all of the arguments allowed by the overridden member.
|
||||
- It doesn't require any arguments that aren't required by the overridden
|
||||
member.
|
||||
* The type of every parameter of the overridden member is assignable to the
|
||||
- The type of every parameter of the overridden member is assignable to the
|
||||
corresponding parameter of the override.
|
||||
* The return type of the override is assignable to the return type of the
|
||||
- The return type of the override is assignable to the return type of the
|
||||
overridden member.
|
||||
|
||||
#### Example
|
||||
|
|
|
@ -120,7 +120,7 @@ class _ErrorCodeDocumentationParser {
|
|||
|
||||
String get line => commentLines[currentLineNumber];
|
||||
|
||||
BlockSection computeCurrentBlockSection() {
|
||||
BlockSection? computeCurrentBlockSection() {
|
||||
switch (currentSection) {
|
||||
case '#### Example':
|
||||
case '#### Examples':
|
||||
|
@ -130,7 +130,7 @@ class _ErrorCodeDocumentationParser {
|
|||
case null:
|
||||
problem('Code block before section header');
|
||||
default:
|
||||
problem('Code block in invalid section ${json.encode(currentSection)}');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -177,7 +177,7 @@ class _ErrorCodeDocumentationParser {
|
|||
List<String>? experiments;
|
||||
assert(line.startsWith('```'));
|
||||
var fileType = line.substring(3);
|
||||
if (fileType.isEmpty) {
|
||||
if (fileType.isEmpty && containingSection != null) {
|
||||
problem('Code blocks should have a file type, e.g. "```dart"');
|
||||
}
|
||||
++currentLineNumber;
|
||||
|
@ -189,12 +189,15 @@ class _ErrorCodeDocumentationParser {
|
|||
problem('Code blocks should end with "```"');
|
||||
}
|
||||
++currentLineNumber;
|
||||
result.add(ErrorCodeDocumentationBlock(codeLines.join('\n'),
|
||||
containingSection: containingSection,
|
||||
experiments: experiments ?? const [],
|
||||
fileType: fileType,
|
||||
languageVersion: languageVersion,
|
||||
uri: uri));
|
||||
if (containingSection != null) {
|
||||
// Ignore code blocks where they're allowed but aren't checked.
|
||||
result.add(ErrorCodeDocumentationBlock(codeLines.join('\n'),
|
||||
containingSection: containingSection,
|
||||
experiments: experiments ?? const [],
|
||||
fileType: fileType,
|
||||
languageVersion: languageVersion,
|
||||
uri: uri));
|
||||
}
|
||||
return;
|
||||
} else if (line.startsWith('%')) {
|
||||
if (line.startsWith(languagePrefix)) {
|
||||
|
|
|
@ -98,6 +98,13 @@ final Map<String, FrontEndErrorCodeInfo> frontEndMessages =
|
|||
final String frontEndPkgPath =
|
||||
normalize(join(pkg_root.packageRoot, 'front_end'));
|
||||
|
||||
/// The path to the `linter` package.
|
||||
final String linterPkgPath = normalize(join(pkg_root.packageRoot, 'linter'));
|
||||
|
||||
/// Decoded messages from the linter's `messages.yaml` file.
|
||||
final Map<String, Map<String, AnalyzerErrorCodeInfo>> lintMessages =
|
||||
_loadLintMessages();
|
||||
|
||||
/// Pattern used by the front end to identify placeholders in error message
|
||||
/// strings.
|
||||
// TODO(paulberry): share this regexp (and the code for interpreting
|
||||
|
@ -202,6 +209,71 @@ Map<String, FrontEndErrorCodeInfo> decodeCfeMessagesYaml(Object? yaml) {
|
|||
return result;
|
||||
}
|
||||
|
||||
/// Decodes a YAML object (obtained from `pkg/linter/messages.yaml`) into a
|
||||
/// two-level map of [ErrorCodeInfo], indexed first by class name and then by
|
||||
/// error name.
|
||||
Map<String, Map<String, AnalyzerErrorCodeInfo>> decodeLinterMessagesYaml(
|
||||
Object? yaml) {
|
||||
Never problem(String message) {
|
||||
throw 'Problem in pkg/linter/messages.yaml: $message';
|
||||
}
|
||||
|
||||
var result = <String, Map<String, AnalyzerErrorCodeInfo>>{};
|
||||
if (yaml is! Map<Object?, Object?>) {
|
||||
problem('root node is not a map');
|
||||
}
|
||||
for (var classEntry in yaml.entries) {
|
||||
var className = classEntry.key;
|
||||
if (className is! String) {
|
||||
problem('non-string class key ${json.encode(className)}');
|
||||
}
|
||||
var classValue = classEntry.value;
|
||||
if (classValue is! Map<Object?, Object?>) {
|
||||
problem('value associated with class key $className is not a map');
|
||||
}
|
||||
for (var errorEntry in classValue.entries) {
|
||||
var errorName = errorEntry.key;
|
||||
if (errorName is! String) {
|
||||
problem('in class $className, non-string error key '
|
||||
'${json.encode(errorName)}');
|
||||
}
|
||||
var errorValue = errorEntry.value;
|
||||
if (errorValue is! Map<Object?, Object?>) {
|
||||
problem('value associated with error $className.$errorName is not a '
|
||||
'map');
|
||||
}
|
||||
|
||||
try {
|
||||
var aliasFor = errorValue['aliasFor'];
|
||||
if (aliasFor is String) {
|
||||
var aliasForPath = aliasFor.split('.');
|
||||
if (aliasForPath.isEmpty) {
|
||||
problem("The 'aliasFor' value at '$className.$errorName is empty");
|
||||
}
|
||||
var node = yaml;
|
||||
for (var key in aliasForPath) {
|
||||
var value = node[key];
|
||||
if (value is! Map<Object?, Object?>) {
|
||||
problem('No Map value at "$aliasFor", aliased from '
|
||||
'$className.$errorName');
|
||||
}
|
||||
node = value;
|
||||
}
|
||||
|
||||
(result[className] ??= {})[errorName] = AliasErrorCodeInfo(
|
||||
aliasFor: aliasFor, comment: errorValue['comment'] as String?);
|
||||
} else {
|
||||
(result[className] ??= {})[errorName] =
|
||||
AnalyzerErrorCodeInfo.fromLinterYaml(errorValue);
|
||||
}
|
||||
} catch (e) {
|
||||
problem('while processing $className.$errorName, $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Loads analyzer messages from the analyzer's `messages.yaml` file.
|
||||
Map<String, Map<String, AnalyzerErrorCodeInfo>> _loadAnalyzerMessages() {
|
||||
Object? messagesYaml =
|
||||
|
@ -216,6 +288,13 @@ Map<String, FrontEndErrorCodeInfo> _loadFrontEndMessages() {
|
|||
return decodeCfeMessagesYaml(messagesYaml);
|
||||
}
|
||||
|
||||
/// Loads linter messages from the linters's `messages.yaml` file.
|
||||
Map<String, Map<String, AnalyzerErrorCodeInfo>> _loadLintMessages() {
|
||||
Object? messagesYaml =
|
||||
loadYaml(File(join(linterPkgPath, 'messages.yaml')).readAsStringSync());
|
||||
return decodeLinterMessagesYaml(messagesYaml);
|
||||
}
|
||||
|
||||
/// Splits [text] on spaces using the given [maxWidth] (and [firstLineWidth] if
|
||||
/// given).
|
||||
List<String> _splitText(
|
||||
|
@ -292,6 +371,8 @@ class AnalyzerErrorCodeInfo extends ErrorCodeInfo {
|
|||
super.sharedName,
|
||||
});
|
||||
|
||||
AnalyzerErrorCodeInfo.fromLinterYaml(super.yaml) : super.fromLinterYaml();
|
||||
|
||||
AnalyzerErrorCodeInfo.fromYaml(super.yaml) : super.fromYaml();
|
||||
}
|
||||
|
||||
|
@ -480,6 +561,13 @@ abstract class ErrorCodeInfo {
|
|||
this.removedIn,
|
||||
});
|
||||
|
||||
/// Decodes an [ErrorCodeInfo] object from its YAML representation.
|
||||
ErrorCodeInfo.fromLinterYaml(Map<Object?, Object?> yaml)
|
||||
: this(
|
||||
documentation: yaml['documentation'] as String?,
|
||||
hasPublishedDocs: yaml['hasPublishedDocs'] as bool? ?? false,
|
||||
problemMessage: '');
|
||||
|
||||
/// Decodes an [ErrorCodeInfo] object from its YAML representation.
|
||||
ErrorCodeInfo.fromYaml(Map<Object?, Object?> yaml)
|
||||
: this(
|
||||
|
|
1292
pkg/linter/messages.yaml
Normal file
1292
pkg/linter/messages.yaml
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue