Skip to content

update how we calculate import prefixes #1012

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions protoc_plugin/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## 22.4.0-wip

* Update how we calculate import prefixes ([#1010]); import prefixes are now
unique per-library instead of being unique across all generated libraries.

[#1010]: https://github.com/google/protobuf.dart/issues/1010

## 22.3.0

* Update the generated code to improve readability and to better follow common
Expand Down
6 changes: 3 additions & 3 deletions protoc_plugin/lib/src/base_type.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ class BaseType {
String get package => generator == null ? '' : generator!.package;

/// The Dart expression to use for this type when in a different file.
String get prefixed => generator == null
String prefixed(FileGenerator fileGen) => generator == null
? unprefixed
: '${generator!.fileImportPrefix}.$unprefixed';
: '${generator!.importPrefix(context: fileGen)}.$unprefixed';

/// Returns the name to use in generated code for this Dart type.
///
Expand All @@ -51,7 +51,7 @@ class BaseType {
String getDartType(FileGenerator fileGen) =>
(fileGen.protoFileUri == generator?.fileGen?.protoFileUri)
? unprefixed
: prefixed;
: prefixed(fileGen);

String getRepeatedDartType(FileGenerator fileGen) =>
'$protobufImportPrefix.PbList<${getDartType(fileGen)}>';
Expand Down
22 changes: 8 additions & 14 deletions protoc_plugin/lib/src/code_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,8 @@ import 'options.dart';
import 'output_config.dart';

abstract class ProtobufContainer {
// Internal map of proto file URIs to prefix aliases to resolve name conflicts
static final _importPrefixes = <String, String>{};
static int _idx = 0;
// A map of proto file paths to import prefix aliases.
final Map<String, String> _prefixes = {};

String get package;
String? get classname;
Expand All @@ -40,22 +39,17 @@ abstract class ProtobufContainer {
/// This exists because names from protoc come like this.
String get dottedName => '.$fullName';

String get fileImportPrefix => _getFileImportPrefix();

String get binaryDescriptorName =>
'${lowerCaseFirstLetter(classname!)}Descriptor';

String _getFileImportPrefix() {
final path = fileGen!.protoFileUri.toString();
if (_importPrefixes.containsKey(path)) {
return _importPrefixes[path]!;
}
final alias = '\$$_idx';
_importPrefixes[path] = alias;
_idx++;
return alias;
String importPrefix({required FileGenerator context}) {
final protoFilePath = fileGen!.protoFileUri.toString();
return context._calculateImportPrefix(protoFilePath);
}

String _calculateImportPrefix(String protoImportPath) =>
_prefixes.putIfAbsent(protoImportPath, () => '\$${_prefixes.length}');

/// The generator of the .pb.dart file defining this entity.
///
/// (Represents the .pb.dart file that we need to import in order to use it.)
Expand Down
10 changes: 6 additions & 4 deletions protoc_plugin/lib/src/enum_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,15 @@ class EnumGenerator extends ProtobufContainer {
}

/// Returns a const expression that evaluates to the JSON for this message.
/// [usage] represents the .pb.dart file where the expression will be used.
String getJsonConstant(FileGenerator usage) {
///
/// [context] represents the .pb.dart file where the expression will be used.
String getJsonConstant(FileGenerator context) {
final name = '$classname\$json';
if (usage.protoFileUri == fileGen!.protoFileUri) {
if (context.protoFileUri == fileGen!.protoFileUri) {
return name;
}
return '$fileImportPrefix.$name';

return '${importPrefix(context: context)}.$name';
}

static const int _enumValueTag = 2;
Expand Down
8 changes: 5 additions & 3 deletions protoc_plugin/lib/src/file_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -671,14 +671,16 @@ class FileGenerator extends ProtobufContainer {
void _addImport(ImportWriter importWriter, OutputConfiguration config,
FileGenerator target, String ext) {
final url = config.resolveImport(target.protoFileUri, protoFileUri, ext);
final import = url.toString();

// .pb.dart files should always be prefixed -- the protoFileUri check will
// evaluate to true not just for the main .pb.dart file based off the proto
// file, but also for the .pbserver.dart, .pbgrpc.dart files.
if ((ext == '.pb.dart') || protoFileUri != target.protoFileUri) {
importWriter.addImport(url.toString(), prefix: target.fileImportPrefix);
if (ext == '.pb.dart' || protoFileUri != target.protoFileUri) {
importWriter.addImport(import,
prefix: target.importPrefix(context: fileGen));
} else {
importWriter.addImport(url.toString());
importWriter.addImport(import);
}
}

Expand Down
30 changes: 15 additions & 15 deletions protoc_plugin/lib/src/gen/google/protobuf/compiler/plugin.pb.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import 'dart:core' as $core;
import 'package:fixnum/fixnum.dart' as $fixnum;
import 'package:protobuf/protobuf.dart' as $pb;

import '../descriptor.pb.dart' as $2;
import '../descriptor.pb.dart' as $0;

export 'package:protobuf/protobuf.dart' show GeneratedMessageGenericExtensions;

Expand Down Expand Up @@ -121,8 +121,8 @@ class CodeGeneratorRequest extends $pb.GeneratedMessage {
$core.Iterable<$core.String>? fileToGenerate,
$core.String? parameter,
Version? compilerVersion,
$core.Iterable<$2.FileDescriptorProto>? protoFile,
$core.Iterable<$2.FileDescriptorProto>? sourceFileDescriptors,
$core.Iterable<$0.FileDescriptorProto>? protoFile,
$core.Iterable<$0.FileDescriptorProto>? sourceFileDescriptors,
}) {
final result = create();
if (fileToGenerate != null) result.fileToGenerate.addAll(fileToGenerate);
Expand Down Expand Up @@ -152,12 +152,12 @@ class CodeGeneratorRequest extends $pb.GeneratedMessage {
..aOS(2, _omitFieldNames ? '' : 'parameter')
..aOM<Version>(3, _omitFieldNames ? '' : 'compilerVersion',
subBuilder: Version.create)
..pc<$2.FileDescriptorProto>(
..pc<$0.FileDescriptorProto>(
15, _omitFieldNames ? '' : 'protoFile', $pb.PbFieldType.PM,
subBuilder: $2.FileDescriptorProto.create)
..pc<$2.FileDescriptorProto>(
subBuilder: $0.FileDescriptorProto.create)
..pc<$0.FileDescriptorProto>(
17, _omitFieldNames ? '' : 'sourceFileDescriptors', $pb.PbFieldType.PM,
subBuilder: $2.FileDescriptorProto.create);
subBuilder: $0.FileDescriptorProto.create);

@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
CodeGeneratorRequest clone() =>
Expand Down Expand Up @@ -229,13 +229,13 @@ class CodeGeneratorRequest extends $pb.GeneratedMessage {
/// Type names of fields and extensions in the FileDescriptorProto are always
/// fully qualified.
@$pb.TagNumber(15)
$pb.PbList<$2.FileDescriptorProto> get protoFile => $_getList(3);
$pb.PbList<$0.FileDescriptorProto> get protoFile => $_getList(3);

/// File descriptors with all options, including source-retention options.
/// These descriptors are only provided for the files listed in
/// files_to_generate.
@$pb.TagNumber(17)
$pb.PbList<$2.FileDescriptorProto> get sourceFileDescriptors => $_getList(4);
$pb.PbList<$0.FileDescriptorProto> get sourceFileDescriptors => $_getList(4);
}

/// Represents a single generated file.
Expand All @@ -244,7 +244,7 @@ class CodeGeneratorResponse_File extends $pb.GeneratedMessage {
$core.String? name,
$core.String? insertionPoint,
$core.String? content,
$2.GeneratedCodeInfo? generatedCodeInfo,
$0.GeneratedCodeInfo? generatedCodeInfo,
}) {
final result = create();
if (name != null) result.name = name;
Expand All @@ -271,8 +271,8 @@ class CodeGeneratorResponse_File extends $pb.GeneratedMessage {
..aOS(1, _omitFieldNames ? '' : 'name')
..aOS(2, _omitFieldNames ? '' : 'insertionPoint')
..aOS(15, _omitFieldNames ? '' : 'content')
..aOM<$2.GeneratedCodeInfo>(16, _omitFieldNames ? '' : 'generatedCodeInfo',
subBuilder: $2.GeneratedCodeInfo.create)
..aOM<$0.GeneratedCodeInfo>(16, _omitFieldNames ? '' : 'generatedCodeInfo',
subBuilder: $0.GeneratedCodeInfo.create)
..hasRequiredFields = false;

@$core.Deprecated('See https://github.com/google/protobuf.dart/issues/998.')
Expand Down Expand Up @@ -379,15 +379,15 @@ class CodeGeneratorResponse_File extends $pb.GeneratedMessage {
/// point is used, this information will be appropriately offset and inserted
/// into the code generation metadata for the generated files.
@$pb.TagNumber(16)
$2.GeneratedCodeInfo get generatedCodeInfo => $_getN(3);
$0.GeneratedCodeInfo get generatedCodeInfo => $_getN(3);
@$pb.TagNumber(16)
set generatedCodeInfo($2.GeneratedCodeInfo value) => $_setField(16, value);
set generatedCodeInfo($0.GeneratedCodeInfo value) => $_setField(16, value);
@$pb.TagNumber(16)
$core.bool hasGeneratedCodeInfo() => $_has(3);
@$pb.TagNumber(16)
void clearGeneratedCodeInfo() => $_clearField(16);
@$pb.TagNumber(16)
$2.GeneratedCodeInfo ensureGeneratedCodeInfo() => $_ensure(3);
$0.GeneratedCodeInfo ensureGeneratedCodeInfo() => $_ensure(3);
}

/// The plugin writes an encoded CodeGeneratorResponse to stdout.
Expand Down
13 changes: 7 additions & 6 deletions protoc_plugin/lib/src/grpc_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -97,14 +97,15 @@ class GrpcServiceGenerator {
/// Returns the Dart class name to use for a message type.
///
/// Throws an exception if it can't be resolved.
String _getDartClassName(String fqname) {
final mg = _deps[fqname];
if (mg == null) {
final location = _undefinedDeps[fqname];
String _getDartClassName(String fqName) {
final generator = _deps[fqName];
if (generator == null) {
final location = _undefinedDeps[fqName];
// TODO(nichite): Throw more actionable error.
throw 'FAILURE: Unknown type reference ($fqname) for $location';
throw 'FAILURE: Unknown type reference ($fqName) for $location';
}
return '${mg.fileImportPrefix}.${mg.classname}';

return '${generator.importPrefix(context: fileGen)}.${generator.classname}';
}

void generate(IndentingWriter out) {
Expand Down
9 changes: 5 additions & 4 deletions protoc_plugin/lib/src/message_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -160,13 +160,14 @@ class MessageGenerator extends ProtobufContainer {
}

/// Returns a const expression that evaluates to the JSON for this message.
/// [usage] represents the .pb.dart file where the expression will be used.
String getJsonConstant(FileGenerator usage) {
///
/// [context] represents the .pb.dart file where the expression will be used.
String getJsonConstant(FileGenerator context) {
final name = '$classname\$json';
if (usage.protoFileUri == fileGen.protoFileUri) {
if (context.protoFileUri == fileGen.protoFileUri) {
return name;
}
return '$fileImportPrefix.$name';
return '${importPrefix(context: context)}.$name';
}

/// Adds all mixins used in this message and any submessages.
Expand Down
18 changes: 10 additions & 8 deletions protoc_plugin/lib/src/service_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,18 @@ class ServiceGenerator {
///
/// The key is the fully qualified name with a leading '.'.
/// Populated by [resolve].
final _deps = <String, MessageGenerator>{};
final Map<String, MessageGenerator> _deps = {};

/// The message types needed transitively by this service.
///
/// The key is the fully qualified name with a leading '.'.
/// Populated by [resolve].
final _transitiveDeps = <String, MessageGenerator>{};
final Map<String, MessageGenerator> _transitiveDeps = {};

/// Maps each undefined type to a string describing its location.
///
/// Populated by [resolve].
final _undefinedDeps = <String, String>{};
final Map<String, String> _undefinedDeps = {};

final String classname;

Expand Down Expand Up @@ -118,16 +118,18 @@ class ServiceGenerator {
/// should be prefixed unless the target file is the main file (the client
/// generator calls this method). Otherwise, prefix everything.
String _getDartClassName(String fqname, {bool forMainFile = false}) {
final mg = _deps[fqname];
if (mg == null) {
final generator = _deps[fqname];
if (generator == null) {
final location = _undefinedDeps[fqname];
throw 'FAILURE: Unknown type reference ($fqname) for $location';
}
if (forMainFile && fileGen.protoFileUri == mg.fileGen.protoFileUri) {

if (forMainFile && fileGen.protoFileUri == generator.fileGen.protoFileUri) {
// If it's the same file, we import it without using "as".
return mg.classname;
return generator.classname;
}
return '${mg.fileImportPrefix}.${mg.classname}';

return '${generator.importPrefix(context: fileGen)}.${generator.classname}';
}

List<MethodDescriptorProto> get _methodDescriptors => _descriptor.method;
Expand Down
2 changes: 1 addition & 1 deletion protoc_plugin/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: protoc_plugin
version: 22.3.0
version: 22.4.0-wip
description: A protobuf protoc compiler plugin used to generate Dart code.
repository: https://github.com/google/protobuf.dart/tree/master/protoc_plugin

Expand Down
20 changes: 10 additions & 10 deletions protoc_plugin/test/goldens/imports.pb
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import 'dart:core' as $core;

import 'package:protobuf/protobuf.dart' as $pb;

import 'package1.pb.dart' as $1;
import 'package2.pb.dart' as $2;
import 'package1.pb.dart' as $0;
import 'package2.pb.dart' as $1;

export 'package:protobuf/protobuf.dart' show GeneratedMessageGenericExtensions;

Expand All @@ -29,8 +29,8 @@ class M extends $pb.GeneratedMessage {

static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'M', createEmptyInstance: create)
..aOM<M>(1, _omitFieldNames ? '' : 'm', subBuilder: M.create)
..aOM<$1.M>(2, _omitFieldNames ? '' : 'm1', subBuilder: $1.M.create)
..aOM<$2.M>(3, _omitFieldNames ? '' : 'm2', subBuilder: $2.M.create)
..aOM<$0.M>(2, _omitFieldNames ? '' : 'm1', subBuilder: $0.M.create)
..aOM<$1.M>(3, _omitFieldNames ? '' : 'm2', subBuilder: $1.M.create)
..hasRequiredFields = false
;

Expand Down Expand Up @@ -63,26 +63,26 @@ class M extends $pb.GeneratedMessage {
M ensureM() => $_ensure(0);

@$pb.TagNumber(2)
$1.M get m1 => $_getN(1);
$0.M get m1 => $_getN(1);
@$pb.TagNumber(2)
set m1($1.M value) => $_setField(2, value);
set m1($0.M value) => $_setField(2, value);
@$pb.TagNumber(2)
$core.bool hasM1() => $_has(1);
@$pb.TagNumber(2)
void clearM1() => $_clearField(2);
@$pb.TagNumber(2)
$1.M ensureM1() => $_ensure(1);
$0.M ensureM1() => $_ensure(1);

@$pb.TagNumber(3)
$2.M get m2 => $_getN(2);
$1.M get m2 => $_getN(2);
@$pb.TagNumber(3)
set m2($2.M value) => $_setField(3, value);
set m2($1.M value) => $_setField(3, value);
@$pb.TagNumber(3)
$core.bool hasM2() => $_has(2);
@$pb.TagNumber(3)
void clearM2() => $_clearField(3);
@$pb.TagNumber(3)
$2.M ensureM2() => $_ensure(2);
$1.M ensureM2() => $_ensure(2);
}


Expand Down