// 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 main() async { await GeneratedContent.generateAll(pkg_root.packageRoot, allTargets); } /// A list of all targets generated by this code generator. final List allTargets = _analyzerGeneratedFiles(); /// Generates a list of [GeneratedContent] objects describing all the analyzer /// files that need to be generated. List _analyzerGeneratedFiles() { var classesByFile = >{}; for (var errorClassInfo in errorClasses) { if (errorClassInfo is! GeneratedErrorClassInfo) continue; // Lint codes are generated separately. if (errorClassInfo == linterLintCodeInfo) continue; (classesByFile[errorClassInfo.file] ??= []).add(errorClassInfo); } var generatedCodes = []; 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 errorClasses; final List 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)}; '''); 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, ...sharedToAnalyzerErrorCodeTables.analyzerCodeToInfo.entries, ].where( (error) => error.key.errorClass == errorClass && !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 _outputDerivedClass( GeneratedErrorClassInfo errorClass, { required bool withArguments, }) { var className = withArguments ? errorClass.templateName : errorClass.withoutArgumentsName; out.writeln('final class $className'); if (withArguments) out.writeln(''); 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 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''' part of 'diagnostic_code_values.dart'; '''); out.writeln(); out.writeln( "@AnalyzerPublicApi(message: 'exported by lib/error/error.dart')", ); out.writeln('const List diagnosticCodeValues = ['); for (var analyzerCode in generatedCodes) { var errorName = analyzerCode.camelCaseErrorName; out.writeln(' ${analyzerCode.errorClass.name}.$errorName,'); } out.writeln('];'); out.writeln(); _generateSharedAnalyzerCodeList(); out.writeln( "@AnalyzerPublicApi(message: 'exported by lib/error/error.dart')", ); out.writeln('@Deprecated("Use \'diagnosticCodeValues\' instead")'); out.writeln( 'List get errorCodeValues => diagnosticCodeValues;', ); } void _generateSharedAnalyzerCodeList() { out.writeln('final sharedAnalyzerCodes = ['); for (var entry in sharedToAnalyzerErrorCodeTables.sortedSharedErrors) { out.writeln('${entry.analyzerCode.analyzerCodeReference},'); } out.writeln('];'); } }