Skip to content

Commit ad3e2ed

Browse files
stereotype441Commit Queue
authored andcommitted
[front_end] Make a script to add "parameters" entries to messages.yaml.
The new script, `pkg/front_end/tool/add_parameters_to_messages.dart`, iterates through all the entries in `pkg/front_end/messages.yaml`. For each parameter used in a `problemMessage` or `correctionMessage`, it generates a `parameters` entry, with the appropriate parameter name and type, and a documentation placeholder ("undocumented"). Future CLs will fill in documentation strings and rename the parameters as needed for clarity. For messages that don't take any parameters, the script adds `parameters: none` to the YAML source. To make the script easier to write, a `yamlNode` field is added to the `ErrorCodeInfo` class; this makes it easy for the script to find the location in the YAML file where message is declared, so that it can add the `parameters` entry. Change-Id: I6a6a6964cbbd01bbe0cf7dbb1ef7944e0a61f6e9 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/448250 Reviewed-by: Johnni Winther <[email protected]> Commit-Queue: Paul Berry <[email protected]>
1 parent 902a2bf commit ad3e2ed

File tree

4 files changed

+105
-8
lines changed

4 files changed

+105
-8
lines changed

pkg/analyzer/tool/messages/error_code_info.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import 'package:analyzer_testing/package_root.dart' as pkg_root;
99
import 'package:analyzer_utilities/messages.dart';
1010
import 'package:analyzer_utilities/tools.dart';
1111
import 'package:path/path.dart';
12-
import 'package:yaml/yaml.dart' show loadYaml;
12+
import 'package:yaml/yaml.dart' show loadYaml, YamlMap;
1313

