blob: bbb4b758d3186bd101574b9440b01f4feb8177f7 [file] [log] [blame] [edit]
// Copyright (c) 2021, 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.
/// This file contains code to generate scanner and parser message
/// based on the information in pkg/front_end/messages.yaml.
///
/// For each message in messages.yaml that contains an 'index:' field,
/// this tool generates an error with the name specified by the 'analyzerCode:'
/// field and an entry in the fastaAnalyzerErrorList for that generated error.
/// The text in the 'analyzerCode:' field must contain the name of the class
/// containing the error and the name of the error separated by a `.`
/// (e.g. ParserErrorCode.EQUALITY_CANNOT_BE_EQUALITY_OPERAND).
///
/// It is expected that 'dart pkg/front_end/tool/generate_messages.dart'
/// has already been successfully run.
library;
import 'dart:convert';
import 'package:analyzer_testing/package_root.dart' as pkg_root;
import 'package:analyzer_utilities/analyzer_messages.dart';
import 'package:analyzer_utilities/located_error.dart';
import 'package:analyzer_utilities/messages.dart';
import 'package:analyzer_utilities/tools.dart';
Future<void> main() async {
await GeneratedContent.generateAll(pkg_root.packageRoot, allTargets);
}
/// A list of all targets generated by this code generator.
final List<GeneratedContent> allTargets = _analyzerGeneratedFiles();
/// Generates a list of [GeneratedContent] objects describing all the analyzer
/// files that need to be generated.
List<GeneratedContent> _analyzerGeneratedFiles() {
var classesByFile = <GeneratedDiagnosticFile, List<DiagnosticClassInfo>>{};
for (var diagnosticClassInfo in diagnosticClasses) {
// Lint codes are generated separately.
if (diagnosticClassInfo == linterLintCodeInfo) continue;
(classesByFile[diagnosticClassInfo.file] ??= []).add(diagnosticClassInfo);
}
return [
for (var entry in classesByFile.entries)
GeneratedFile(entry.key.path, (pkgRoot) async {
var codeGenerator = _AnalyzerDiagnosticClassGenerator(
entry.key,
entry.value,
);
codeGenerator.generate();
return codeGenerator.out.toString();
}),
GeneratedFile('analyzer/lib/src/diagnostic/diagnostic_code_values.g.dart', (
pkgRoot,
) async {
var codeGenerator = _DiagnosticCodeValuesGenerator();
codeGenerator.generate();
return codeGenerator.out.toString();
}),
for (var package in AnalyzerDiagnosticPackage.values)
GeneratedFile(
'${package.dirName}/lib/${package.diagnosticPathPart}.g.dart',
(pkgRoot) async {
var codeGenerator = _AnalyzerDiagnosticGenerator(
package: package,
parentLibrary:
'package:${package.dirName}/${package.diagnosticPathPart}.dart',
);
codeGenerator.generate();
return codeGenerator.out.toString();
},
),
];
}
/// Code generator for analyzer diagnostic classes.
class _AnalyzerDiagnosticClassGenerator {
final GeneratedDiagnosticFile file;
final List<DiagnosticClassInfo> diagnosticClasses;
final StringBuffer out = StringBuffer('''
// Copyright (c) 2021, 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.
// THIS FILE IS GENERATED. DO NOT EDIT.
//
// Instead modify 'pkg/analyzer/messages.yaml' and run
// 'dart run pkg/analyzer/tool/messages/generate.dart' to update.
''');
_AnalyzerDiagnosticClassGenerator(this.file, this.diagnosticClasses);
void generate() {
file.package.writeIgnoresTo(out);
out.writeln();
out.write('''
part of ${json.encode(file.parentLibrary)};
''');
for (var diagnosticClass
in diagnosticClasses.toList()
..sort((a, b) => a.name.compareTo(b.name))) {
out.writeln();
if (diagnosticClass.comment.isNotEmpty) {
diagnosticClass.comment.trimRight().split('\n').forEach((line) {
out.writeln('/// $line');
});
}
out.write('class ${diagnosticClass.name} {');
var memberAccumulator = MemberAccumulator();
for (var message
in diagnosticTables.activeMessagesByPackage[diagnosticClass
.file
.package]!) {
if (message.analyzerCode.diagnosticClass != diagnosticClass) continue;
LocatedError.wrap(span: message.keySpan, () {
message.toClassMember(memberAccumulator: memberAccumulator);
});
}
var constructor = StringBuffer();
constructor.writeln('/// Do not construct instances of this class.');
constructor.writeln('${diagnosticClass.name}._() : assert(false);');
memberAccumulator.constructors['_'] = constructor.toString();
memberAccumulator.writeTo(out);
out.writeln('}');
}
}
}
/// Code generator for files containing analyzer diagnostics as top level
/// constants.
class _AnalyzerDiagnosticGenerator {
/// The package into which diagnostic will be generated.
final AnalyzerDiagnosticPackage package;
/// The Uri of the library that the generated file will be a part of.
final String parentLibrary;
final StringBuffer out = StringBuffer('''
// Copyright (c) 2025, 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.
// THIS FILE IS GENERATED. DO NOT EDIT.
//
// Instead modify 'pkg/analyzer/messages.yaml' and run
// 'dart run pkg/analyzer/tool/messages/generate.dart' to update.
''');
_AnalyzerDiagnosticGenerator({
required this.package,
required this.parentLibrary,
});
void generate() {
package.writeIgnoresTo(out);
out.writeln();
out.write('''
part of ${json.encode(parentLibrary)};
''');
out.writeln();
var memberAccumulator = MemberAccumulator();
for (var message in diagnosticTables.activeMessagesByPackage[package]!) {
LocatedError.wrap(span: message.keySpan, () {
message.toAnalyzerCode(memberAccumulator: memberAccumulator);
});
}
memberAccumulator.writeTo(out);
}
}
class _DiagnosticCodeValuesGenerator {
final StringBuffer out = StringBuffer('''
// Copyright (c) 2014, 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.
// THIS FILE IS GENERATED. DO NOT EDIT.
//
// Instead modify 'pkg/analyzer/messages.yaml' and run
// 'dart run pkg/analyzer/tool/messages/generate.dart' to update.
''');
void generate() {
AnalyzerDiagnosticPackage.analyzer.writeIgnoresTo(out);
out.writeln();
out.writeln(r'''
part of 'diagnostic_code_values.dart';
''');
out.writeln();
out.writeln(
"@AnalyzerPublicApi(message: 'exported by lib/error/error.dart')",
);
out.writeln('const List<DiagnosticCode> diagnosticCodeValues = [');
for (var message
in diagnosticTables.activeMessagesByPackage[AnalyzerDiagnosticPackage
.analyzer]!) {
out.writeln(' ${message.analyzerCode.analyzerCodeReference},');
}
out.writeln('];');
out.writeln();
_generateSharedAnalyzerCodeList();
out.writeln(
"@AnalyzerPublicApi(message: 'exported by lib/error/error.dart')",
);
out.writeln('@Deprecated("Use \'diagnosticCodeValues\' instead")');
out.writeln(
'List<DiagnosticCode> get errorCodeValues => diagnosticCodeValues;',
);
}
void _generateSharedAnalyzerCodeList() {
out.writeln('final sharedAnalyzerCodes = <DiagnosticCode>[');
for (var entry in diagnosticTables.sortedSharedDiagnostics) {
out.writeln('${entry.analyzerCode.analyzerCodeReference},');
}
out.writeln('];');
}
}