From 65e33d1a958dcde5f5983d181cefe19bef3043c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oscar=20S=C3=B6derlund?= Date: Fri, 14 Nov 2025 07:37:33 +0100 Subject: [PATCH] feat(191): allow proto3 and newer editions This should allow for yet unreleased editions (> 2024) without needing any bumps to the linter. Despite proto3 being part of the editions enum, the IsProto3() still needs to be checked to reliably detect proto3 syntax. The practical result of this rule should be that anything newer than proto2 is now allowed. --- rules/aep0191/proto_version.go | 9 +++++---- rules/aep0191/proto_version_test.go | 12 +++++++----- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/rules/aep0191/proto_version.go b/rules/aep0191/proto_version.go index 9e1b0156..39b69e18 100644 --- a/rules/aep0191/proto_version.go +++ b/rules/aep0191/proto_version.go @@ -18,17 +18,18 @@ import ( "github.com/aep-dev/api-linter/lint" "github.com/aep-dev/api-linter/locations" "github.com/jhump/protoreflect/desc" + "google.golang.org/protobuf/types/descriptorpb" ) -// APIs must use proto3. +// APIs must use proto3 or newer edition. var syntax = &lint.FileRule{ Name: lint.NewRuleName(191, "proto-version"), RuleType: lint.NewRuleType(lint.MustRule), LintFile: func(f *desc.FileDescriptor) []lint.Problem { - if !f.IsProto3() { + if !f.IsProto3() && f.Edition() < descriptorpb.Edition_EDITION_PROTO3 { return []lint.Problem{{ - Message: "All API proto files must use proto3 syntax.", - Suggestion: "syntax = \"proto3\";", + Message: "All API proto files must use proto3 or newer edition.", + Suggestion: "edition = \"2023\";", Descriptor: f, Location: locations.FileSyntax(f), }} diff --git a/rules/aep0191/proto_version_test.go b/rules/aep0191/proto_version_test.go index fcc6ab9d..572c73d7 100644 --- a/rules/aep0191/proto_version_test.go +++ b/rules/aep0191/proto_version_test.go @@ -19,28 +19,30 @@ import ( "github.com/aep-dev/api-linter/rules/internal/testutils" "github.com/jhump/protoreflect/desc/builder" + "google.golang.org/protobuf/types/descriptorpb" ) func TestSyntax(t *testing.T) { // Set up the two permutations. tests := []struct { testName string - isProto3 bool + edition descriptorpb.Edition problems testutils.Problems }{ - {"Valid", true, testutils.Problems{}}, - {"Invalid", false, testutils.Problems{{Suggestion: `syntax = "proto3";`}}}, + {"Valid (proto3)", descriptorpb.Edition_EDITION_PROTO3, testutils.Problems{}}, + {"Valid (2023)", descriptorpb.Edition_EDITION_2023, testutils.Problems{}}, + {"Valid (2024)", descriptorpb.Edition_EDITION_2024, testutils.Problems{}}, + {"Invalid (proto2)", descriptorpb.Edition_EDITION_PROTO2, testutils.Problems{{Suggestion: `edition = "2023";`}}}, } // Run each permutation as an individual test. for _, test := range tests { t.Run(test.testName, func(t *testing.T) { // Build an appropriate file descriptor. - f, err := builder.NewFile("library.proto").SetProto3(test.isProto3).Build() + f, err := builder.NewFile("library.proto").SetEdition(test.edition).Build() if err != nil { t.Fatalf("Could not build file descriptor.") } - // Lint the file, and ensure we got the expected problems. if diff := test.problems.SetDescriptor(f).Diff(syntax.Lint(f)); diff != "" { t.Error(diff)