Linter rulesProtobufpackage-no-import-cycle

package-no-import-cycle

Since 0.9.0

This rule checks whether packages import each other in a cycle.

Rule Details

Even when no file imports itself through a chain of imports, two packages can depend on each other: a file of package A imports a file of package B, and another file of package B imports a file of package A. Code generators that turn Protobuf packages into packages of the target language, like the Go one, produce code with an import cycle that does not compile.

The package-no-import-cycle rule builds the graph of the packages: package A imports package B when any file of A imports a file of B. For every import of the linted file that refers to a file of another package, it looks for a path in this graph from that package back to the package of the linted file. If there is one, it reports:

Import '<import>' creates a package import cycle: <package> -> ... -> <package>

The cycle starts and ends with the package of the linted file. Each imported package is reported once per file, at the first import that leads to it.

The graph contains the files the linter knows about: the files being linted, the other .proto files in their directories, and the files these import, directly or indirectly. Imports that cannot be found are ignored. Files without a package are not checked, and are not part of the graph.

Configuration

This rule has no options.

Examples

Good. acme/pets/v1/pet.proto imports a file of acme.owners.v1, which does not import anything from acme.pets.v1.

syntax = "proto3";

package acme.pets.v1;

import "acme/owners/v1/owner.proto";

message Pet {
  string name = 1;
  acme.owners.v1.Owner owner = 2;
}

Bad. The same acme/pets/v1/pet.proto, while acme/owners/v1/owner.proto imports another file of acme.pets.v1:

syntax = "proto3";

package acme.owners.v1;

import "acme/pets/v1/kind.proto";

message Owner {
  string name = 1;
  repeated acme.pets.v1.Kind preferred_kinds = 2;
}

No file imports itself, but when pet.proto is linted, the linter reports:

Import 'acme/owners/v1/owner.proto' creates a package import cycle: acme.pets.v1 -> acme.owners.v1 -> acme.pets.v1

When Not to Use It

We recommend to always enable this rule. To accept an import that is part of a known cycle, add an // apigenie-lint-disable package-no-import-cycle comment to the import statement.

Compatibility

This rule is compatible with proto3 files. proto2 and editions files are rejected by the reader and are not linted. The rule reads the imported files, found in the import paths and in the directories above the importing file, and the other files in the directories of the linted files.

Related rules