[linter] Publish new linter diagnostic messages

This CL doesn't set up the analyzer to surface links to these destinations, just includes the new format in the diagnostic messages file.

Change-Id: I6c227448b42a5abac53d5c6a863e599c7e8809da
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/368081
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Phil Quitslund <pquitslund@google.com>
Commit-Queue: Marya Belanger <mbelanger@google.com>
This commit is contained in:
Parker Lougheed 2024-06-05 22:07:03 +00:00 committed by Commit Queue
parent d9ff61bd87
commit 83135245b2
4 changed files with 4529 additions and 25 deletions

File diff suppressed because it is too large Load diff

View file

@ -36,7 +36,7 @@ class DiagnosticInformation {
final String name;
/// The messages associated with the diagnostic.
List<String> messages;
final List<String> messages;
/// The previous names by which this diagnostic has been known.
List<String> previousNames = [];
@ -46,7 +46,7 @@ class DiagnosticInformation {
/// Initialize a newly created information holder with the given [name] and
/// [message].
DiagnosticInformation(this.name, String message) : messages = [message];
DiagnosticInformation(this.name, this.messages);
/// Return `true` if this diagnostic has documentation.
bool get hasDocumentation => documentation != null;
@ -58,6 +58,12 @@ class DiagnosticInformation {
}
}
/// Add the list of [messages] to the list of messages
/// associated with the diagnostic.
void addMessages(List<String> messages) {
messages.forEach(addMessage);
}
void addPreviousName(String previousName) {
if (!previousNames.contains(previousName)) {
previousNames.add(previousName);
@ -118,6 +124,9 @@ class DocumentationGenerator {
for (var classEntry in analyzerMessages.entries) {
_extractAllDocs(classEntry.key, classEntry.value);
}
for (var classEntry in lintMessages.entries) {
_extractAllDocs(classEntry.key, classEntry.value);
}
for (var errorClass in errorClasses) {
if (errorClass.includeCfeMessages) {
_extractAllDocs(
@ -147,14 +156,12 @@ class DocumentationGenerator {
}
var name = errorCodeInfo.sharedName ?? errorName;
var info = infoByName[name];
var message = convertTemplate(
errorCodeInfo.computePlaceholderToIndexMap(),
errorCodeInfo.problemMessage);
var messages = errorCodeInfo.formattedProblemMessages;
if (info == null) {
info = DiagnosticInformation(name, message);
info = DiagnosticInformation(name, messages);
infoByName[name] = info;
} else {
info.addMessage(message);
info.addMessages(messages);
}
var previousName = errorCodeInfo.previousName;
if (previousName != null) {
@ -196,7 +203,7 @@ that might work in unexpected ways.
[bottom type]: https://dart.dev/null-safety/understanding-null-safety#top-and-bottom
[debugPrint]: https://api.flutter.dev/flutter/foundation/debugPrint.html
[ffi]: https://dart.dev/guides/libraries/c-interop
[ffi]: https://dart.dev/interop/c-interop
[IEEE 754]: https://en.wikipedia.org/wiki/IEEE_754
[irrefutable pattern]: https://dart.dev/resources/glossary#irrefutable-pattern
[kDebugMode]: https://api.flutter.dev/flutter/foundation/kDebugMode-constant.html

View file

@ -5,7 +5,9 @@
import 'dart:convert';
import 'dart:io';
import 'package:analyzer/src/lint/registry.dart' as linter;
import 'package:analyzer_utilities/package_root.dart' as pkg_root;
import 'package:linter/src/rules.dart' as linter;
import 'package:path/path.dart';
import 'package:yaml/yaml.dart' show loadYaml;
@ -214,6 +216,7 @@ Map<String, FrontEndErrorCodeInfo> decodeCfeMessagesYaml(Object? yaml) {
/// error name.
Map<String, Map<String, AnalyzerErrorCodeInfo>> decodeLinterMessagesYaml(
Object? yaml) {
linter.registerLintRules();
Never problem(String message) {
throw 'Problem in pkg/linter/messages.yaml: $message';
}
@ -264,7 +267,7 @@ Map<String, Map<String, AnalyzerErrorCodeInfo>> decodeLinterMessagesYaml(
aliasFor: aliasFor, comment: errorValue['comment'] as String?);
} else {
(result[className] ??= {})[errorName] =
AnalyzerErrorCodeInfo.fromLinterYaml(errorValue);
LinterRuleInfo.fromYaml(errorName, errorValue);
}
} catch (e) {
problem('while processing $className.$errorName, $e');
@ -288,7 +291,7 @@ Map<String, FrontEndErrorCodeInfo> _loadFrontEndMessages() {
return decodeCfeMessagesYaml(messagesYaml);
}
/// Loads linter messages from the linters's `messages.yaml` file.
/// Loads linter messages from the linter's `messages.yaml` file.
Map<String, Map<String, AnalyzerErrorCodeInfo>> _loadLintMessages() {
Object? messagesYaml =
loadYaml(File(join(linterPkgPath, 'messages.yaml')).readAsStringSync());
@ -354,6 +357,10 @@ class AliasErrorCodeInfo extends AnalyzerErrorCodeInfo {
String get aliasForFilePath => errorClasses
.firstWhere((element) => element.name == aliasForClass)
.filePath;
@override
List<String> get formattedProblemMessages => throw StateError(
'The problem message of an error code should not be used.');
}
/// In-memory representation of error code information obtained from the
@ -371,9 +378,10 @@ class AnalyzerErrorCodeInfo extends ErrorCodeInfo {
super.sharedName,
});
AnalyzerErrorCodeInfo.fromLinterYaml(super.yaml) : super.fromLinterYaml();
AnalyzerErrorCodeInfo.fromYaml(super.yaml) : super.fromYaml();
@override
List<String> get formattedProblemMessages => [problemMessage];
}
/// Data tables mapping between CFE errors and their corresponding automatically
@ -561,13 +569,6 @@ 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(
@ -583,6 +584,8 @@ abstract class ErrorCodeInfo {
removedIn: yaml['removedIn'] as String?,
previousName: yaml['previousName'] as String?);
List<String> get formattedProblemMessages;
/// If this error is no longer reported and
/// its error codes should no longer be generated.
bool get isRemoved => removedIn != null;
@ -680,6 +683,10 @@ class FrontEndErrorCodeInfo extends ErrorCodeInfo {
index = yaml['index'] as int?,
super.fromYaml();
@override
List<String> get formattedProblemMessages =>
[convertTemplate(computePlaceholderToIndexMap(), problemMessage)];
@override
Map<Object?, Object?> toYaml() => {
if (analyzerCode.isNotEmpty)
@ -708,3 +715,33 @@ class FrontEndErrorCodeInfo extends ErrorCodeInfo {
}
}
}
/// In-memory representation of error code information obtained from the
/// linter's `messages.yaml` file.
class LinterRuleInfo extends AnalyzerErrorCodeInfo {
final List<String> problemMessages;
/// Decodes an [LinterRuleInfo] object from its [ruleName] and
/// its [yaml] representation.
factory LinterRuleInfo.fromYaml(String ruleName, Map<Object?, Object?> yaml) {
var lint = linter.Registry.ruleRegistry[ruleName]!;
var problemMessages = lint.lintCodes
.map((code) => code.problemMessage)
.toList(growable: false);
return LinterRuleInfo._(
documentation: yaml['documentation'] as String?,
hasPublishedDocs: yaml['hasPublishedDocs'] as bool? ?? false,
problemMessages: problemMessages,
);
}
LinterRuleInfo._({
required this.problemMessages,
required super.documentation,
required super.hasPublishedDocs,
}) : super(problemMessage: problemMessages.firstOrNull ?? '');
@override
List<String> get formattedProblemMessages => problemMessages;
}

View file

@ -159,7 +159,8 @@ LintCode:
The analyzer produces this diagnostic when the statement after an `else`
is an empty statement (a semicolon).
For more information, see the documentation for [avoid_empty_else][].
For more information, see the documentation for
[`avoid_empty_else`](https://dart.dev/diagnostics/avoid_empty_else).
#### Example
@ -302,7 +303,8 @@ LintCode:
#### Common fixes
If you're writing code that uses Flutter, then use the function
`[debugPrint][]`, guarded by a test using `[kDebugMode][]`:
[`debugPrint`][debugPrint], guarded by a test
using [`kDebugMode`][kDebugMode]:
```dart
import 'package:flutter/foundation.dart';
@ -315,7 +317,7 @@ LintCode:
```
If you're writing code that doesn't use Flutter, then use a logging
service, such as `[package:logging][package-logging]`, to write the
service, such as [`package:logging`][package-logging], to write the
information.
avoid_relative_lib_imports:
documentation: |-
@ -1042,7 +1044,7 @@ LintCode:
The analyzer produces this diagnostic when a documentation comment that
appears to be library documentation isn't followed by a `library`
directive. More specificially, it is produced when a documentation comment
directive. More specifically, it is produced when a documentation comment
appears before the first directive in the library, assuming that it isn't
a `library` directive, or before the first top-level declaration and is
separated from the declaration by one or more blank lines.
@ -1989,7 +1991,7 @@ LintCode:
For example, given a package named `my_package`, here are the library
names for various files in the package:
```
```dart
// In lib/my_package.dart
library my_package;