blob: 6bee968431e78829697a4e50a771d8d80a1eeb53 [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/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 = <GeneratedErrorCodeFile, List<ErrorClassInfo>>{};
for (var errorClassInfo in errorClasses) {
(classesByFile[errorClassInfo.file] ??= []).add(errorClassInfo);
}
var generatedCodes = <AnalyzerCode>[];
return [
for (var entry in classesByFile.entries)
GeneratedFile(entry.key.path, (pkgRoot) async {
var codeGenerator = _AnalyzerErrorGenerator(
entry.key,
entry.value,
generatedCodes,
);
codeGenerator.generate();
return codeGenerator.out.toString();
}),
GeneratedFile('analyzer/lib/src/diagnostic/diagnostic_code_values.g.dart', (
pkgRoot,
) async {
var codeGenerator = _DiagnosticCodeValuesGenerator(generatedCodes);
codeGenerator.generate();
return codeGenerator.out.toString();
}),
];
}
/// Code generator for analyzer error classes.
class _AnalyzerErrorGenerator {
final GeneratedErrorCodeFile file;
final List<ErrorClassInfo> errorClasses;
final List<AnalyzerCode> generatedCodes;
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.
// While transitioning `HintCodes` to `WarningCodes`, we refer to deprecated
// codes here.
''');
_AnalyzerErrorGenerator(this.file, this.errorClasses, this.generatedCodes);
void generate() {
out.writeln('// ignore_for_file: deprecated_member_use_from_same_package');
if (file.shouldIgnorePreferSingleQuotes) {
out.writeln('// ignore_for_file: prefer_single_quotes');
}
out.write('''
//
// Generated comments don't quite align with flutter style.
// ignore_for_file: flutter_style_todos
''');
out.writeln();
out.write('''
part of ${json.encode(file.parentLibrary)};
''');
bool shouldGenerateFastaAnalyzerErrorCodes = false;
for (var errorClass in errorClasses) {
if (errorClass.includeCfeMessages) {
shouldGenerateFastaAnalyzerErrorCodes = true;
}
}
if (shouldGenerateFastaAnalyzerErrorCodes) {
out.writeln();
_generateFastaAnalyzerErrorCodeList();
}
for (var errorClass
in errorClasses.toList()..sort((a, b) => a.name.compareTo(b.name))) {
out.writeln();
if (errorClass.comment.isNotEmpty) {
errorClass.comment.trimRight().split('\n').forEach((line) {
out.writeln('/// $line');
});
}
out.write(
'class ${errorClass.name} extends DiagnosticCodeWithExpectedTypes {',
);
var memberAccumulator = MemberAccumulator();
var entries =
[
...analyzerMessages.entries,
if (errorClass.includeCfeMessages)
...sharedToAnalyzerErrorCodeTables.analyzerCodeToInfo.entries,
].where(
(error) =>
error.key.className == errorClass.name &&
!error.value.isRemoved,
);
for (var entry in entries) {
var errorCode = entry.key;
var errorName = errorCode.snakeCaseErrorName;
var errorCodeInfo = entry.value;
try {
if (errorCodeInfo is! AliasErrorCodeInfo &&
errorClass.includeInDiagnosticCodeValues) {
generatedCodes.add(errorCode);
}
errorCodeInfo.toAnalyzerCode(
errorClass,
errorName,
memberAccumulator: memberAccumulator,
);
} catch (e, st) {
Error.throwWithStackTrace(
'While processing ${errorClass.name}.$errorName: $e',
st,
);
}
}
var constructor = StringBuffer();
constructor.writeln(
'/// Initialize a newly created error code to have the given '
'[name].',
);
constructor.writeln(
'const ${errorClass.name}(String name, String problemMessage, {',
);
constructor.writeln('super.correctionMessage,');
constructor.writeln('super.hasPublishedDocs = false,');
constructor.writeln('super.isUnresolvedIdentifier = false,');
constructor.writeln('String? uniqueName,');
constructor.writeln('required super.expectedTypes,');
constructor.writeln('}) : super(');
constructor.writeln('name: name,');
constructor.writeln('problemMessage: problemMessage,');
constructor.writeln(
"uniqueName: '${errorClass.name}.\${uniqueName ?? name}',",
);
constructor.writeln(');');
memberAccumulator.constructors[''] = constructor.toString();
memberAccumulator.accessors['severity'] =
'''
@override
DiagnosticSeverity get severity => ${errorClass.severityCode};
''';
memberAccumulator.accessors['type'] =
'''
@override
DiagnosticType get type => ${errorClass.typeCode};
''';
memberAccumulator.writeTo(out);
out.writeln('}');
out.writeln();
_outputDerivedClass(errorClass, withArguments: true);
out.writeln();
_outputDerivedClass(errorClass, withArguments: false);
}
}
void _generateFastaAnalyzerErrorCodeList() {
out.writeln('final fastaAnalyzerErrorCodes = <DiagnosticCode?>[');
for (var entry in sharedToAnalyzerErrorCodeTables.indexToInfo) {
if (sharedToAnalyzerErrorCodeTables.infoToAnalyzerCode[entry]
case var analyzerCode?) {
out.writeln(
'${analyzerCode.className}.${analyzerCode.camelCaseErrorName},',
);
} else {
out.writeln('null,');
}
}
out.writeln('];');
}
void _outputDerivedClass(
ErrorClassInfo errorClass, {
required bool withArguments,
}) {
var className = withArguments
? errorClass.templateName
: errorClass.withoutArgumentsName;
out.writeln('final class $className');
if (withArguments) out.writeln('<T extends Function>');
out.writeln(' extends ${errorClass.name}');
if (!withArguments) out.writeln(' with DiagnosticWithoutArguments');
out.writeln('{');
if (withArguments) {
out.writeln('final T withArguments;');
out.writeln();
}
out.writeln(
'/// Initialize a newly created error code to have the given '
'[name].',
);
out.writeln('const $className(');
out.writeln('super.name,');
out.writeln('super.problemMessage, {');
out.writeln('super.correctionMessage,');
out.writeln('super.hasPublishedDocs = false,');
out.writeln('super.isUnresolvedIdentifier = false,');
out.writeln('super.uniqueName,');
out.writeln('required super.expectedTypes,');
if (withArguments) out.writeln('required this.withArguments,');
out.writeln('});');
out.writeln('}');
}
}
class _DiagnosticCodeValuesGenerator {
final List<AnalyzerCode> generatedCodes;
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.
// We allow some snake_case and SCREAMING_SNAKE_CASE identifiers in generated
// code, as they match names declared in the source configuration files.
// ignore_for_file: constant_identifier_names
// While transitioning `HintCodes` to `WarningCodes`, we refer to deprecated
// codes here.
// ignore_for_file: deprecated_member_use_from_same_package
''');
_DiagnosticCodeValuesGenerator(this.generatedCodes);
void generate() {
generatedCodes.sort();
out.writeln();
out.writeln(r'''
import 'package:_fe_analyzer_shared/src/base/analyzer_public_api.dart';
import 'package:_fe_analyzer_shared/src/base/errors.dart';
import 'package:analyzer/src/dart/error/ffi_code.dart';
import 'package:analyzer/src/dart/error/syntactic_errors.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/manifest/manifest_warning_code.dart';
import 'package:analyzer/src/pubspec/pubspec_warning_code.dart';
''');
out.writeln();
out.writeln(
"@AnalyzerPublicApi(message: 'exported by lib/error/error.dart')",
);
out.writeln('const List<DiagnosticCode> diagnosticCodeValues = [');
for (var analyzerCode in generatedCodes) {
var errorName = analyzerCode.camelCaseErrorName;
out.writeln(' ${analyzerCode.className}.$errorName,');
}
out.writeln('];');
out.writeln();
out.writeln(
"@AnalyzerPublicApi(message: 'exported by lib/error/error.dart')",
);
out.writeln('@Deprecated("Use \'diagnosticCodeValues\' instead")');
out.writeln(
'List<DiagnosticCode> get errorCodeValues => diagnosticCodeValues;',
);
}
}