// 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. import 'package:analysis_server_client/protocol.dart'; import 'package:analysis_server_client/server.dart'; import 'package:analyzer/dart/analysis/analysis_context_collection.dart'; import 'package:analyzer/dart/analysis/results.dart'; import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/src/utilities/extensions/object.dart'; import 'package:analyzer/src/utilities/extensions/string.dart'; import 'package:analyzer_testing/package_root.dart' as pkg_root; import 'package:analyzer_utilities/tools.dart'; import 'package:path/path.dart'; void main() async { await GeneratedContent.generateAll(pkg_root.packageRoot, await allTargets); } Future> get allTargets async { var astLibrary = await _getElementLibrary(); return [ GeneratedFile('analyzer/lib/src/dart/element/element.g.dart', (_) async { var generator = _ElementGenerator(astLibrary); return await generator.generate(); }), ]; } String get _analyzerPath => normalize(join(pkg_root.packageRoot, 'analyzer')); Future _formatSortCode(String path, String code) async { var server = Server(); await server.start(); server.listenToOutput(); await server.send('analysis.setAnalysisRoots', { 'included': [path], 'excluded': [], }); Future updateContent() async { await server.send('analysis.updateContent', { 'files': { path: {'type': 'add', 'content': code}, }, }); } await updateContent(); var formatResponse = await server.send('edit.format', { 'file': path, 'selectionOffset': 0, 'selectionLength': code.length, }); var formatResult = EditFormatResult.fromJson( ResponseDecoder(null), 'result', formatResponse, ); code = SourceEdit.applySequence(code, formatResult.edits); await updateContent(); var sortResponse = await server.send('edit.sortMembers', {'file': path}); var sortResult = EditSortMembersResult.fromJson( ResponseDecoder(null), 'result', sortResponse, ); code = SourceEdit.applySequence(code, sortResult.edit.edits); await server.kill(); return code; } Future _getElementLibrary() async { var collection = AnalysisContextCollection(includedPaths: [_analyzerPath]); var analysisContext = collection.contextFor(_analyzerPath); var analysisSession = analysisContext.currentSession; var libraryResult = await analysisSession.getLibraryByUri( 'package:analyzer/src/dart/element/element.dart', ); libraryResult as LibraryElementResult; return libraryResult.element; } class _ElementGenerator { final LibraryElement astLibrary; 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. // // Run 'dart pkg/analyzer/tool/element/generate.dart' to update. part of 'element.dart'; '''); _ElementGenerator(this.astLibrary); Future generate() async { _writeMixins(); var resultPath = normalize( join(_analyzerPath, 'lib', 'src', 'dart', 'element', 'element.g.dart'), ); return _formatSortCode(resultPath, out.toString()); } void _writeMixins() { for (var generateFragment in astLibrary.generateFragments) { var fragmentName = generateFragment.element.name; out.write(''' mixin _${fragmentName}Mixin { bool hasModifier(Modifier modifier); void setModifier(Modifier modifier, bool value); '''); for (var modifier in generateFragment.modifiers) { var name = modifier.name; var constName = name.removePrefixOrSelf('is').toScreamingSnake(); // TODO(scheglov): remove after removing `isSynthetic`. if (fragmentName == 'FragmentImpl' && name == 'isSynthetic') { out.writeln("@Deprecated('Use isOriginX instead')"); } out.write(''' ${modifier.documentationComment ?? ''} bool get $name { return hasModifier(Modifier.$constName); } set $name(bool value) { setModifier(Modifier.$constName, value); } '''); } out.writeln('}\n'); } } } class _GenerateElementModifier { final String name; final String? documentationComment; _GenerateElementModifier({ required this.name, required this.documentationComment, }); } class _GenerateFragment { final ClassElement element; final List<_GenerateElementModifier> modifiers; _GenerateFragment(this.element, this.modifiers); bool get isConcrete => !element.isAbstract; _GenerateFragment? get superNode { return element.supertype?.element .ifTypeOrNull() ?.asGenerateFragment; } } extension on ClassElement { _GenerateFragment? get asGenerateFragment { var generateObject = metadata.annotations .map((annotation) { var generateObject = annotation.computeConstantValue(); var generateObjectType = generateObject?.type; if (generateObjectType?.element?.name != 'GenerateFragmentImpl') { return null; } return generateObject; }) .nonNulls .firstOrNull; if (generateObject == null) { return null; } var modifiersField = generateObject.getField('modifiers')!; var modifiers = modifiersField.toListValue()!.map((modifier) { var variable = modifier.variable!; return _GenerateElementModifier( name: variable.name!, documentationComment: variable.documentationComment, ); }).toList(); return _GenerateFragment(this, modifiers); } } extension on LibraryElement { List<_GenerateFragment> get generateFragments { return classes .map((element) => element.asGenerateFragment) .nonNulls .toList(); } }