1414
const codesFile = GeneratedErrorCodeFile(
1515
path: 'analyzer/lib/src/error/codes.g.dart',
@@ -212,7 +212,7 @@ Map<String, Map<String, AnalyzerErrorCodeInfo>> decodeAnalyzerMessagesYaml(
212212
);
213213
}
214214
var errorValue = errorEntry.value;
215-
if (errorValue is! Map<Object?, Object?>) {
215+
if (errorValue is! YamlMap) {
216216
problem(
217217
'value associated with error $className.$errorName is not a '
218218
'map',
@@ -296,7 +296,7 @@ class AliasErrorCodeInfo extends AnalyzerErrorCodeInfo {
296296
/// In-memory representation of error code information obtained from the
297297
/// analyzer's `messages.yaml` file.
298298
class AnalyzerErrorCodeInfo extends ErrorCodeInfo {
299-
factory AnalyzerErrorCodeInfo.fromYaml(Map<Object?, Object?> yaml) {
299+
factory AnalyzerErrorCodeInfo.fromYaml(YamlMap yaml) {
300300
if (yaml['aliasFor'] case var aliasFor?) {
301301
return AliasErrorCodeInfo._fromYaml(yaml, aliasFor: aliasFor as String);
302302
} else {

pkg/analyzer_utilities/lib/messages.dart

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import 'package:analyzer_testing/package_root.dart' as pkg_root;
99
import 'package:analyzer_utilities/extensions/string.dart';
1010
import 'package:analyzer_utilities/tools.dart';
1111
import 'package:path/path.dart';
12-
import 'package:yaml/yaml.dart' show loadYaml;
12+
import 'package:yaml/yaml.dart' show loadYaml, YamlMap;
1313

1414
const Map<String, String> severityEnumNames = <String, String>{
1515
'CONTEXT': 'context',
@@ -105,7 +105,7 @@ Map<String, FrontEndErrorCodeInfo> decodeCfeMessagesYaml(Object? yaml) {
105105
problem('non-string error key ${json.encode(errorName)}');
106106
}
107107
var errorValue = entry.value;
108-
if (errorValue is! Map<Object?, Object?>) {
108+
if (errorValue is! YamlMap) {
109109
problem('value associated with error $errorName is not a map');
110110
}
111111
try {
@@ -119,8 +119,10 @@ Map<String, FrontEndErrorCodeInfo> decodeCfeMessagesYaml(Object? yaml) {
119119

120120
/// Loads front end messages from the front end's `messages.yaml` file.
121121
Map<String, FrontEndErrorCodeInfo> _loadFrontEndMessages() {
122+
var path = join(frontEndPkgPath, 'messages.yaml');
122123
Object? messagesYaml = loadYaml(
123-
File(join(frontEndPkgPath, 'messages.yaml')).readAsStringSync(),
124+
File(path).readAsStringSync(),
125+
sourceUrl: Uri.file(path),
124126
);
125127
return decodeCfeMessagesYaml(messagesYaml);
126128
}
@@ -302,6 +304,13 @@ abstract class ErrorCodeInfo {
302304
/// hasn't been translated from the old placeholder format yet.
303305
final List<ErrorCodeParameter>? parameters;
304306

307+
/// The raw YAML node that this `ErrorCodeInfo` was parsed from, or `null` if
308+
/// this `ErrorCodeInfo` was created without reference to a raw YAML node.
309+
///
310+
/// This exists to make it easier for automated scripts to edit the YAML
311+
/// source.
312+
final YamlMap? yamlNode;
313+
305314
ErrorCodeInfo({
306315
this.comment,
307316
this.documentation,
@@ -314,6 +323,7 @@ abstract class ErrorCodeInfo {
314323
this.previousName,
315324
this.removedIn,
316325
this.parameters,
326+
this.yamlNode,
317327
}) {
318328
for (var MapEntry(:key, :value) in {
319329
'problemMessage': problemMessage,
@@ -330,7 +340,7 @@ abstract class ErrorCodeInfo {
330340
}
331341

332342
/// Decodes an [ErrorCodeInfo] object from its YAML representation.
333-
ErrorCodeInfo.fromYaml(Map<Object?, Object?> yaml)
343+
ErrorCodeInfo.fromYaml(YamlMap yaml)
334344
: this(
335345
comment: yaml['comment'] as String?,
336346
correctionMessage: _decodeMessage(yaml['correctionMessage']),
@@ -344,6 +354,7 @@ abstract class ErrorCodeInfo {
344354
removedIn: yaml['removedIn'] as String?,
345355
previousName: yaml['previousName'] as String?,
346356
parameters: _decodeParameters(yaml['parameters']),
357+
yamlNode: yaml,
347358
);
348359

349360
/// If this error is no longer reported and
@@ -757,7 +768,7 @@ class FrontEndErrorCodeInfo extends ErrorCodeInfo {
757768
/// severity.
758769
final String? cfeSeverity;
759770

760-
FrontEndErrorCodeInfo.fromYaml(Map<Object?, Object?> yaml)
771+
FrontEndErrorCodeInfo.fromYaml(YamlMap yaml)
761772
: analyzerCode = _decodeAnalyzerCode(yaml['analyzerCode']),
762773
index = _decodeIndex(yaml['index']),
763774
cfeSeverity = _decodeSeverity(yaml['severity']),

pkg/front_end/pubspec.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,11 @@ dependencies:
2323
# Use 'any' constraints here; we get our versions from the DEPS file.
2424
dev_dependencies:
2525
analyzer: any
26+
analyzer_plugin: any
2627
analyzer_utilities: any
2728
args: any
2829
build_integration: any
30+
collection: any
2931
compiler: any
3032
dart_style: any
3133
dart2wasm: any
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
/// This is a temporary utility script that modifies
6+
/// `pkg/front_end/messages.yaml`, inserting explicit "parameters" declarations
7+
/// based on the existing parameter placeholders.
8+
///
9+
/// For example, a message like this:
10+
/// ```
11+
/// SwitchExpressionNotSubtype:
12+
/// problemMessage: >
13+
/// Type '#type' of the case expression is not a subtype of type '#type2'
14+
/// of this switch expression.
15+
/// ...
16+
/// ```
17+
///
18+
/// will get converted into:
19+
/// ```
20+
/// SwitchExpressionNotSubtype:
21+
/// parameters:
22+
/// Type type: undocumented
23+
/// Type type2: undocumented
24+
/// problemMessage: >
25+
/// Type '#type' of the case expression is not a subtype of type '#type2'
26+
/// of this switch expression.
27+
/// ...
28+
/// ```
29+
///
30+
/// Messages that don't take any parameters will have an entry added of the form
31+
/// `parameters: none`.
32+
library;
33+
34+
import 'dart:io';
35+
36+
import 'package:analyzer_plugin/protocol/protocol_common.dart';
37+
import 'package:analyzer_utilities/messages.dart';
38+
import 'package:collection/collection.dart';
39+
40+
void main() {
41+
var edits = <Uri, List<SourceEdit>>{};
42+
43+
for (var message in frontEndMessages.values) {
44+
if (message.parameters != null) {
45+
// Message already converted.
46+
continue;
47+
}
48+
var parameters = <String, String>{};
49+
for (var value in [message.problemMessage, message.correctionMessage]) {
50+
if (value == null) continue;
51+
for (Match match in placeholderPattern.allMatches(value)) {
52+
var parsedPlaceholder = ParsedPlaceholder.fromMatch(match);
53+
var type = parsedPlaceholder.templateParameterType.messagesYamlName;
54+
var name = parsedPlaceholder.name;
55+
parameters['$type $name'] = 'undocumented';
56+
}
57+
}
58+
List<String> linesToAdd;
59+
if (parameters.isEmpty) {
60+
linesToAdd = ['parameters: none'];
61+
} else {
62+
linesToAdd = [
63+
'parameters:',
64+
for (var entry in parameters.entries) ' ${entry.key}: ${entry.value}',
65+
];
66+
}
67+
var span = message.yamlNode!.span;
68+
var indent = ' ' * span.start.column;
69+
var text = linesToAdd.map((line) => '$line\n$indent').join();
70+
(edits[span.sourceUrl!] ??= []).add(SourceEdit(span.start.offset, 0, text));
71+
}
72+
73+
for (var entry in edits.entries) {
74+
var file = File(entry.key.toFilePath());
75+
stdout.write('Updating ${file.path}...');
76+
file.writeAsStringSync(
77+
SourceEdit.applySequence(
78+
file.readAsStringSync(),
79+
entry.value..sortBy((e) => -e.offset),
80+
),
81+
);
82+
stdout.writeln('done!');
83+
}
84+
}

0 commit comments

Comments
 (0)