[analyzer] Stop generating the diagnostics.md file

dart.dev now consumes the source YAML files, so generating the Markdown file is no longer necessary.

The contents of the YAML files are still verified as before.

Resolves https://github.com/dart-lang/site-www/issues/6387

Change-Id: I8b989b598ec3c7abef66242dceb9d5a481f43e39
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/407640
Reviewed-by: Phil Quitslund <pquitslund@google.com>
Commit-Queue: Johnni Winther <johnniwinther@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Auto-Submit: Parker Lougheed <parlough@gmail.com>
Reviewed-by: Johnni Winther <johnniwinther@google.com>
This commit is contained in:
Parker Lougheed
2025-02-03 22:13:10 -08:00
committed by Commit Queue
parent 7bb0c4cdf4
commit d678a7e191
7 changed files with 4 additions and 30413 deletions
@@ -3,11 +3,9 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:analyzer/error/error.dart';
import 'package:analyzer/file_system/physical_file_system.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../tool/diagnostics/generate.dart';
import '../tool/messages/error_code_documentation_info.dart';
import '../tool/messages/error_code_info.dart';
import 'src/dart/resolution/context_collection_resolution.dart';
@@ -421,25 +419,6 @@ class VerifyDiagnosticsTest {
//
DocumentationValidator validator = DocumentationValidator();
await validator.validate();
//
// Validate that the generator has been run.
//
String actualContent = PhysicalResourceProvider.INSTANCE
.getFile(computeOutputPath())
.readAsStringSync();
// Normalize Windows line endings to Unix line endings so that the
// comparison doesn't fail on Windows.
actualContent = actualContent.replaceAll('\r\n', '\n');
StringBuffer sink = StringBuffer();
DocumentationGenerator generator = DocumentationGenerator();
generator.writeDocumentation(sink);
String expectedContent = sink.toString();
if (actualContent != expectedContent) {
fail('The diagnostic documentation needs to be regenerated.\n'
'Please run tool/diagnostics/generate.dart.');
}
}
test_published() {
File diff suppressed because it is too large Load Diff
-268
View File
@@ -1,268 +0,0 @@
// Copyright (c) 2019, 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.
import 'dart:io';
import 'package:analyzer/file_system/physical_file_system.dart';
import 'package:analyzer_utilities/package_root.dart' as package_root;
import '../messages/error_code_documentation_info.dart';
import '../messages/error_code_info.dart';
/// Generate the file `diagnostics.md` based on the documentation associated
/// with the declarations of the error codes.
Future<void> main() async {
var sink = File(computeOutputPath()).openWrite();
var generator = DocumentationGenerator();
generator.writeDocumentation(sink);
await sink.flush();
await sink.close();
}
/// Compute the path to the file into which documentation is being generated.
String computeOutputPath() {
var pathContext = PhysicalResourceProvider.INSTANCE.pathContext;
var packageRoot = pathContext.normalize(package_root.packageRoot);
var analyzerPath = pathContext.join(packageRoot, 'analyzer');
return pathContext.join(
analyzerPath, 'tool', 'diagnostics', 'diagnostics.md');
}
/// An information holder containing information about a diagnostic that was
/// extracted from the instance creation expression.
class DiagnosticInformation {
/// The name of the diagnostic.
final String name;
/// The messages associated with the diagnostic.
final List<String> messages;
/// The previous names by which this diagnostic has been known.
List<String> previousNames = [];
/// The documentation text associated with the diagnostic.
String? documentation;
/// Initialize a newly created information holder with the given [name] and
/// [message].
DiagnosticInformation(this.name, String message) : messages = [message];
/// Return `true` if this diagnostic has documentation.
bool get hasDocumentation => documentation != null;
/// Add the [message] to the list of messages associated with the diagnostic.
void addMessage(String message) {
if (!messages.contains(message)) {
messages.add(message);
}
}
void addPreviousName(String previousName) {
if (!previousNames.contains(previousName)) {
previousNames.add(previousName);
}
}
/// Return the full documentation for this diagnostic.
void writeOn(StringSink sink) {
messages.sort();
sink.writeln('### ${name.toLowerCase()}');
for (var previousName in previousNames) {
sink.writeln();
var previousInLowerCase = previousName.toLowerCase();
sink.writeln('<a id="$previousInLowerCase" aria-hidden="true"></a>'
'_(Previously known as `$previousInLowerCase`)_');
}
for (String message in messages) {
sink.writeln();
for (String line in _split('_${_escape(message)}_')) {
sink.writeln(line);
}
}
sink.writeln();
sink.writeln(documentation!);
}
/// Return a version of the [text] in which characters that have special
/// meaning in markdown have been escaped.
String _escape(String text) {
return text.replaceAll('_', '\\_');
}
/// Split the [message] into multiple lines, each of which is less than 80
/// characters long.
List<String> _split(String message) {
// This uses a brute force approach because we don't expect to have messages
// that need to be split more than once.
int length = message.length;
if (length <= 80) {
return [message];
}
int endIndex = message.lastIndexOf(' ', 80);
if (endIndex < 0) {
return [message];
}
return [message.substring(0, endIndex), message.substring(endIndex + 1)];
}
}
/// A class used to generate diagnostic documentation.
class DocumentationGenerator {
/// A map from the name of a diagnostic to the information about that
/// diagnostic.
Map<String, DiagnosticInformation> infoByName = {};
/// Initialize a newly created documentation generator.
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(
errorClass.name, cfeToAnalyzerErrorCodeTables.analyzerCodeToInfo);
// Note: only one error class has the `includeCfeMessages` flag set;
// verify_diagnostics_test.dart verifies this. So we can safely break.
break;
}
}
}
/// Writes the documentation to [sink].
void writeDocumentation(StringSink sink) {
_writeHeader(sink);
_writeGlossary(sink);
_writeDiagnostics(sink);
}
/// Extract documentation from all of the files containing the definitions of
/// diagnostics.
void _extractAllDocs(String className, Map<String, ErrorCodeInfo> messages) {
for (var errorEntry in messages.entries) {
var errorName = errorEntry.key;
var errorCodeInfo = errorEntry.value;
if (errorCodeInfo is AliasErrorCodeInfo) {
continue;
}
var name = errorCodeInfo.sharedName ?? errorName;
var info = infoByName[name];
var message = convertTemplate(
errorCodeInfo.computePlaceholderToIndexMap(),
errorCodeInfo.problemMessage);
if (info == null) {
info = DiagnosticInformation(name, message);
infoByName[name] = info;
} else {
info.addMessage(message);
}
var previousName = errorCodeInfo.previousName;
if (previousName != null) {
info.addPreviousName(previousName);
}
var docs = _extractDoc('$className.$errorName', errorCodeInfo);
if (docs.isNotEmpty) {
if (info.documentation != null) {
throw StateError(
'Documentation defined multiple times for ${info.name}');
}
info.documentation = docs;
}
}
}
/// Extract documentation from the given [errorCodeInfo].
String _extractDoc(String errorCode, ErrorCodeInfo errorCodeInfo) {
var parsedComment =
parseErrorCodeDocumentation(errorCode, errorCodeInfo.documentation);
if (parsedComment == null) {
return '';
}
return [
for (var documentationPart in parsedComment)
documentationPart.formatForDocumentation()
].join('\n');
}
/// Write the documentation for all of the diagnostics.
void _writeDiagnostics(StringSink sink) {
sink.write('''
## Diagnostics
The analyzer produces the following diagnostics for code that
doesn't conform to the language specification or
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/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
[meta-doNotStore]: https://pub.dev/documentation/meta/latest/meta/doNotStore-constant.html
[meta-doNotSubmit]: https://pub.dev/documentation/meta/latest/meta/doNotSubmit-constant.html
[meta-factory]: https://pub.dev/documentation/meta/latest/meta/factory-constant.html
[meta-immutable]: https://pub.dev/documentation/meta/latest/meta/immutable-constant.html
[meta-internal]: https://pub.dev/documentation/meta/latest/meta/internal-constant.html
[meta-literal]: https://pub.dev/documentation/meta/latest/meta/literal-constant.html
[meta-mustBeConst]: https://pub.dev/documentation/meta/latest/meta/mustBeConst-constant.html
[meta-mustCallSuper]: https://pub.dev/documentation/meta/latest/meta/mustCallSuper-constant.html
[meta-optionalTypeArgs]: https://pub.dev/documentation/meta/latest/meta/optionalTypeArgs-constant.html
[meta-sealed]: https://pub.dev/documentation/meta/latest/meta/sealed-constant.html
[meta-useResult]: https://pub.dev/documentation/meta/latest/meta/useResult-constant.html
[meta-UseResult]: https://pub.dev/documentation/meta/latest/meta/UseResult-class.html
[meta-visibleForOverriding]: https://pub.dev/documentation/meta/latest/meta/visibleForOverriding-constant.html
[meta-visibleForTesting]: https://pub.dev/documentation/meta/latest/meta/visibleForTesting-constant.html
[package-logging]: https://pub.dev/packages/logging
[refutable pattern]: https://dart.dev/resources/glossary#refutable-pattern
''');
var errorCodes = infoByName.keys.toList();
errorCodes.sort();
for (String errorCode in errorCodes) {
var info = infoByName[errorCode]!;
if (info.hasDocumentation) {
sink.writeln();
info.writeOn(sink);
}
}
}
/// Link to the glossary.
void _writeGlossary(StringSink sink) {
sink.write(r'''
[constant context]: /resources/glossary#constant-context
[definite assignment]: /resources/glossary#definite-assignment
[mixin application]: /resources/glossary#mixin-application
[override inference]: /resources/glossary#override-inference
[part file]: /resources/glossary#part-file
[potentially non-nullable]: /resources/glossary#potentially-non-nullable
[public library]: /resources/glossary#public-library
''');
}
/// Write the header of the file.
void _writeHeader(StringSink sink) {
sink.write('''
---
title: Diagnostic messages
description: Details for diagnostics produced by the Dart analyzer.
body_class: highlight-diagnostics
---
{%- comment %}
WARNING: Do NOT EDIT this file directly. It is autogenerated by the script in
`pkg/analyzer/tool/diagnostics/generate.dart` in the sdk repository.
Update instructions: https://github.com/dart-lang/site-www/issues/1949
{% endcomment -%}
This page lists diagnostic messages produced by the Dart analyzer,
with details about what those messages mean and how you can fix your code.
For more information about the analyzer, see
[Customizing static analysis](/tools/analysis).
''');
}
}
@@ -63,19 +63,12 @@ class ErrorCodeDocumentationBlock extends ErrorCodeDocumentationPart {
required this.fileType,
this.languageVersion,
this.uri});
@override
String formatForDocumentation() => ['```$fileType', text, '```'].join('\n');
}
/// A portion of an error code's documentation. This could be free form
/// markdown text ([ErrorCodeDocumentationText]) or a code block
/// ([ErrorCodeDocumentationBlock]).
abstract class ErrorCodeDocumentationPart {
/// Formats this documentation part as text suitable for inclusion in the
/// analyzer's `diagnostics.md` file.
String formatForDocumentation();
}
abstract class ErrorCodeDocumentationPart {}
/// An [ErrorCodeDocumentationPart] containing free form markdown text.
class ErrorCodeDocumentationText extends ErrorCodeDocumentationPart {
@@ -83,9 +76,6 @@ class ErrorCodeDocumentationText extends ErrorCodeDocumentationPart {
final String text;
ErrorCodeDocumentationText(this.text);
@override
String formatForDocumentation() => text;
}
class _ErrorCodeDocumentationParser {
+3 -4
View File
@@ -67,10 +67,9 @@
# the `sharedName`.
#
# Errors with an `index` can also optionally contain user-facing documentation
# for the problem (documentation), which will be extracted to
# `pkg/analyzer/tool/diagnostics/diagnostics.md`, as well as internal
# documentation (comment), which will be included in the code generated for the
# analyzer.
# for the problem (documentation), which dart.dev will extract,
# as well as internal documentation (comment), which will be included in
# the code generated for the analyzer.
#
# In some cases a message is internal to the frontend, and no meaningful
# analyzer code can be provided. In such cases set `frontendInternal: true`.
-9
View File
@@ -266,15 +266,7 @@ control_flow_in_finally:
...
```
If you do add or change a lint's diagnostic documentation,
make sure to regenerate the resulting [`diagnostics.md` file][]:
```
dart run pkg/analyzer/tool/diagnostics/generate.dart
```
[documenting diagnostics]: ../../analyzer/doc/implementation/diagnostics.md#document-the-diagnostic
[`diagnostics.md` file]: ../../analyzer/tool/diagnostics/diagnostics.md
## Mechanics
@@ -399,7 +391,6 @@ make sure all generated files are up to date.
```
dart run pkg/linter/tool/generate_lints.dart
dart run pkg/analyzer/tool/diagnostics/generate.dart
```
### Utilities
-5
View File
@@ -6,11 +6,6 @@
# make sure to regenerate supporting classes by running:
#
# dart run pkg/linter/tool/generate_lints.dart
#
# If you updated or added a `documentation` property,
# make sure to regenerate the documentation by running:
#
# dart run pkg/analyzer/tool/diagnostics/generate.dart
# There is a fixed set of categories:
#