diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0856366ee5..12e397512d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -33,6 +33,13 @@
2. `FIELD_SAME_UTF8_VALIDATION` catches changes to the `utf8_validation`
feature, which controls validation of string values.
3. `ENUM_SAME_TYPE` catches changes to an enum's type, open vs. closed.
+- Adds support for extensions to `buf breaking`. All existing rules for
+ fields are now applied to extensions, except for `FIELD_NO_DELETE` (and its
+ variants). There are also new `EXTENSION_NO_DELETE` and
+ `PACKAGE_EXTENSION_NO_DELETE` rules for catching deletions of an extension.
+ The new rules are not active by default in existing v1 and v1beta1
+ configurations, for backwards-compatibility reasons. Migrate your config to
+ v2 to use them.
- Adds support for top-level extensions to `buf lint`. It previously only
checked extensions that were defined inside of messages.
- Adds a new `FIELD_NOT_REQUIRED` lint rule that prevents use of required
diff --git a/private/buf/cmd/buf/buf_test.go b/private/buf/cmd/buf/buf_test.go
index 5f4c6ec93f..f45b9c435a 100644
--- a/private/buf/cmd/buf/buf_test.go
+++ b/private/buf/cmd/buf/buf_test.go
@@ -438,7 +438,7 @@ func TestFailCheckBreaking2(t *testing.T) {
t,
nil,
bufctl.ExitCodeFileAnnotation,
- filepath.FromSlash(`testdata/protofileref/breaking/a/foo.proto:7:3:Field "2" on message "Foo" changed type from "int32" to "string".`),
+ filepath.FromSlash(`testdata/protofileref/breaking/a/foo.proto:7:3:Field "2" with name "world" on message "Foo" changed type from "int32" to "string".`),
"breaking",
filepath.Join("testdata", "protofileref", "breaking", "a", "foo.proto"),
"--against",
@@ -454,7 +454,7 @@ func TestFailCheckBreaking3(t *testing.T) {
bufctl.ExitCodeFileAnnotation,
filepath.FromSlash(`
:1:1:Previously present file "bar.proto" was deleted.
- testdata/protofileref/breaking/a/foo.proto:7:3:Field "2" on message "Foo" changed type from "int32" to "string".
+ testdata/protofileref/breaking/a/foo.proto:7:3:Field "2" with name "world" on message "Foo" changed type from "int32" to "string".
`),
"breaking",
filepath.Join("testdata", "protofileref", "breaking", "a", "foo.proto"),
@@ -471,7 +471,7 @@ func TestFailCheckBreaking4(t *testing.T) {
bufctl.ExitCodeFileAnnotation,
filepath.FromSlash(`
testdata/protofileref/breaking/a/bar.proto:5:1:Previously present field "2" with name "value" on message "Bar" was deleted.
- testdata/protofileref/breaking/a/foo.proto:7:3:Field "2" on message "Foo" changed type from "int32" to "string".
+ testdata/protofileref/breaking/a/foo.proto:7:3:Field "2" with name "world" on message "Foo" changed type from "int32" to "string".
`),
"breaking",
fmt.Sprintf("%s#include_package_files=true", filepath.Join("testdata", "protofileref", "breaking", "a", "foo.proto")),
@@ -488,7 +488,7 @@ func TestFailCheckBreaking5(t *testing.T) {
bufctl.ExitCodeFileAnnotation,
filepath.FromSlash(`
:1:1:Previously present file "bar.proto" was deleted.
- testdata/protofileref/breaking/a/foo.proto:7:3:Field "2" on message "Foo" changed type from "int32" to "string".
+ testdata/protofileref/breaking/a/foo.proto:7:3:Field "2" with name "world" on message "Foo" changed type from "int32" to "string".
`),
"breaking",
filepath.Join("testdata", "protofileref", "breaking", "a", "foo.proto"),
@@ -854,6 +854,7 @@ func TestCheckLsBreakingRulesV2(t *testing.T) {
expectedStdout := `
ID CATEGORIES PURPOSE
ENUM_NO_DELETE FILE Checks that enums are not deleted from a given file.
+EXTENSION_NO_DELETE FILE Checks that extensions are not deleted from a given file.
FILE_NO_DELETE FILE Checks that files are not deleted.
MESSAGE_NO_DELETE FILE Checks that messages are not deleted from a given file.
SERVICE_NO_DELETE FILE Checks that services are not deleted from a given file.
@@ -905,6 +906,7 @@ RPC_SAME_REQUEST_TYPE FILE, PACKAGE, WIRE_JSON, WIRE
RPC_SAME_RESPONSE_TYPE FILE, PACKAGE, WIRE_JSON, WIRE Checks that rpcs are have the same response type.
RPC_SAME_SERVER_STREAMING FILE, PACKAGE, WIRE_JSON, WIRE Checks that rpcs have the same server streaming value.
PACKAGE_ENUM_NO_DELETE PACKAGE Checks that enums are not deleted from a given package.
+PACKAGE_EXTENSION_NO_DELETE PACKAGE Checks that extensions are not deleted from a given package.
PACKAGE_MESSAGE_NO_DELETE PACKAGE Checks that messages are not deleted from a given package.
PACKAGE_NO_DELETE PACKAGE Checks that packages are not deleted.
PACKAGE_SERVICE_NO_DELETE PACKAGE Checks that services are not deleted from a given package.
@@ -1905,7 +1907,7 @@ func TestBreakingWithPaths(t *testing.T) {
t,
nil,
bufctl.ExitCodeFileAnnotation,
- `a/v3/a.proto:6:3:Field "1" on message "Foo" changed type from "string" to "int32".
+ `a/v3/a.proto:6:3:Field "1" with name "key" on message "Foo" changed type from "string" to "int32".
a/v3/a.proto:7:3:Field "2" with name "Value" on message "Foo" changed option "json_name" from "value" to "Value".
a/v3/a.proto:7:10:Field "2" on message "Foo" changed name from "value" to "Value".`,
"",
diff --git a/private/bufpkg/bufanalysis/bufanalysistesting/bufanalysistesting.go b/private/bufpkg/bufanalysis/bufanalysistesting/bufanalysistesting.go
index 7360cc00f7..8304a55160 100644
--- a/private/bufpkg/bufanalysis/bufanalysistesting/bufanalysistesting.go
+++ b/private/bufpkg/bufanalysis/bufanalysistesting/bufanalysistesting.go
@@ -110,19 +110,33 @@ func AssertFileAnnotationsEqual(
actual = normalizeFileAnnotations(t, actual)
if !assert.Equal(
t,
- slicesext.Map(expected, func(annotation bufanalysis.FileAnnotation) string { return annotation.String() }),
- slicesext.Map(actual, func(annotation bufanalysis.FileAnnotation) string { return annotation.String() }),
+ slicesext.Map(expected, bufanalysis.FileAnnotation.String),
+ slicesext.Map(actual, bufanalysis.FileAnnotation.String),
) {
- t.Log("Expecting:")
+ t.Log("If actuals are correct, change expectations to the following:")
for _, annotation := range actual {
- t.Logf(" bufanalysistesting.NewFileAnnotation(t, %q, %d, %d, %d, %d, %q),",
- annotation.FileInfo().Path(),
- annotation.StartLine(),
- annotation.StartColumn(),
- annotation.EndLine(),
- annotation.EndColumn(),
- annotation.Type(),
- )
+ if annotation.StartLine() == 0 && annotation.StartColumn() == 0 &&
+ annotation.EndLine() == 0 && annotation.EndColumn() == 0 {
+ if annotation.FileInfo().Path() == "" {
+ t.Logf(" bufanalysistesting.NewFileAnnotationNoLocationOrPath(t, %q),",
+ annotation.Type(),
+ )
+ } else {
+ t.Logf(" bufanalysistesting.NewFileAnnotationNoLocation(t, %q, %q),",
+ annotation.FileInfo().Path(),
+ annotation.Type(),
+ )
+ }
+ } else {
+ t.Logf(" bufanalysistesting.NewFileAnnotation(t, %q, %d, %d, %d, %d, %q),",
+ annotation.FileInfo().Path(),
+ annotation.StartLine(),
+ annotation.StartColumn(),
+ annotation.EndLine(),
+ annotation.EndColumn(),
+ annotation.Type(),
+ )
+ }
}
}
}
diff --git a/private/bufpkg/bufcheck/bufbreaking/bufbreaking_test.go b/private/bufpkg/bufcheck/bufbreaking/bufbreaking_test.go
index 779312957f..c5bd64ff20 100644
--- a/private/bufpkg/bufcheck/bufbreaking/bufbreaking_test.go
+++ b/private/bufpkg/bufcheck/bufbreaking/bufbreaking_test.go
@@ -154,6 +154,19 @@ func TestRunBreakingExtensionMessageNoDelete(t *testing.T) {
)
}
+func TestRunBreakingExtensionNoDelete(t *testing.T) {
+ t.Parallel()
+ testBreaking(
+ t,
+ "breaking_extension_no_delete",
+ bufanalysistesting.NewFileAnnotationNoLocation(t, "2.proto", "EXTENSION_NO_DELETE"),
+ bufanalysistesting.NewFileAnnotationNoLocation(t, "2.proto", "EXTENSION_NO_DELETE"),
+ bufanalysistesting.NewFileAnnotationNoLocation(t, "2.proto", "EXTENSION_NO_DELETE"),
+ bufanalysistesting.NewFileAnnotationNoLocation(t, "3.proto", "EXTENSION_NO_DELETE"),
+ bufanalysistesting.NewFileAnnotation(t, "3.proto", 8, 3, 14, 4, "EXTENSION_NO_DELETE"),
+ )
+}
+
func TestRunBreakingFieldNoDelete(t *testing.T) {
t.Parallel()
testBreaking(
@@ -225,6 +238,8 @@ func TestRunBreakingFieldSameCardinality(t *testing.T) {
bufanalysistesting.NewFileAnnotation(t, "2.proto", 14, 3, 14, 24, "FIELD_SAME_CARDINALITY"),
bufanalysistesting.NewFileAnnotation(t, "2.proto", 70, 3, 70, 26, "FIELD_SAME_CARDINALITY"),
bufanalysistesting.NewFileAnnotation(t, "2.proto", 71, 3, 71, 26, "FIELD_SAME_CARDINALITY"),
+ bufanalysistesting.NewFileAnnotation(t, "3.proto", 17, 7, 17, 32, "FIELD_SAME_CARDINALITY"),
+ bufanalysistesting.NewFileAnnotation(t, "3.proto", 25, 3, 25, 20, "FIELD_SAME_CARDINALITY"),
)
}
@@ -268,12 +283,19 @@ func TestRunBreakingFieldSameCppStringType(t *testing.T) {
bufanalysistesting.NewFileAnnotation(t, "2.proto", 31, 34, 31, 52, "FIELD_SAME_CPP_STRING_TYPE"),
bufanalysistesting.NewFileAnnotation(t, "2.proto", 33, 25, 33, 59, "FIELD_SAME_CPP_STRING_TYPE"),
bufanalysistesting.NewFileAnnotation(t, "2.proto", 34, 24, 34, 58, "FIELD_SAME_CPP_STRING_TYPE"),
- bufanalysistesting.NewFileAnnotation(t, "3.proto", 10, 24, 10, 36, "FIELD_SAME_CPP_STRING_TYPE"),
- bufanalysistesting.NewFileAnnotation(t, "3.proto", 11, 23, 11, 33, "FIELD_SAME_CPP_STRING_TYPE"),
- bufanalysistesting.NewFileAnnotation(t, "3.proto", 12, 33, 12, 51, "FIELD_SAME_CPP_STRING_TYPE"),
- bufanalysistesting.NewFileAnnotation(t, "3.proto", 13, 32, 13, 68, "FIELD_SAME_CPP_STRING_TYPE"),
- bufanalysistesting.NewFileAnnotation(t, "3.proto", 15, 23, 15, 57, "FIELD_SAME_CPP_STRING_TYPE"),
- bufanalysistesting.NewFileAnnotation(t, "3.proto", 16, 3, 16, 32, "FIELD_SAME_CPP_STRING_TYPE"),
+ bufanalysistesting.NewFileAnnotation(t, "2.proto", 43, 7, 43, 37, "FIELD_SAME_CPP_STRING_TYPE"),
+ bufanalysistesting.NewFileAnnotation(t, "2.proto", 44, 29, 44, 63, "FIELD_SAME_CPP_STRING_TYPE"),
+ bufanalysistesting.NewFileAnnotation(t, "3.proto", 11, 24, 11, 36, "FIELD_SAME_CPP_STRING_TYPE"),
+ bufanalysistesting.NewFileAnnotation(t, "3.proto", 12, 23, 12, 33, "FIELD_SAME_CPP_STRING_TYPE"),
+ bufanalysistesting.NewFileAnnotation(t, "3.proto", 13, 33, 13, 51, "FIELD_SAME_CPP_STRING_TYPE"),
+ bufanalysistesting.NewFileAnnotation(t, "3.proto", 14, 32, 14, 68, "FIELD_SAME_CPP_STRING_TYPE"),
+ bufanalysistesting.NewFileAnnotation(t, "3.proto", 16, 23, 16, 57, "FIELD_SAME_CPP_STRING_TYPE"),
+ bufanalysistesting.NewFileAnnotation(t, "3.proto", 17, 3, 17, 32, "FIELD_SAME_CPP_STRING_TYPE"),
+ bufanalysistesting.NewFileAnnotation(t, "3.proto", 27, 39, 27, 73, "FIELD_SAME_CPP_STRING_TYPE"),
+ bufanalysistesting.NewFileAnnotation(t, "3.proto", 28, 7, 28, 29, "FIELD_SAME_CPP_STRING_TYPE"),
+ bufanalysistesting.NewFileAnnotation(t, "3.proto", 30, 29, 30, 41, "FIELD_SAME_CPP_STRING_TYPE"),
+ bufanalysistesting.NewFileAnnotation(t, "3.proto", 36, 3, 36, 33, "FIELD_SAME_CPP_STRING_TYPE"),
+ bufanalysistesting.NewFileAnnotation(t, "3.proto", 40, 3, 40, 34, "FIELD_SAME_CPP_STRING_TYPE"),
)
}
@@ -289,6 +311,7 @@ func TestRunBreakingFieldSameCType(t *testing.T) {
bufanalysistesting.NewFileAnnotation(t, "1.proto", 23, 21, 23, 33, "FIELD_SAME_CPP_STRING_TYPE"),
bufanalysistesting.NewFileAnnotation(t, "2.proto", 49, 28, 49, 48, "FIELD_SAME_CPP_STRING_TYPE"),
bufanalysistesting.NewFileAnnotation(t, "2.proto", 50, 28, 50, 42, "FIELD_SAME_CPP_STRING_TYPE"),
+ bufanalysistesting.NewFileAnnotation(t, "2.proto", 58, 32, 58, 36, "FIELD_SAME_CPP_STRING_TYPE"),
)
}
@@ -297,26 +320,26 @@ func TestRunBreakingFieldSameJavaUTF8Validation(t *testing.T) {
testBreaking(
t,
"breaking_field_same_java_utf8_validation",
- bufanalysistesting.NewFileAnnotation(t, "1.proto", 17, 3, 17, 26, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
- bufanalysistesting.NewFileAnnotation(t, "1.proto", 18, 3, 18, 26, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
- bufanalysistesting.NewFileAnnotation(t, "1.proto", 19, 3, 19, 22, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
- bufanalysistesting.NewFileAnnotation(t, "1.proto", 19, 3, 19, 22, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
- bufanalysistesting.NewFileAnnotation(t, "1.proto", 28, 3, 28, 26, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
- bufanalysistesting.NewFileAnnotation(t, "1.proto", 29, 3, 29, 26, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
- bufanalysistesting.NewFileAnnotation(t, "1.proto", 30, 3, 30, 22, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
- bufanalysistesting.NewFileAnnotation(t, "1.proto", 30, 3, 30, 22, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
- bufanalysistesting.NewFileAnnotation(t, "1.proto", 39, 3, 39, 26, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
- bufanalysistesting.NewFileAnnotation(t, "1.proto", 40, 3, 40, 26, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
- bufanalysistesting.NewFileAnnotation(t, "1.proto", 41, 3, 41, 22, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
- bufanalysistesting.NewFileAnnotation(t, "1.proto", 41, 3, 41, 22, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
- bufanalysistesting.NewFileAnnotation(t, "1.proto", 50, 3, 50, 26, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
- bufanalysistesting.NewFileAnnotation(t, "1.proto", 51, 3, 51, 26, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
- bufanalysistesting.NewFileAnnotation(t, "1.proto", 52, 3, 52, 22, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
- bufanalysistesting.NewFileAnnotation(t, "1.proto", 52, 3, 52, 22, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
- bufanalysistesting.NewFileAnnotation(t, "1.proto", 61, 3, 61, 26, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
- bufanalysistesting.NewFileAnnotation(t, "1.proto", 62, 3, 62, 26, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
- bufanalysistesting.NewFileAnnotation(t, "1.proto", 63, 3, 63, 22, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
- bufanalysistesting.NewFileAnnotation(t, "1.proto", 63, 3, 63, 22, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
+ bufanalysistesting.NewFileAnnotation(t, "1.proto", 19, 3, 19, 26, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
+ bufanalysistesting.NewFileAnnotation(t, "1.proto", 20, 3, 20, 26, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
+ bufanalysistesting.NewFileAnnotation(t, "1.proto", 21, 3, 21, 22, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
+ bufanalysistesting.NewFileAnnotation(t, "1.proto", 21, 3, 21, 22, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
+ bufanalysistesting.NewFileAnnotation(t, "1.proto", 30, 3, 30, 26, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
+ bufanalysistesting.NewFileAnnotation(t, "1.proto", 31, 3, 31, 26, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
+ bufanalysistesting.NewFileAnnotation(t, "1.proto", 32, 3, 32, 22, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
+ bufanalysistesting.NewFileAnnotation(t, "1.proto", 32, 3, 32, 22, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
+ bufanalysistesting.NewFileAnnotation(t, "1.proto", 42, 3, 42, 26, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
+ bufanalysistesting.NewFileAnnotation(t, "1.proto", 43, 3, 43, 26, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
+ bufanalysistesting.NewFileAnnotation(t, "1.proto", 44, 3, 44, 22, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
+ bufanalysistesting.NewFileAnnotation(t, "1.proto", 44, 3, 44, 22, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
+ bufanalysistesting.NewFileAnnotation(t, "1.proto", 54, 3, 54, 26, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
+ bufanalysistesting.NewFileAnnotation(t, "1.proto", 55, 3, 55, 26, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
+ bufanalysistesting.NewFileAnnotation(t, "1.proto", 56, 3, 56, 22, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
+ bufanalysistesting.NewFileAnnotation(t, "1.proto", 56, 3, 56, 22, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
+ bufanalysistesting.NewFileAnnotation(t, "1.proto", 66, 3, 66, 26, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
+ bufanalysistesting.NewFileAnnotation(t, "1.proto", 67, 3, 67, 26, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
+ bufanalysistesting.NewFileAnnotation(t, "1.proto", 68, 3, 68, 22, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
+ bufanalysistesting.NewFileAnnotation(t, "1.proto", 68, 3, 68, 22, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
bufanalysistesting.NewFileAnnotation(t, "2.proto", 5, 1, 5, 38, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
bufanalysistesting.NewFileAnnotation(t, "2.proto", 5, 1, 5, 38, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
bufanalysistesting.NewFileAnnotation(t, "2.proto", 5, 1, 5, 38, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
@@ -343,10 +366,16 @@ func TestRunBreakingFieldSameJavaUTF8Validation(t *testing.T) {
bufanalysistesting.NewFileAnnotation(t, "6.proto", 12, 3, 12, 26, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
bufanalysistesting.NewFileAnnotation(t, "6.proto", 13, 3, 13, 22, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
bufanalysistesting.NewFileAnnotation(t, "6.proto", 13, 3, 13, 22, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
- bufanalysistesting.NewFileAnnotation(t, "6.proto", 79, 3, 79, 22, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
- bufanalysistesting.NewFileAnnotation(t, "6.proto", 79, 3, 79, 22, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
- bufanalysistesting.NewFileAnnotation(t, "6.proto", 90, 3, 90, 22, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
- bufanalysistesting.NewFileAnnotation(t, "6.proto", 90, 3, 90, 22, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
+ bufanalysistesting.NewFileAnnotation(t, "6.proto", 23, 5, 23, 24, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
+ bufanalysistesting.NewFileAnnotation(t, "6.proto", 24, 5, 24, 33, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
+ bufanalysistesting.NewFileAnnotation(t, "6.proto", 106, 5, 106, 24, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
+ bufanalysistesting.NewFileAnnotation(t, "6.proto", 114, 3, 114, 22, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
+ bufanalysistesting.NewFileAnnotation(t, "6.proto", 114, 3, 114, 22, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
+ bufanalysistesting.NewFileAnnotation(t, "6.proto", 125, 3, 125, 22, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
+ bufanalysistesting.NewFileAnnotation(t, "6.proto", 125, 3, 125, 22, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
+ bufanalysistesting.NewFileAnnotation(t, "6.proto", 134, 3, 134, 22, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
+ bufanalysistesting.NewFileAnnotation(t, "6.proto", 135, 3, 135, 31, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
+ bufanalysistesting.NewFileAnnotation(t, "6.proto", 154, 3, 154, 22, "FIELD_SAME_JAVA_UTF8_VALIDATION"),
)
}
@@ -387,6 +416,11 @@ func TestRunBreakingFieldSameDefault(t *testing.T) {
bufanalysistesting.NewFileAnnotation(t, "1.proto", 104, 27, 104, 42, "FIELD_SAME_DEFAULT"),
bufanalysistesting.NewFileAnnotation(t, "1.proto", 109, 5, 109, 26, "FIELD_SAME_DEFAULT"),
bufanalysistesting.NewFileAnnotation(t, "1.proto", 111, 27, 111, 40, "FIELD_SAME_DEFAULT"),
+ bufanalysistesting.NewFileAnnotation(t, "1.proto", 140, 30, 140, 45, "FIELD_SAME_DEFAULT"),
+ bufanalysistesting.NewFileAnnotation(t, "1.proto", 141, 5, 141, 27, "FIELD_SAME_DEFAULT"),
+ bufanalysistesting.NewFileAnnotation(t, "1.proto", 148, 29, 148, 42, "FIELD_SAME_DEFAULT"),
+ bufanalysistesting.NewFileAnnotation(t, "1.proto", 150, 28, 150, 43, "FIELD_SAME_DEFAULT"),
+ bufanalysistesting.NewFileAnnotation(t, "1.proto", 153, 29, 153, 42, "FIELD_SAME_DEFAULT"),
bufanalysistesting.NewFileAnnotation(t, "2.proto", 6, 18, 6, 33, "FIELD_SAME_DEFAULT"),
bufanalysistesting.NewFileAnnotation(t, "2.proto", 8, 17, 8, 30, "FIELD_SAME_DEFAULT"),
bufanalysistesting.NewFileAnnotation(t, "2.proto", 10, 17, 10, 32, "FIELD_SAME_DEFAULT"),
@@ -468,8 +502,14 @@ func TestRunBreakingFieldSameJSType(t *testing.T) {
bufanalysistesting.NewFileAnnotation(t, "1.proto", 13, 22, 13, 40, "FIELD_SAME_JSTYPE"),
bufanalysistesting.NewFileAnnotation(t, "1.proto", 14, 7, 14, 21, "FIELD_SAME_JSTYPE"),
bufanalysistesting.NewFileAnnotation(t, "1.proto", 22, 20, 22, 38, "FIELD_SAME_JSTYPE"),
- bufanalysistesting.NewFileAnnotation(t, "2.proto", 49, 27, 49, 45, "FIELD_SAME_JSTYPE"),
- bufanalysistesting.NewFileAnnotation(t, "2.proto", 50, 3, 50, 26, "FIELD_SAME_JSTYPE"),
+ bufanalysistesting.NewFileAnnotation(t, "2.proto", 51, 27, 51, 45, "FIELD_SAME_JSTYPE"),
+ bufanalysistesting.NewFileAnnotation(t, "2.proto", 52, 3, 52, 26, "FIELD_SAME_JSTYPE"),
+ bufanalysistesting.NewFileAnnotation(t, "2.proto", 57, 32, 57, 50, "FIELD_SAME_JSTYPE"),
+ bufanalysistesting.NewFileAnnotation(t, "2.proto", 58, 34, 58, 52, "FIELD_SAME_JSTYPE"),
+ bufanalysistesting.NewFileAnnotation(t, "2.proto", 59, 5, 59, 34, "FIELD_SAME_JSTYPE"),
+ bufanalysistesting.NewFileAnnotation(t, "2.proto", 68, 29, 68, 47, "FIELD_SAME_JSTYPE"),
+ bufanalysistesting.NewFileAnnotation(t, "2.proto", 69, 29, 69, 47, "FIELD_SAME_JSTYPE"),
+ bufanalysistesting.NewFileAnnotation(t, "2.proto", 70, 3, 70, 28, "FIELD_SAME_JSTYPE"),
)
}
@@ -559,7 +599,12 @@ func TestRunBreakingFieldSameName(t *testing.T) {
bufanalysistesting.NewFileAnnotation(t, "1.proto", 7, 9, 7, 13, "FIELD_SAME_NAME"),
bufanalysistesting.NewFileAnnotation(t, "1.proto", 15, 13, 15, 17, "FIELD_SAME_NAME"),
bufanalysistesting.NewFileAnnotation(t, "1.proto", 26, 11, 26, 15, "FIELD_SAME_NAME"),
- bufanalysistesting.NewFileAnnotation(t, "2.proto", 60, 11, 60, 15, "FIELD_SAME_NAME"),
+ bufanalysistesting.NewFileAnnotation(t, "1.proto", 35, 14, 35, 25, "FIELD_SAME_NAME"),
+ bufanalysistesting.NewFileAnnotation(t, "2.proto", 48, 23, 48, 33, "FIELD_SAME_NAME"),
+ bufanalysistesting.NewFileAnnotation(t, "2.proto", 64, 19, 64, 27, "FIELD_SAME_NAME"),
+ bufanalysistesting.NewFileAnnotation(t, "2.proto", 72, 19, 72, 29, "FIELD_SAME_NAME"),
+ bufanalysistesting.NewFileAnnotation(t, "2.proto", 78, 11, 78, 15, "FIELD_SAME_NAME"),
+ bufanalysistesting.NewFileAnnotation(t, "2.proto", 82, 19, 82, 27, "FIELD_SAME_NAME"),
)
}
@@ -605,8 +650,13 @@ func TestRunBreakingFieldSameType(t *testing.T) {
bufanalysistesting.NewFileAnnotation(t, "1.proto", 41, 5, 41, 20, "FIELD_SAME_TYPE"),
bufanalysistesting.NewFileAnnotation(t, "2.proto", 57, 5, 57, 10, "FIELD_SAME_TYPE"),
bufanalysistesting.NewFileAnnotation(t, "2.proto", 58, 5, 58, 9, "FIELD_SAME_TYPE"),
+ bufanalysistesting.NewFileAnnotation(t, "2.proto", 68, 12, 68, 16, "FIELD_SAME_TYPE"),
bufanalysistesting.NewFileAnnotation(t, "3.proto", 8, 3, 8, 7, "FIELD_SAME_TYPE"),
bufanalysistesting.NewFileAnnotation(t, "3.proto", 9, 3, 9, 7, "FIELD_SAME_TYPE"),
+ bufanalysistesting.NewFileAnnotation(t, "3.proto", 31, 5, 31, 9, "FIELD_SAME_TYPE"),
+ bufanalysistesting.NewFileAnnotation(t, "3.proto", 33, 5, 33, 10, "FIELD_SAME_TYPE"),
+ bufanalysistesting.NewFileAnnotation(t, "3.proto", 38, 3, 38, 8, "FIELD_SAME_TYPE"),
+ bufanalysistesting.NewFileAnnotation(t, "3.proto", 39, 3, 39, 7, "FIELD_SAME_TYPE"),
)
}
@@ -699,6 +749,8 @@ func TestRunBreakingFieldWireCompatibleCardinality(t *testing.T) {
bufanalysistesting.NewFileAnnotation(t, "2.proto", 14, 3, 14, 24, "FIELD_WIRE_COMPATIBLE_CARDINALITY"),
bufanalysistesting.NewFileAnnotation(t, "2.proto", 70, 3, 70, 26, "FIELD_WIRE_COMPATIBLE_CARDINALITY"),
bufanalysistesting.NewFileAnnotation(t, "2.proto", 71, 3, 71, 26, "FIELD_WIRE_COMPATIBLE_CARDINALITY"),
+ bufanalysistesting.NewFileAnnotation(t, "3.proto", 17, 7, 17, 32, "FIELD_WIRE_COMPATIBLE_CARDINALITY"),
+ bufanalysistesting.NewFileAnnotation(t, "3.proto", 25, 3, 25, 20, "FIELD_WIRE_COMPATIBLE_CARDINALITY"),
)
}
@@ -726,8 +778,11 @@ func TestRunBreakingFieldWireCompatibleType(t *testing.T) {
bufanalysistesting.NewFileAnnotation(t, "2.proto", 79, 3, 79, 6, "FIELD_WIRE_COMPATIBLE_TYPE"),
bufanalysistesting.NewFileAnnotation(t, "2.proto", 80, 3, 80, 6, "FIELD_WIRE_COMPATIBLE_TYPE"),
bufanalysistesting.NewFileAnnotation(t, "2.proto", 85, 3, 85, 9, "FIELD_WIRE_COMPATIBLE_TYPE"),
+ bufanalysistesting.NewFileAnnotation(t, "2.proto", 89, 12, 89, 15, "FIELD_WIRE_COMPATIBLE_TYPE"),
bufanalysistesting.NewFileAnnotation(t, "3.proto", 6, 3, 6, 7, "FIELD_WIRE_COMPATIBLE_TYPE"),
bufanalysistesting.NewFileAnnotation(t, "3.proto", 7, 3, 7, 7, "FIELD_WIRE_COMPATIBLE_TYPE"),
+ bufanalysistesting.NewFileAnnotation(t, "3.proto", 15, 5, 15, 12, "FIELD_WIRE_COMPATIBLE_TYPE"),
+ bufanalysistesting.NewFileAnnotation(t, "3.proto", 23, 3, 23, 9, "FIELD_WIRE_COMPATIBLE_TYPE"),
)
}
@@ -759,6 +814,8 @@ func TestRunBreakingFieldWireJSONCompatibleCardinality(t *testing.T) {
bufanalysistesting.NewFileAnnotation(t, "2.proto", 14, 3, 14, 24, "FIELD_WIRE_JSON_COMPATIBLE_CARDINALITY"),
bufanalysistesting.NewFileAnnotation(t, "2.proto", 70, 3, 70, 26, "FIELD_WIRE_JSON_COMPATIBLE_CARDINALITY"),
bufanalysistesting.NewFileAnnotation(t, "2.proto", 71, 3, 71, 26, "FIELD_WIRE_JSON_COMPATIBLE_CARDINALITY"),
+ bufanalysistesting.NewFileAnnotation(t, "3.proto", 17, 7, 17, 32, "FIELD_WIRE_JSON_COMPATIBLE_CARDINALITY"),
+ bufanalysistesting.NewFileAnnotation(t, "3.proto", 25, 3, 25, 20, "FIELD_WIRE_JSON_COMPATIBLE_CARDINALITY"),
)
}
@@ -787,8 +844,14 @@ func TestRunBreakingFieldWireJSONCompatibleType(t *testing.T) {
bufanalysistesting.NewFileAnnotation(t, "2.proto", 83, 3, 83, 6, "FIELD_WIRE_JSON_COMPATIBLE_TYPE"),
bufanalysistesting.NewFileAnnotation(t, "2.proto", 87, 3, 87, 8, "FIELD_WIRE_JSON_COMPATIBLE_TYPE"),
bufanalysistesting.NewFileAnnotation(t, "2.proto", 88, 3, 88, 9, "FIELD_WIRE_JSON_COMPATIBLE_TYPE"),
+ bufanalysistesting.NewFileAnnotation(t, "2.proto", 92, 12, 92, 15, "FIELD_WIRE_JSON_COMPATIBLE_TYPE"),
+ bufanalysistesting.NewFileAnnotation(t, "2.proto", 93, 3, 93, 8, "FIELD_WIRE_JSON_COMPATIBLE_TYPE"),
bufanalysistesting.NewFileAnnotation(t, "3.proto", 6, 3, 6, 7, "FIELD_WIRE_JSON_COMPATIBLE_TYPE"),
bufanalysistesting.NewFileAnnotation(t, "3.proto", 7, 3, 7, 7, "FIELD_WIRE_JSON_COMPATIBLE_TYPE"),
+ bufanalysistesting.NewFileAnnotation(t, "3.proto", 14, 5, 14, 10, "FIELD_WIRE_JSON_COMPATIBLE_TYPE"),
+ bufanalysistesting.NewFileAnnotation(t, "3.proto", 15, 5, 15, 12, "FIELD_WIRE_JSON_COMPATIBLE_TYPE"),
+ bufanalysistesting.NewFileAnnotation(t, "3.proto", 16, 5, 16, 11, "FIELD_WIRE_JSON_COMPATIBLE_TYPE"),
+ bufanalysistesting.NewFileAnnotation(t, "3.proto", 23, 3, 23, 9, "FIELD_WIRE_JSON_COMPATIBLE_TYPE"),
)
}
@@ -963,6 +1026,17 @@ func TestRunBreakingOneofNoDelete(t *testing.T) {
)
}
+func TestRunBreakingPackageExtensionNoDelete(t *testing.T) {
+ t.Parallel()
+ testBreaking(
+ t,
+ "breaking_package_extension_no_delete",
+ bufanalysistesting.NewFileAnnotationNoLocation(t, "2.proto", "PACKAGE_EXTENSION_NO_DELETE"),
+ bufanalysistesting.NewFileAnnotationNoLocation(t, "3.proto", "PACKAGE_EXTENSION_NO_DELETE"),
+ bufanalysistesting.NewFileAnnotation(t, "3.proto", 8, 3, 14, 4, "PACKAGE_EXTENSION_NO_DELETE"),
+ )
+}
+
func TestRunBreakingPackageNoDelete(t *testing.T) {
t.Parallel()
testBreaking(
@@ -983,6 +1057,15 @@ func TestRunBreakingPackageNoDelete(t *testing.T) {
)
}
+func TestRunBreakingPackageServiceNoDelete(t *testing.T) {
+ t.Parallel()
+ testBreaking(
+ t,
+ "breaking_package_service_no_delete",
+ bufanalysistesting.NewFileAnnotationNoLocation(t, "1.proto", "PACKAGE_SERVICE_NO_DELETE"),
+ )
+}
+
func TestRunBreakingReservedEnumNoDelete(t *testing.T) {
t.Parallel()
testBreaking(
@@ -1089,15 +1172,6 @@ func TestRunBreakingServiceNoDelete(t *testing.T) {
)
}
-func TestRunBreakingPackageServiceNoDelete(t *testing.T) {
- t.Parallel()
- testBreaking(
- t,
- "breaking_package_service_no_delete",
- bufanalysistesting.NewFileAnnotationNoLocation(t, "1.proto", "PACKAGE_SERVICE_NO_DELETE"),
- )
-}
-
func TestRunBreakingIgnoreUnstablePackagesTrue(t *testing.T) {
t.Parallel()
testBreaking(
diff --git a/private/bufpkg/bufcheck/bufbreaking/internal/bufbreakingbuild/bufbreakingbuild.go b/private/bufpkg/bufcheck/bufbreaking/internal/bufbreakingbuild/bufbreakingbuild.go
index cebffd9c3a..9fbae52e70 100644
--- a/private/bufpkg/bufcheck/bufbreaking/internal/bufbreakingbuild/bufbreakingbuild.go
+++ b/private/bufpkg/bufcheck/bufbreaking/internal/bufbreakingbuild/bufbreakingbuild.go
@@ -74,6 +74,12 @@ var (
"extension ranges are not deleted from a given message",
bufbreakingcheck.CheckExtensionMessageNoDelete,
)
+ // ExtensionNoDeleteRuleBuilder is a rule builder.
+ ExtensionNoDeleteRuleBuilder = internal.NewNopRuleBuilder(
+ "EXTENSION_NO_DELETE",
+ "extensions are not deleted from a given file",
+ bufbreakingcheck.CheckExtensionNoDelete,
+ )
// FieldNoDeleteRuleBuilder is a rule builder.
FieldNoDeleteRuleBuilder = internal.NewNopRuleBuilder(
"FIELD_NO_DELETE",
@@ -366,6 +372,12 @@ var (
"enums are not deleted from a given package",
bufbreakingcheck.CheckPackageEnumNoDelete,
)
+ // PackageExtensionNoDeleteRuleBuilder is a rule builder.
+ PackageExtensionNoDeleteRuleBuilder = internal.NewNopRuleBuilder(
+ "PACKAGE_EXTENSION_NO_DELETE",
+ "extensions are not deleted from a given package",
+ bufbreakingcheck.CheckPackageExtensionNoDelete,
+ )
// PackageMessageNoDeleteRuleBuilder is a rule builder.
PackageMessageNoDeleteRuleBuilder = internal.NewNopRuleBuilder(
"PACKAGE_MESSAGE_NO_DELETE",
diff --git a/private/bufpkg/bufcheck/bufbreaking/internal/bufbreakingcheck/bufbreakingcheck.go b/private/bufpkg/bufcheck/bufbreaking/internal/bufbreakingcheck/bufbreakingcheck.go
index 830b0dbc58..ddfdf33a97 100644
--- a/private/bufpkg/bufcheck/bufbreaking/internal/bufbreakingcheck/bufbreakingcheck.go
+++ b/private/bufpkg/bufcheck/bufbreaking/internal/bufbreakingcheck/bufbreakingcheck.go
@@ -56,7 +56,7 @@ func checkEnumNoDelete(add addFunc, corpus *corpus, previousFile bufprotosource.
for previousNestedName := range previousNestedNameToEnum {
if _, ok := nestedNameToEnum[previousNestedName]; !ok {
// TODO: search for enum in other files and return that the enum was moved?
- descriptor, location, err := getDescriptorAndLocationForDeletedEnum(file, previousNestedName)
+ descriptor, location, err := getDescriptorAndLocationForDeletedElement(file, previousNestedName)
if err != nil {
return err
}
@@ -242,6 +242,30 @@ func checkExtensionMessageNoDelete(add addFunc, corpus *corpus, previousMessage
return checkTagRanges(add, "extension", message, previousMessage.ExtensionRanges(), message.ExtensionRanges())
}
+// CheckExtensionNoDelete is a check function.
+var CheckExtensionNoDelete = newFilePairCheckFunc(checkExtensionNoDelete)
+
+func checkExtensionNoDelete(add addFunc, corpus *corpus, previousFile bufprotosource.File, file bufprotosource.File) error {
+ previousNestedNameToExtension, err := bufprotosource.NestedNameToExtension(previousFile)
+ if err != nil {
+ return err
+ }
+ nestedNameToExtension, err := bufprotosource.NestedNameToExtension(file)
+ if err != nil {
+ return err
+ }
+ for previousNestedName := range previousNestedNameToExtension {
+ if _, ok := nestedNameToExtension[previousNestedName]; !ok {
+ descriptor, location, err := getDescriptorAndLocationForDeletedElement(file, previousNestedName)
+ if err != nil {
+ return err
+ }
+ add(descriptor, nil, location, `Previously present extension %q was deleted from file.`, previousNestedName)
+ }
+ }
+ return nil
+}
+
// CheckFieldNoDelete is a check function.
var CheckFieldNoDelete = newMessagePairCheckFunc(checkFieldNoDelete)
@@ -275,8 +299,6 @@ func checkFieldNoDeleteWithRules(add addFunc, previousMessage bufprotosource.Mes
for previousNumber, previousField := range previousNumberToField {
if _, ok := numberToField[previousNumber]; !ok {
if !isDeletedFieldAllowedWithRules(previousField, message, allowIfNumberReserved, allowIfNameReserved) {
- // otherwise prints as hex
- previousNumberString := strconv.FormatInt(int64(previousNumber), 10)
suffix := ""
if allowIfNumberReserved && allowIfNameReserved {
return errors.New("both allowIfNumberReserved and allowIfNameReserved set")
@@ -287,7 +309,17 @@ func checkFieldNoDeleteWithRules(add addFunc, previousMessage bufprotosource.Mes
if allowIfNameReserved {
suffix = fmt.Sprintf(` without reserving the name %q`, previousField.Name())
}
- add(message, nil, message.Location(), `Previously present field %q with name %q on message %q was deleted%s.`, previousNumberString, previousField.Name(), message.Name(), suffix)
+ description := fieldDescription(previousField)
+ // Description will start with capital letter; lower-case it
+ // to better fit in this message.
+ description = strings.ToLower(description[:1]) + description[1:]
+ add(
+ message,
+ nil,
+ message.Location(),
+ `Previously present %s was deleted%s.`,
+ description,
+ suffix)
}
}
}
@@ -323,11 +355,9 @@ func checkFieldSameCardinality(
previousCardinality := getCardinality(previousDescriptor)
currentCardinality := getCardinality(descriptor)
if previousCardinality != currentCardinality {
- // otherwise prints as hex
- numberString := strconv.FormatInt(int64(field.Number()), 10)
add(field, nil, field.Location(),
- `Field %q on message %q changed cardinality from %q to %q.`,
- numberString, field.ParentMessage().Name(),
+ `%s changed cardinality from %q to %q.`,
+ fieldDescription(field),
previousCardinality,
currentCardinality,
)
@@ -367,8 +397,6 @@ func checkFieldSameCppStringType(
if (previousStringType != stringType || previousIsStringPiece != isStringPiece) &&
// it is NOT breaking to move from string_piece -> string
!(previousIsStringPiece && stringType == protobuf.CppFeatures_STRING) {
- // otherwise prints as hex
- numberString := strconv.FormatInt(int64(field.Number()), 10)
var previousType, currentType fmt.Stringer
if previousIsStringPiece {
previousType = descriptorpb.FieldOptions_STRING_PIECE
@@ -384,10 +412,8 @@ func checkFieldSameCppStringType(
field,
nil,
withBackupLocation(field.CTypeLocation(), fieldCppStringTypeLocation(field), field.Location()),
- `Field %q with name %q on message %q changed C++ string type from %q to %q.`,
- numberString,
- field.Name(),
- field.ParentMessage().Name(),
+ `%s changed C++ string type from %q to %q.`,
+ fieldDescription(field),
previousType,
currentType,
)
@@ -419,16 +445,12 @@ func checkFieldSameJavaUTF8Validation(
return err
}
if previousValidation != validation {
- // otherwise prints as hex
- numberString := strconv.FormatInt(int64(field.Number()), 10)
add(
field,
nil,
withBackupLocation(field.File().JavaStringCheckUtf8Location(), fieldJavaUTF8ValidationLocation(field), field.Location()),
- `Field %q with name %q on message %q changed Java string UTF8 validation from %q to %q.`,
- numberString,
- field.Name(),
- field.ParentMessage().Name(),
+ `%s changed Java string UTF8 validation from %q to %q.`,
+ fieldDescription(field),
previousValidation,
validation,
)
@@ -457,16 +479,12 @@ func checkFieldSameDefault(
return nil
}
if !defaultsEqual(previousDefault, currentDefault) {
- // otherwise prints as hex
- numberString := strconv.FormatInt(int64(field.Number()), 10)
add(
field,
nil,
withBackupLocation(field.DefaultLocation(), field.Location()),
- `Field %q with name %q on message %q changed default value from %v to %v.`,
- numberString,
- field.Name(),
- field.ParentMessage().Name(),
+ `% changed default value from %v to %v.`,
+ fieldDescription(field),
previousDefault.printable,
currentDefault.printable,
)
@@ -478,10 +496,15 @@ func checkFieldSameDefault(
var CheckFieldSameJSONName = newFieldPairCheckFunc(checkFieldSameJSONName)
func checkFieldSameJSONName(add addFunc, corpus *corpus, previousField bufprotosource.Field, field bufprotosource.Field) error {
+ if previousField.Extendee() != "" {
+ // JSON name can't be set explicitly for extensions
+ return nil
+ }
if previousField.JSONName() != field.JSONName() {
- // otherwise prints as hex
- numberString := strconv.FormatInt(int64(field.Number()), 10)
- add(field, nil, withBackupLocation(field.JSONNameLocation(), field.Location()), `Field %q with name %q on message %q changed option "json_name" from %q to %q.`, numberString, field.Name(), field.ParentMessage().Name(), previousField.JSONName(), field.JSONName())
+ add(field, nil, withBackupLocation(field.JSONNameLocation(), field.Location()),
+ `%s changed option "json_name" from %q to %q.`,
+ fieldDescription(field),
+ previousField.JSONName(), field.JSONName())
}
return nil
}
@@ -490,10 +513,15 @@ func checkFieldSameJSONName(add addFunc, corpus *corpus, previousField bufprotos
var CheckFieldSameJSType = newFieldPairCheckFunc(checkFieldSameJSType)
func checkFieldSameJSType(add addFunc, corpus *corpus, previousField bufprotosource.Field, field bufprotosource.Field) error {
+ if !is64bitInteger(previousField.Type()) || !is64bitInteger(field.Type()) {
+ // this check only applies to 64-bit integer fields
+ return nil
+ }
if previousField.JSType() != field.JSType() {
- // otherwise prints as hex
- numberString := strconv.FormatInt(int64(field.Number()), 10)
- add(field, nil, withBackupLocation(field.JSTypeLocation(), field.Location()), `Field %q with name %q on message %q changed option "jstype" from %q to %q.`, numberString, field.Name(), field.ParentMessage().Name(), previousField.JSType().String(), field.JSType().String())
+ add(field, nil, withBackupLocation(field.JSTypeLocation(), field.Location()),
+ `%s changed option "jstype" from %q to %q.`,
+ fieldDescription(field),
+ previousField.JSType().String(), field.JSType().String())
}
return nil
}
@@ -502,10 +530,19 @@ func checkFieldSameJSType(add addFunc, corpus *corpus, previousField bufprotosou
var CheckFieldSameName = newFieldPairCheckFunc(checkFieldSameName)
func checkFieldSameName(add addFunc, corpus *corpus, previousField bufprotosource.Field, field bufprotosource.Field) error {
- if previousField.Name() != field.Name() {
- // otherwise prints as hex
- numberString := strconv.FormatInt(int64(field.Number()), 10)
- add(field, nil, field.NameLocation(), `Field %q on message %q changed name from %q to %q.`, numberString, field.ParentMessage().Name(), previousField.Name(), field.Name())
+ var previousName, name string
+ if previousField.Extendee() != "" {
+ previousName = previousField.FullName()
+ name = field.FullName()
+ } else {
+ previousName = previousField.Name()
+ name = field.Name()
+ }
+ if previousName != name {
+ add(field, nil, field.NameLocation(),
+ `%s changed name from %q to %q.`,
+ fieldDescriptionWithName(field, ""), // don't include name in description
+ previousName, name)
}
return nil
}
@@ -514,6 +551,10 @@ func checkFieldSameName(add addFunc, corpus *corpus, previousField bufprotosourc
var CheckFieldSameOneof = newFieldPairCheckFunc(checkFieldSameOneof)
func checkFieldSameOneof(add addFunc, corpus *corpus, previousField bufprotosource.Field, field bufprotosource.Field) error {
+ if previousField.Extendee() != "" {
+ // extensions can't be defined inside oneofs
+ return nil
+ }
previousOneof := previousField.Oneof()
if previousOneof != nil {
previousOneofDescriptor, err := previousOneof.AsDescriptor()
@@ -547,9 +588,10 @@ func checkFieldSameOneof(add addFunc, corpus *corpus, previousField bufprotosour
}
if previousInsideOneof && insideOneof {
if previousOneof.Name() != oneof.Name() {
- // otherwise prints as hex
- numberString := strconv.FormatInt(int64(field.Number()), 10)
- add(field, nil, field.Location(), `Field %q on message %q moved from oneof %q to oneof %q.`, numberString, field.ParentMessage().Name(), previousOneof.Name(), oneof.Name())
+ add(field, nil, field.Location(),
+ `%sq moved from oneof %q to oneof %q.`,
+ fieldDescription(field),
+ previousOneof.Name(), oneof.Name())
}
return nil
}
@@ -560,9 +602,10 @@ func checkFieldSameOneof(add addFunc, corpus *corpus, previousField bufprotosour
previous = "outside"
current = "inside"
}
- // otherwise prints as hex
- numberString := strconv.FormatInt(int64(field.Number()), 10)
- add(field, nil, field.Location(), `Field %q on message %q moved from %s to %s a oneof.`, numberString, field.ParentMessage().Name(), previous, current)
+ add(field, nil, field.Location(),
+ `%s moved from %s to %s a oneof.`,
+ fieldDescription(field),
+ previous, current)
return nil
}
@@ -626,16 +669,12 @@ func checkFieldSameUTF8Validation(
}
utf8Validation := descriptorpb.FeatureSet_Utf8Validation(val.Enum())
if previousUTF8Validation != utf8Validation {
- // otherwise prints as hex
- numberString := strconv.FormatInt(int64(field.Number()), 10)
add(
field,
nil,
withBackupLocation(field.Features().UTF8ValidationLocation(), field.Location()),
- `Field %q with name %q on message %q changed UTF8 validation from %v to %v.`,
- numberString,
- field.Name(),
- field.ParentMessage().Name(),
+ `%s changed UTF8 validation from %v to %v.`,
+ fieldDescription(field),
previousUTF8Validation,
utf8Validation,
)
@@ -667,11 +706,9 @@ func checkFieldWireCompatibleCardinality(
previousCardinality := getCardinality(previousDescriptor)
currentCardinality := getCardinality(descriptor)
if cardinalityToWireCompatiblityGroup[previousCardinality] != cardinalityToWireCompatiblityGroup[currentCardinality] {
- // otherwise prints as hex
- numberString := strconv.FormatInt(int64(field.Number()), 10)
add(field, nil, field.Location(),
- `Field %q on message %q changed cardinality from %q to %q.`,
- numberString, field.ParentMessage().Name(),
+ `%s changed cardinality from %q to %q.`,
+ fieldDescription(field),
previousCardinality,
currentCardinality,
)
@@ -758,11 +795,9 @@ func checkFieldWireJSONCompatibleCardinality(
previousCardinality := getCardinality(previousDescriptor)
currentCardinality := getCardinality(descriptor)
if cardinalityToWireJSONCompatiblityGroup[previousCardinality] != cardinalityToWireJSONCompatiblityGroup[currentCardinality] {
- // otherwise prints as hex
- numberString := strconv.FormatInt(int64(field.Number()), 10)
add(field, nil, field.Location(),
- `Field %q on message %q changed cardinality from %q to %q.`,
- numberString, field.ParentMessage().Name(),
+ `%s changed cardinality from %q to %q.`,
+ fieldDescription(field),
previousCardinality,
currentCardinality,
)
@@ -875,15 +910,12 @@ func addFieldChangedType(
default:
fieldLocation = field.TypeLocation()
}
- // otherwise prints as hex
- previousNumberString := strconv.FormatInt(int64(previousField.Number()), 10)
add(
field,
nil,
fieldLocation,
- `Field %q on message %q changed type from %q to %q.%s`,
- previousNumberString,
- field.ParentMessage().Name(),
+ `%s changed type from %q to %q.%s`,
+ fieldDescription(field),
fieldDescriptorTypePrettyString(previousDescriptor),
fieldDescriptorTypePrettyString(descriptor),
combinedExtraMessage,
@@ -891,15 +923,12 @@ func addFieldChangedType(
}
func addEnumGroupMessageFieldChangedTypeName(add addFunc, previousField bufprotosource.Field, field bufprotosource.Field) {
- // otherwise prints as hex
- numberString := strconv.FormatInt(int64(previousField.Number()), 10)
add(
field,
nil,
field.TypeNameLocation(),
- `Field %q on message %q changed type from %q to %q.`,
- numberString,
- field.ParentMessage().Name(),
+ `%s changed type from %q to %q.`,
+ fieldDescription(field),
strings.TrimPrefix(previousField.TypeName(), "."),
strings.TrimPrefix(field.TypeName(), "."),
)
@@ -1252,7 +1281,7 @@ func checkPackageEnumNoDelete(add addFunc, corpus *corpus) error {
file, ok := filePathToFile[previousEnum.File().Path()]
if ok {
// File exists, try to get a location to attach the error to.
- descriptor, location, err := getDescriptorAndLocationForDeletedEnum(file, previousNestedName)
+ descriptor, location, err := getDescriptorAndLocationForDeletedElement(file, previousNestedName)
if err != nil {
return err
}
@@ -1271,6 +1300,54 @@ func checkPackageEnumNoDelete(add addFunc, corpus *corpus) error {
return nil
}
+// CheckPackageExtensionNoDelete is a check function.
+var CheckPackageExtensionNoDelete = newFilesCheckFunc(checkPackageExtensionNoDelete)
+
+func checkPackageExtensionNoDelete(add addFunc, corpus *corpus) error {
+ previousPackageToNestedNameToExtension, err := bufprotosource.PackageToNestedNameToExtension(corpus.previousFiles...)
+ if err != nil {
+ return err
+ }
+ packageToNestedNameToExtension, err := bufprotosource.PackageToNestedNameToExtension(corpus.files...)
+ if err != nil {
+ return err
+ }
+ // caching across loops
+ var filePathToFile map[string]bufprotosource.File
+ for previousPackage, previousNestedNameToExtension := range previousPackageToNestedNameToExtension {
+ if nestedNameToExtension, ok := packageToNestedNameToExtension[previousPackage]; ok {
+ for previousNestedName, previousExtension := range previousNestedNameToExtension {
+ if _, ok := nestedNameToExtension[previousNestedName]; !ok {
+ // if cache not populated, populate it
+ if filePathToFile == nil {
+ filePathToFile, err = bufprotosource.FilePathToFile(corpus.files...)
+ if err != nil {
+ return err
+ }
+ }
+ // Check if the file still exists.
+ file, ok := filePathToFile[previousExtension.File().Path()]
+ if ok {
+ // File exists, try to get a location to attach the error to.
+ descriptor, location, err := getDescriptorAndLocationForDeletedElement(file, previousNestedName)
+ if err != nil {
+ return err
+ }
+ add(descriptor, nil, location, `Previously present extension %q was deleted from package %q.`, previousNestedName, previousPackage)
+ } else {
+ // File does not exist, we don't know where the enum was deleted from.
+ // Add the previous enum to check for ignores. This means that if
+ // ignore_unstable_packages is set, this will be triggered if the
+ // previous enum was in an unstable package.
+ add(nil, []bufprotosource.Descriptor{previousExtension}, nil, `Previously present extension %q was deleted from package %q.`, previousNestedName, previousPackage)
+ }
+ }
+ }
+ }
+ }
+ return nil
+}
+
// CheckPackageMessageNoDelete is a check function.
var CheckPackageMessageNoDelete = newFilesCheckFunc(checkPackageMessageNoDelete)
diff --git a/private/bufpkg/bufcheck/bufbreaking/internal/bufbreakingcheck/util.go b/private/bufpkg/bufcheck/bufbreaking/internal/bufbreakingcheck/util.go
index 2396a6a67b..c92cd81b93 100644
--- a/private/bufpkg/bufcheck/bufbreaking/internal/bufbreakingcheck/util.go
+++ b/private/bufpkg/bufcheck/bufbreaking/internal/bufbreakingcheck/util.go
@@ -17,6 +17,7 @@ package bufbreakingcheck
import (
"fmt"
"sort"
+ "strconv"
"strings"
"github.com/bufbuild/buf/private/bufpkg/bufanalysis"
@@ -236,25 +237,56 @@ func newMessagePairCheckFunc(
func newFieldPairCheckFunc(
f func(addFunc, *corpus, bufprotosource.Field, bufprotosource.Field) error,
) func(string, internal.IgnoreFunc, []bufprotosource.File, []bufprotosource.File) ([]bufanalysis.FileAnnotation, error) {
- return newMessagePairCheckFunc(
- func(add addFunc, corpus *corpus, previousMessage bufprotosource.Message, message bufprotosource.Message) error {
- previousNumberToField, err := bufprotosource.NumberToMessageField(previousMessage)
- if err != nil {
- return err
- }
- numberToField, err := bufprotosource.NumberToMessageField(message)
- if err != nil {
- return err
- }
- for previousNumber, previousField := range previousNumberToField {
- if field, ok := numberToField[previousNumber]; ok {
- if err := f(add, corpus, previousField, field); err != nil {
+ return combine(
+ // Regular fields
+ newMessagePairCheckFunc(
+ func(add addFunc, corpus *corpus, previousMessage bufprotosource.Message, message bufprotosource.Message) error {
+ previousNumberToField, err := bufprotosource.NumberToMessageField(previousMessage)
+ if err != nil {
+ return err
+ }
+ numberToField, err := bufprotosource.NumberToMessageField(message)
+ if err != nil {
+ return err
+ }
+ for previousNumber, previousField := range previousNumberToField {
+ if field, ok := numberToField[previousNumber]; ok {
+ if err := f(add, corpus, previousField, field); err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+ },
+ ),
+ // And extension fields
+ newFilesCheckFunc(
+ func(add addFunc, corpus *corpus) error {
+ previousTypeToNumberToField := make(map[string]map[int]bufprotosource.Field)
+ for _, previousFile := range corpus.previousFiles {
+ if err := addToTypeToNumberToExtension(previousFile, previousTypeToNumberToField); err != nil {
return err
}
}
- }
- return nil
- },
+ typeToNumberToField := make(map[string]map[int]bufprotosource.Field)
+ for _, file := range corpus.files {
+ if err := addToTypeToNumberToExtension(file, typeToNumberToField); err != nil {
+ return err
+ }
+ }
+ for previousType, previousNumberToField := range previousTypeToNumberToField {
+ numberToField := typeToNumberToField[previousType]
+ for previousNumber, previousField := range previousNumberToField {
+ if field, ok := numberToField[previousNumber]; ok {
+ if err := f(add, corpus, previousField, field); err != nil {
+ return err
+ }
+ }
+ }
+ }
+ return nil
+ },
+ ),
)
}
@@ -326,7 +358,23 @@ func newMethodPairCheckFunc(
)
}
-func getDescriptorAndLocationForDeletedEnum(file bufprotosource.File, previousNestedName string) (bufprotosource.Descriptor, bufprotosource.Location, error) {
+func combine(
+ checks ...func(string, internal.IgnoreFunc, []bufprotosource.File, []bufprotosource.File) ([]bufanalysis.FileAnnotation, error),
+) func(string, internal.IgnoreFunc, []bufprotosource.File, []bufprotosource.File) ([]bufanalysis.FileAnnotation, error) {
+ return func(id string, ignoreFunc internal.IgnoreFunc, previousFiles, files []bufprotosource.File) ([]bufanalysis.FileAnnotation, error) {
+ var annotations []bufanalysis.FileAnnotation
+ for _, check := range checks {
+ checkAnnotations, err := check(id, ignoreFunc, previousFiles, files)
+ if err != nil {
+ return nil, err
+ }
+ annotations = append(annotations, checkAnnotations...)
+ }
+ return annotations, nil
+ }
+}
+
+func getDescriptorAndLocationForDeletedElement(file bufprotosource.File, previousNestedName string) (bufprotosource.Descriptor, bufprotosource.Location, error) {
if strings.Contains(previousNestedName, ".") {
nestedNameToMessage, err := bufprotosource.NestedNameToMessage(file)
if err != nil {
@@ -488,3 +536,67 @@ func getCustomFeatureLocation(field bufprotosource.Field, extension protoreflect
}
return field.OptionLocation(featureField, int32(extension.Number()), int32(feature.Number()))
}
+
+func fieldDescription(field bufprotosource.Field) string {
+ var name string
+ if field.Extendee() != "" {
+ // extensions are known by fully-qualified name
+ name = field.FullName()
+ } else {
+ name = field.Name()
+ }
+ return fieldDescriptionWithName(field, name)
+}
+
+func fieldDescriptionWithName(field bufprotosource.Field, name string) string {
+ if name != "" {
+ name = fmt.Sprintf(" with name %q", name)
+ }
+ // otherwise prints as hex
+ numberString := strconv.FormatInt(int64(field.Number()), 10)
+ var kind, message string
+ if field.Extendee() != "" {
+ kind = "Extension"
+ message = field.Extendee()
+ } else {
+ kind = "Field"
+ message = field.ParentMessage().Name()
+ }
+ return fmt.Sprintf("%s %q%s on message %q", kind, numberString, name, message)
+}
+
+func addToTypeToNumberToExtension(container bufprotosource.ContainerDescriptor, typeToNumberToExt map[string]map[int]bufprotosource.Field) error {
+ for _, extension := range container.Extensions() {
+ numberToExt := typeToNumberToExt[extension.Extendee()]
+ if numberToExt == nil {
+ numberToExt = make(map[int]bufprotosource.Field)
+ typeToNumberToExt[extension.Extendee()] = numberToExt
+ }
+ if existing, ok := numberToExt[extension.Number()]; ok {
+ return fmt.Errorf("duplicate extension %d of %s: %s in %q and %s in %q",
+ extension.Number(), extension.Extendee(),
+ existing.FullName(), existing.File().Path(),
+ extension.FullName(), extension.File().Path())
+ }
+ numberToExt[extension.Number()] = extension
+ }
+ for _, message := range container.Messages() {
+ if err := addToTypeToNumberToExtension(message, typeToNumberToExt); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func is64bitInteger(fieldType descriptorpb.FieldDescriptorProto_Type) bool {
+ switch fieldType {
+ case descriptorpb.FieldDescriptorProto_TYPE_INT64,
+ descriptorpb.FieldDescriptorProto_TYPE_SINT64,
+ descriptorpb.FieldDescriptorProto_TYPE_UINT64,
+ descriptorpb.FieldDescriptorProto_TYPE_FIXED64,
+ descriptorpb.FieldDescriptorProto_TYPE_SFIXED64:
+ return true
+ default:
+ return false
+ }
+}
diff --git a/private/bufpkg/bufcheck/bufbreaking/internal/bufbreakingv2/bufbreakingv2.go b/private/bufpkg/bufcheck/bufbreaking/internal/bufbreakingv2/bufbreakingv2.go
index 4aa617cecf..d73093b571 100644
--- a/private/bufpkg/bufcheck/bufbreaking/internal/bufbreakingv2/bufbreakingv2.go
+++ b/private/bufpkg/bufcheck/bufbreaking/internal/bufbreakingv2/bufbreakingv2.go
@@ -31,6 +31,11 @@ import (
// values are a feature of proto2 and editions, but not in
// proto3 syntax.)
//
+// Adds new EXTENSION_NO_DELETE and PACKAGE_EXTENSION_NO_DELETE
+// checks, to make sure that extensions are not deleted from a
+// file or package, respectively. (In previous versions, an
+// extension being deleted was simply undetected.)
+//
// Removes the following deprecated checks (but retains their
// replacements):
// - FIELD_SAME_CTYPE
diff --git a/private/bufpkg/bufcheck/bufbreaking/internal/bufbreakingv2/vars.go b/private/bufpkg/bufcheck/bufbreaking/internal/bufbreakingv2/vars.go
index 5fbfde0c5c..7d2c8e9785 100644
--- a/private/bufpkg/bufcheck/bufbreaking/internal/bufbreakingv2/vars.go
+++ b/private/bufpkg/bufcheck/bufbreaking/internal/bufbreakingv2/vars.go
@@ -30,6 +30,7 @@ var (
bufbreakingbuild.EnumValueNoDeleteUnlessNumberReservedRuleBuilder,
bufbreakingbuild.EnumValueSameNameRuleBuilder,
bufbreakingbuild.ExtensionMessageNoDeleteRuleBuilder,
+ bufbreakingbuild.ExtensionNoDeleteRuleBuilder,
bufbreakingbuild.FieldNoDeleteRuleBuilder,
bufbreakingbuild.FieldNoDeleteUnlessNameReservedRuleBuilder,
bufbreakingbuild.FieldNoDeleteUnlessNumberReservedRuleBuilder,
@@ -73,6 +74,7 @@ var (
bufbreakingbuild.MessageSameRequiredFieldsRuleBuilder,
bufbreakingbuild.OneofNoDeleteRuleBuilder,
bufbreakingbuild.PackageEnumNoDeleteRuleBuilder,
+ bufbreakingbuild.PackageExtensionNoDeleteRuleBuilder,
bufbreakingbuild.PackageMessageNoDeleteRuleBuilder,
bufbreakingbuild.PackageNoDeleteRuleBuilder,
bufbreakingbuild.PackageServiceNoDeleteRuleBuilder,
@@ -124,6 +126,9 @@ var (
"FILE",
"PACKAGE",
},
+ "EXTENSION_NO_DELETE": {
+ "FILE",
+ },
"FIELD_NO_DELETE": {
"FILE",
"PACKAGE",
@@ -300,6 +305,9 @@ var (
"PACKAGE_ENUM_NO_DELETE": {
"PACKAGE",
},
+ "PACKAGE_EXTENSION_NO_DELETE": {
+ "PACKAGE",
+ },
"PACKAGE_MESSAGE_NO_DELETE": {
"PACKAGE",
},
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_extension_no_delete/1.proto b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_extension_no_delete/1.proto
new file mode 100644
index 0000000000..c4b0a38d51
--- /dev/null
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_extension_no_delete/1.proto
@@ -0,0 +1,37 @@
+syntax = "proto3";
+
+package a;
+
+message Two {
+ int32 one = 1;
+ int32 two = 2;
+ int32 three = 3;
+}
+
+message Three {
+ message Four {
+ message Five {
+ int32 one = 1;
+ int32 two = 2;
+ int32 three = 3;
+ }
+ message Six {
+ int32 one = 1;
+ int32 two = 2;
+ int32 three = 3;
+ }
+ }
+ message Seven {
+ int32 one = 1;
+ int32 two = 2;
+ int32 three = 3;
+ }
+ message Eight {
+ int32 one = 1;
+ int32 two = 2;
+ int32 three = 3;
+ }
+ int32 one = 1;
+ int32 two = 2;
+ int32 three = 3;
+}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_extension_no_delete/2.proto b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_extension_no_delete/2.proto
new file mode 100644
index 0000000000..5fa7742702
--- /dev/null
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_extension_no_delete/2.proto
@@ -0,0 +1,25 @@
+syntax = "proto2";
+
+package a;
+
+import "3.proto";
+import "4.proto";
+
+message One {
+ optional int32 one = 1;
+ optional int32 two = 2;
+}
+
+message Nine {
+ optional int32 one = 1;
+ optional int32 three = 3;
+}
+
+extend Foo {
+ optional bytes meta = 20;
+ optional Foo ch = 22;
+}
+
+extend b.Fizz {
+ optional b.Fizz child = 22;
+}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_extension_no_delete/3.proto b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_extension_no_delete/3.proto
new file mode 100644
index 0000000000..5fe583c710
--- /dev/null
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_extension_no_delete/3.proto
@@ -0,0 +1,20 @@
+syntax = "proto2";
+
+package b;
+
+message Fizz {
+ extensions 10 to 100;
+
+ message Buzz {
+ optional int32 len = 1;
+
+ extend Fizz {
+ optional string str = 10;
+ }
+ }
+}
+
+extend Fizz {
+ optional bytes meta = 20;
+ repeated uint64 tags = 21;
+}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_extension_no_delete/4.proto b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_extension_no_delete/4.proto
new file mode 100644
index 0000000000..721ba92620
--- /dev/null
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_extension_no_delete/4.proto
@@ -0,0 +1,16 @@
+syntax = "proto2";
+
+package a;
+
+message Foo {
+ extensions 10 to 100;
+
+ message Bar {
+ optional int32 len = 1;
+
+ extend Foo {
+ optional string str = 10;
+ repeated string labels = 11;
+ }
+ }
+}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_extension_no_delete/buf.yaml b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_extension_no_delete/buf.yaml
new file mode 100644
index 0000000000..4c07ede4e4
--- /dev/null
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_extension_no_delete/buf.yaml
@@ -0,0 +1,4 @@
+version: v2
+breaking:
+ use:
+ - EXTENSION_NO_DELETE
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_no_delete/3.proto b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_no_delete/3.proto
new file mode 100644
index 0000000000..a4ecd5c3b4
--- /dev/null
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_no_delete/3.proto
@@ -0,0 +1,17 @@
+syntax = "proto2";
+
+package a;
+
+message Foo {
+ extensions 10 to 100;
+ message Bar {
+ optional int32 len = 1;
+ extend Foo {
+ optional string str = 10;
+ }
+ }
+}
+
+extend Foo {
+ repeated uint64 tags = 21;
+}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_no_delete_unless_name_reserved/3.proto b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_no_delete_unless_name_reserved/3.proto
new file mode 100644
index 0000000000..a4ecd5c3b4
--- /dev/null
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_no_delete_unless_name_reserved/3.proto
@@ -0,0 +1,17 @@
+syntax = "proto2";
+
+package a;
+
+message Foo {
+ extensions 10 to 100;
+ message Bar {
+ optional int32 len = 1;
+ extend Foo {
+ optional string str = 10;
+ }
+ }
+}
+
+extend Foo {
+ repeated uint64 tags = 21;
+}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_no_delete_unless_number_reserved/3.proto b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_no_delete_unless_number_reserved/3.proto
new file mode 100644
index 0000000000..a4ecd5c3b4
--- /dev/null
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_no_delete_unless_number_reserved/3.proto
@@ -0,0 +1,17 @@
+syntax = "proto2";
+
+package a;
+
+message Foo {
+ extensions 10 to 100;
+ message Bar {
+ optional int32 len = 1;
+ extend Foo {
+ optional string str = 10;
+ }
+ }
+}
+
+extend Foo {
+ repeated uint64 tags = 21;
+}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_cardinality/3.proto b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_cardinality/3.proto
index 8115aee21b..e5c7186484 100644
--- a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_cardinality/3.proto
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_cardinality/3.proto
@@ -6,3 +6,21 @@ message Ten2 {
repeated int64 one = 1;
int64 two = 2 [features.field_presence = LEGACY_REQUIRED];
}
+
+message Foo {
+ extensions 10 to 100;
+
+ message Bar {
+ int32 len = 1;
+
+ extend Foo {
+ repeated string str = 10;
+ repeated string labels = 11;
+ }
+ }
+}
+
+extend Foo {
+ bytes meta = 20;
+ uint64 tags = 21;
+}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_cpp_string_type/2.proto b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_cpp_string_type/2.proto
index be7db0c60c..2ab9f8cb82 100644
--- a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_cpp_string_type/2.proto
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_cpp_string_type/2.proto
@@ -33,4 +33,19 @@ message Bar {
string default5 = 35 [features.(pb.cpp).string_type=VIEW];
bytes default6 = 36 [features.(pb.cpp).string_type=CORD];
repeated string default7 = 37;
+
+ extensions 100 to 300;
+
+ message Frobnitz {
+ extend Bar {
+ repeated string ext_str1 = 100;
+ string ext_str2 = 101;
+ repeated bytes ext_byt1 = 102;
+ bytes ext_byt2 = 103 [features.(pb.cpp).string_type=VIEW];
+ }
+ }
+}
+
+extend Bar {
+ repeated string ext_str1 = 200;
}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_cpp_string_type/3.proto b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_cpp_string_type/3.proto
index 43f4a47252..dd39615f0e 100644
--- a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_cpp_string_type/3.proto
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_cpp_string_type/3.proto
@@ -2,6 +2,7 @@ edition = "2023";
package a;
+import "2.proto";
import "google/protobuf/cpp_features.proto";
option features.(pb.cpp).string_type=CORD;
@@ -18,4 +19,23 @@ message Baz {
map map1 = 11;
map map2 = 12;
map map3 = 13;
+
+ extensions 100 to 200;
+
+ message Fizzbuzz {
+ extend Baz {
+ repeated string ext_str1 = 100 [features.(pb.cpp).string_type=CORD];
+ string ext_str2 = 101;
+ repeated bytes ext_byt1 = 102 [features.(pb.cpp).string_type=VIEW];
+ bytes ext_byt2 = 103 [ctype=STRING];
+ }
+ }
+}
+
+extend Baz {
+ repeated string ext_str = 200;
+}
+
+extend Bar {
+ repeated string ext_str2 = 201;
}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_ctype/2.proto b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_ctype/2.proto
index 6b96d0903b..97d3135566 100644
--- a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_ctype/2.proto
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_ctype/2.proto
@@ -49,3 +49,11 @@ message Nine {
optional string one = 1 [ctype = STRING_PIECE];
optional string two = 2 [ctype = STRING];
}
+
+message Ten2 {
+ extensions 1 to 100;
+}
+
+extend Ten2 {
+ optional string ten_one = 1 [ctype = CORD];
+}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_default/1.proto b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_default/1.proto
index 72df2636b0..0986ca76c0 100644
--- a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_default/1.proto
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_default/1.proto
@@ -133,4 +133,23 @@ message Baz {
repeated uint64 uints = 3;
optional Bar bar = 4;
repeated Bar bars = 5;
-}
\ No newline at end of file
+
+ extensions 100 to 200;
+
+ extend Baz {
+ optional string s = 200 [default = "abc"];
+ optional bool b = 199;
+ optional float f32 = 198 [default = 123.0];
+ optional double f = 196 [default = 0.1020304];
+ }
+}
+
+extend Baz {
+ optional uint32 u1 = 100 [default = 123];
+ optional uint32 u2 = 101 [default = 9876];
+ optional bytes b1 = 102 [default = "abc"];
+ optional bytes b2 = 103 [default = "a1b2c3"];
+ repeated Baz bb1 = 104;
+ optional string s1 = 105 [default = "0"];
+ optional string s2 = 106 [default = "xyz"];
+}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_default/2.proto b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_default/2.proto
index 0bdc0851d8..44212a0aee 100644
--- a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_default/2.proto
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_default/2.proto
@@ -1,6 +1,6 @@
edition = "2023";
-
package a;
+import "google/protobuf/descriptor.proto";
message Fizz {
string s1 = 1 [default = "abc"];
@@ -97,3 +97,9 @@ message Buzz {
Buzz buzz = 4;
repeated Buzz buzzes = 5;
}
+
+extend google.protobuf.MessageOptions {
+ uint32 num = 10000 [default = 0];
+ repeated string strings = 10101;
+ Buzz buzz = 20202;
+}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_java_utf8_validation/1.proto b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_java_utf8_validation/1.proto
index 3bc0df1942..9488ab7d9e 100644
--- a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_java_utf8_validation/1.proto
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_java_utf8_validation/1.proto
@@ -3,6 +3,7 @@ syntax = "proto2";
package a;
message A1 {
+ extensions 100 to 1000;
optional string s1 = 1;
repeated string s2 = 2;
map s3 = 3;
@@ -14,6 +15,7 @@ message A1 {
}
message B1 {
+ extensions 100 to 1000;
optional string s1 = 1;
repeated string s2 = 2;
map s3 = 3;
@@ -36,6 +38,7 @@ message C1 {
}
message D1 {
+ extensions 100 to 1000;
optional string s1 = 1;
repeated string s2 = 2;
map s3 = 3;
@@ -47,6 +50,7 @@ message D1 {
}
message E1 {
+ extensions 100 to 1000;
optional string s1 = 1;
repeated string s2 = 2;
map s3 = 3;
@@ -58,6 +62,7 @@ message E1 {
}
message F1 {
+ extensions 100 to 1000;
optional string s1 = 1;
repeated string s2 = 2;
map s3 = 3;
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_java_utf8_validation/6.proto b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_java_utf8_validation/6.proto
index 104516b73b..832989c5d3 100644
--- a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_java_utf8_validation/6.proto
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_java_utf8_validation/6.proto
@@ -1,7 +1,7 @@
edition = "2023";
package a;
-
+import "1.proto";
import "google/protobuf/java_features.proto";
option features.utf8_validation = NONE;
@@ -16,6 +16,13 @@ message A6 {
repeated double other2 = 12;
map other3 = 13;
A6 other4 = 14;
+
+ extensions 100 to 1000;
+
+ extend A1 {
+ string a1_s1 = 100;
+ repeated string a1_s2 = 101;
+ }
}
message B6 {
@@ -27,6 +34,13 @@ message B6 {
repeated double other2 = 12;
map other3 = 13;
B6 other4 = 14;
+
+ extensions 100 to 1000;
+
+ extend B1 {
+ string b1_s1 = 100;
+ repeated string b1_s2 = 101;
+ }
}
message C6 {
@@ -49,6 +63,13 @@ message D6 {
repeated double other2 = 12;
map other3 = 13;
D6 other4 = 14;
+
+ extensions 100 to 1000;
+
+ extend D1 {
+ string d1_s1 = 100;
+ repeated string d1_s2 = 101;
+ }
}
message E6 {
@@ -60,6 +81,13 @@ message E6 {
repeated double other2 = 12;
map other3 = 13;
E6 other4 = 14;
+
+ extensions 100 to 1000;
+
+ extend E1 {
+ string e1_s1 = 100;
+ repeated string e1_s2 = 101;
+ }
}
message F6 {
@@ -71,6 +99,13 @@ message F6 {
repeated double other2 = 12;
map other3 = 13;
F6 other4 = 14;
+
+ extensions 100 to 1000;
+
+ extend F1 {
+ string f1_s1 = 100;
+ repeated string f1_s2 = 101;
+ }
}
message D8 {
@@ -94,3 +129,28 @@ message E8 {
map other3 = 13;
E8 other4 = 14;
}
+
+extend A6 {
+ string a6_s1 = 100;
+ repeated string a6_s2 = 101;
+}
+
+extend B6 {
+ string b6_s1 = 100;
+ repeated string b6_s2 = 101;
+}
+
+extend D6 {
+ string d6_s1 = 100;
+ repeated string d6_s2 = 101;
+}
+
+extend E6 {
+ string e6_s1 = 100;
+ repeated string e6_s2 = 101;
+}
+
+extend F6 {
+ string f6_s1 = 100;
+ repeated string f6_s2 = 101;
+}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_jstype/2.proto b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_jstype/2.proto
index 6aa2334f2c..40e4ba5da3 100644
--- a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_jstype/2.proto
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_jstype/2.proto
@@ -1,6 +1,6 @@
syntax = "proto2";
-
package a;
+import "google/protobuf/descriptor.proto";
message One {
optional int64 one = 1;
@@ -43,9 +43,30 @@ message Three2 {
message Nine2 {
optional int64 one = 1 [jstype = JS_NUMBER];
required int64 two = 2 [jstype = JS_STRING];
+
+ extensions 100 to 1000;
}
message Nine {
optional int64 one = 1 [jstype = JS_NUMBER];
optional int64 two = 2;
+
+ extend google.protobuf.FileOptions {
+ optional uint64 f1 = 10001 [jstype = JS_NORMAL];
+ optional sint64 f2 = 10002 [jstype = JS_STRING];
+ optional int64 f3 = 10003 [jstype = JS_STRING];
+ optional fixed64 f4 = 10004 [jstype = JS_NUMBER];
+ optional sfixed64 f5 = 10005;
+ }
+}
+
+extend Nine2 {
+ optional sint64 s1 = 101 [jstype = JS_NORMAL];
+ optional sint64 s2 = 102 [jstype = JS_NUMBER];
+ optional sint64 s3 = 103 [jstype = JS_STRING];
+ optional sint64 s4 = 104;
+ optional sint64 s5 = 105 [jstype = JS_STRING];
+ optional sint64 s6 = 106 [jstype = JS_NUMBER];
+ optional sint64 s7 = 107;
+ optional sint64 s8 = 108;
}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_name/1.proto b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_name/1.proto
index 4975740a07..a84f16cebc 100644
--- a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_name/1.proto
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_name/1.proto
@@ -1,6 +1,6 @@
syntax = "proto3";
-
package a;
+import "google/protobuf/descriptor.proto";
message Two {
int32 one = 1;
@@ -30,8 +30,17 @@ message Three {
int32 one = 1;
int32 two = 2;
int32 three = 3;
+
+ extend google.protobuf.MessageOptions {
+ string msg_str_opt = 10101;
+ }
}
int32 one = 1;
int32 two = 2;
int32 three = 3;
}
+
+
+extend google.protobuf.FileOptions {
+ string file_str_opt = 10101;
+}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_name/2.proto b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_name/2.proto
index eb4361be69..df173dd0db 100644
--- a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_name/2.proto
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_name/2.proto
@@ -1,57 +1,75 @@
-syntax = "proto3";
+syntax = "proto2";
package a;
message One {
- int32 one = 1;
- int32 two = 2;
- int32 three = 3;
+ optional int32 one = 1;
+ optional int32 two = 2;
+ optional int32 three = 3;
}
message One2 {
- int32 one = 1;
- int32 two = 2;
- int32 three = 3;
+ optional int32 one = 1;
+ optional int32 two = 2;
+ optional int32 three = 3;
}
message Two2 {
- int32 one = 1;
- int32 two = 2;
- int32 three = 3;
+ optional int32 one = 1;
+ optional int32 two = 2;
+ optional int32 three = 3;
}
message Three2 {
message Four2 {
message Five2 {
- int32 one = 1;
- int32 two = 2;
- int32 three = 3;
+ optional int32 one = 1;
+ optional int32 two = 2;
+ optional int32 three = 3;
}
message Six2 {
- int32 one = 1;
- int32 two = 2;
- int32 three = 3;
+ optional int32 one = 1;
+ optional int32 two = 2;
+ optional int32 three = 3;
}
}
message Seven2 {
- int32 one = 1;
- int32 two = 2;
- int32 three = 3;
+ optional int32 one = 1;
+ optional int32 two = 2;
+ optional int32 three = 3;
}
message Eight2 {
- int32 one = 1;
- int32 two = 2;
- int32 three = 3;
+ optional int32 one = 1;
+ optional int32 two = 2;
+ optional int32 three = 3;
+
+ extend Three2 {
+ repeated uint64 uint_opt = 101;
+ optional string str_option = 102;
+ }
}
- int32 one = 1;
- int32 two = 2;
- int32 three = 3;
+ optional int32 one = 1;
+ optional int32 two = 2;
+ optional int32 three = 3;
+
+ extensions 100 to 1000;
}
message Nine2 {
- int32 one = 1;
- int32 two = 2;
- int32 three = 3;
+ optional int32 one = 1;
+ optional int32 two = 2;
+ optional int32 three = 3;
+
+ extend Three2 {
+ optional bool bool_opt = 103;
+ }
+
+ extensions 100 to 1000;
+}
+
+extend Nine2 {
+ repeated uint64 uint_opt = 101;
+ optional string str_option = 102;
}
message Nine {
@@ -60,4 +78,7 @@ message Nine {
int32 four = 2;
int32 three = 3;
}
+ extend Nine2 {
+ optional bool bool_opt = 103;
+ }
}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_type/2.proto b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_type/2.proto
index 9af1a018ce..48c00aadf1 100644
--- a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_type/2.proto
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_type/2.proto
@@ -1,6 +1,6 @@
syntax = "proto2";
-
package a;
+import "google/protobuf/descriptor.proto";
message One {
optional int32 one = 1;
@@ -63,3 +63,7 @@ message Ten {
repeated int64 one = 1;
required int64 two = 2;
}
+
+extend google.protobuf.FieldOptions {
+ repeated Nine ten_option = 10101;
+}
\ No newline at end of file
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_type/3.proto b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_type/3.proto
index 9d309ce149..39e4e16561 100644
--- a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_type/3.proto
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_same_type/3.proto
@@ -9,6 +9,8 @@ message One3 {
One3 delimited = 2;
One3 normal_b = 3;
One3 delimited_b = 4 [features.message_encoding=DELIMITED];
+
+ extensions 100 to 1000;
}
message Nine2 {
@@ -22,4 +24,18 @@ message Ten2 {
Foo foo = 1 [features.message_encoding=DELIMITED];
message Foo {
}
+
+ extensions 100 to 1000;
+
+ extend Ten2 {
+ bool ten2_str = 100;
+ Ten2 ten2_msg = 101;
+ int64 ten2_uint32 = 102;
+ }
+}
+
+extend One3 {
+ bytes one3_str = 100;
+ One3 one3_msg = 101 [features.message_encoding=DELIMITED];
+ uint32 one3_uint32 = 102;
}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_wire_compatible_cardinality/3.proto b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_wire_compatible_cardinality/3.proto
index 8115aee21b..e5c7186484 100644
--- a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_wire_compatible_cardinality/3.proto
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_wire_compatible_cardinality/3.proto
@@ -6,3 +6,21 @@ message Ten2 {
repeated int64 one = 1;
int64 two = 2 [features.field_presence = LEGACY_REQUIRED];
}
+
+message Foo {
+ extensions 10 to 100;
+
+ message Bar {
+ int32 len = 1;
+
+ extend Foo {
+ repeated string str = 10;
+ repeated string labels = 11;
+ }
+ }
+}
+
+extend Foo {
+ bytes meta = 20;
+ uint64 tags = 21;
+}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_wire_compatible_type/2.proto b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_wire_compatible_type/2.proto
index 4e916f3fa9..c1070f3fb8 100644
--- a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_wire_compatible_type/2.proto
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_wire_compatible_type/2.proto
@@ -1,6 +1,6 @@
syntax = "proto3";
-
package a;
+import "google/protobuf/descriptor.proto";
message CompatiblePrimitives {
bool bool_field_1 = 1;
@@ -84,3 +84,9 @@ message StringBytes {
bytes string_field_1 = 1;
string bytes_field_1 = 2;
}
+
+extend google.protobuf.FieldOptions {
+ repeated Bat bat_option = 10101;
+ bytes str_option = 10102;
+ bytes byt_option = 10103;
+}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_wire_compatible_type/3.proto b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_wire_compatible_type/3.proto
index 3a6e0a2034..eae0c3ffe7 100644
--- a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_wire_compatible_type/3.proto
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_wire_compatible_type/3.proto
@@ -7,4 +7,18 @@ message Message {
Message delimited = 2;
Message normal_b = 3;
Message delimited_b = 4 [features.message_encoding=DELIMITED];
+
+ extensions 100 to 1000;
+
+ extend Message {
+ bytes str = 100;
+ Message msg = 101 [features.message_encoding=DELIMITED];
+ uint64 uint64 = 102;
+ }
+}
+
+extend Message {
+ string str = 200;
+ Message msg = 201;
+ sint32 sint32 = 202;
}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_wire_json_compatible_cardinality/3.proto b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_wire_json_compatible_cardinality/3.proto
index 8115aee21b..e5c7186484 100644
--- a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_wire_json_compatible_cardinality/3.proto
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_wire_json_compatible_cardinality/3.proto
@@ -6,3 +6,21 @@ message Ten2 {
repeated int64 one = 1;
int64 two = 2 [features.field_presence = LEGACY_REQUIRED];
}
+
+message Foo {
+ extensions 10 to 100;
+
+ message Bar {
+ int32 len = 1;
+
+ extend Foo {
+ repeated string str = 10;
+ repeated string labels = 11;
+ }
+ }
+}
+
+extend Foo {
+ bytes meta = 20;
+ uint64 tags = 21;
+}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_wire_json_compatible_type/2.proto b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_wire_json_compatible_type/2.proto
index 95ed57b8fb..48399ce3f2 100644
--- a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_wire_json_compatible_type/2.proto
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_wire_json_compatible_type/2.proto
@@ -1,6 +1,6 @@
syntax = "proto3";
-
package a;
+import "google/protobuf/descriptor.proto";
message CompatiblePrimitives {
uint32 uint32_field_1 = 1;
@@ -87,3 +87,9 @@ message StringBytes {
bytes string_field_1 = 1;
string bytes_field_1 = 2;
}
+
+extend google.protobuf.FieldOptions {
+ repeated Bat bat_option = 10101;
+ bytes str_option = 10102;
+ bytes byt_option = 10103;
+}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_wire_json_compatible_type/3.proto b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_wire_json_compatible_type/3.proto
index 3a6e0a2034..eae0c3ffe7 100644
--- a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_wire_json_compatible_type/3.proto
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_field_wire_json_compatible_type/3.proto
@@ -7,4 +7,18 @@ message Message {
Message delimited = 2;
Message normal_b = 3;
Message delimited_b = 4 [features.message_encoding=DELIMITED];
+
+ extensions 100 to 1000;
+
+ extend Message {
+ bytes str = 100;
+ Message msg = 101 [features.message_encoding=DELIMITED];
+ uint64 uint64 = 102;
+ }
+}
+
+extend Message {
+ string str = 200;
+ Message msg = 201;
+ sint32 sint32 = 202;
}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_package_extension_no_delete/1.proto b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_package_extension_no_delete/1.proto
new file mode 100644
index 0000000000..c4b0a38d51
--- /dev/null
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_package_extension_no_delete/1.proto
@@ -0,0 +1,37 @@
+syntax = "proto3";
+
+package a;
+
+message Two {
+ int32 one = 1;
+ int32 two = 2;
+ int32 three = 3;
+}
+
+message Three {
+ message Four {
+ message Five {
+ int32 one = 1;
+ int32 two = 2;
+ int32 three = 3;
+ }
+ message Six {
+ int32 one = 1;
+ int32 two = 2;
+ int32 three = 3;
+ }
+ }
+ message Seven {
+ int32 one = 1;
+ int32 two = 2;
+ int32 three = 3;
+ }
+ message Eight {
+ int32 one = 1;
+ int32 two = 2;
+ int32 three = 3;
+ }
+ int32 one = 1;
+ int32 two = 2;
+ int32 three = 3;
+}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_package_extension_no_delete/2.proto b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_package_extension_no_delete/2.proto
new file mode 100644
index 0000000000..5fa7742702
--- /dev/null
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_package_extension_no_delete/2.proto
@@ -0,0 +1,25 @@
+syntax = "proto2";
+
+package a;
+
+import "3.proto";
+import "4.proto";
+
+message One {
+ optional int32 one = 1;
+ optional int32 two = 2;
+}
+
+message Nine {
+ optional int32 one = 1;
+ optional int32 three = 3;
+}
+
+extend Foo {
+ optional bytes meta = 20;
+ optional Foo ch = 22;
+}
+
+extend b.Fizz {
+ optional b.Fizz child = 22;
+}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_package_extension_no_delete/3.proto b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_package_extension_no_delete/3.proto
new file mode 100644
index 0000000000..5fe583c710
--- /dev/null
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_package_extension_no_delete/3.proto
@@ -0,0 +1,20 @@
+syntax = "proto2";
+
+package b;
+
+message Fizz {
+ extensions 10 to 100;
+
+ message Buzz {
+ optional int32 len = 1;
+
+ extend Fizz {
+ optional string str = 10;
+ }
+ }
+}
+
+extend Fizz {
+ optional bytes meta = 20;
+ repeated uint64 tags = 21;
+}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_package_extension_no_delete/4.proto b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_package_extension_no_delete/4.proto
new file mode 100644
index 0000000000..721ba92620
--- /dev/null
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_package_extension_no_delete/4.proto
@@ -0,0 +1,16 @@
+syntax = "proto2";
+
+package a;
+
+message Foo {
+ extensions 10 to 100;
+
+ message Bar {
+ optional int32 len = 1;
+
+ extend Foo {
+ optional string str = 10;
+ repeated string labels = 11;
+ }
+ }
+}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_package_extension_no_delete/buf.yaml b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_package_extension_no_delete/buf.yaml
new file mode 100644
index 0000000000..a4b55888dc
--- /dev/null
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata/breaking_package_extension_no_delete/buf.yaml
@@ -0,0 +1,4 @@
+version: v2
+breaking:
+ use:
+ - PACKAGE_EXTENSION_NO_DELETE
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_extension_no_delete/1.proto b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_extension_no_delete/1.proto
new file mode 100644
index 0000000000..e44c469ede
--- /dev/null
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_extension_no_delete/1.proto
@@ -0,0 +1,49 @@
+syntax = "proto3";
+
+package a;
+
+message One {
+ int32 one = 1;
+ int32 two = 2;
+ int32 three = 3;
+}
+
+message Two {
+ int32 one = 1;
+ int32 two = 2;
+ int32 three = 3;
+}
+
+message Three {
+ message Four {
+ message Five {
+ int32 one = 1;
+ int32 two = 2;
+ int32 three = 3;
+ }
+ message Six {
+ int32 one = 1;
+ int32 two = 2;
+ int32 three = 3;
+ }
+ }
+ message Seven {
+ int32 one = 1;
+ int32 two = 2;
+ int32 three = 3;
+ }
+ message Eight {
+ int32 one = 1;
+ int32 two = 2;
+ int32 three = 3;
+ }
+ int32 one = 1;
+ int32 two = 2;
+ int32 three = 3;
+}
+
+message Nine {
+ int32 one = 1;
+ int32 two = 2;
+ int32 three = 3;
+}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_extension_no_delete/2.proto b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_extension_no_delete/2.proto
new file mode 100644
index 0000000000..d9826fc340
--- /dev/null
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_extension_no_delete/2.proto
@@ -0,0 +1,22 @@
+syntax = "proto2";
+
+package a;
+
+message Foo {
+ extensions 10 to 100;
+
+ message Bar {
+ optional int32 len = 1;
+
+ extend Foo {
+ optional string str = 10;
+ repeated string labels = 11;
+ }
+ }
+}
+
+extend Foo {
+ optional bytes meta = 20;
+ repeated uint64 tags = 21;
+ optional Foo ch = 22;
+}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_extension_no_delete/3.proto b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_extension_no_delete/3.proto
new file mode 100644
index 0000000000..af0b4ef3f4
--- /dev/null
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_extension_no_delete/3.proto
@@ -0,0 +1,22 @@
+syntax = "proto2";
+
+package b;
+
+message Fizz {
+ extensions 10 to 100;
+
+ message Buzz {
+ optional int32 len = 1;
+
+ extend Fizz {
+ optional string str = 10;
+ repeated string labels = 11;
+ }
+ }
+}
+
+extend Fizz {
+ optional bytes meta = 20;
+ repeated uint64 tags = 21;
+ optional Fizz child = 22;
+}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_no_delete/3.proto b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_no_delete/3.proto
new file mode 100644
index 0000000000..aabbf2b6d3
--- /dev/null
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_no_delete/3.proto
@@ -0,0 +1,21 @@
+syntax = "proto2";
+
+package a;
+
+message Foo {
+ extensions 10 to 100;
+
+ message Bar {
+ optional int32 len = 1;
+
+ extend Foo {
+ optional string str = 10;
+ repeated string labels = 11;
+ }
+ }
+}
+
+extend Foo {
+ optional bytes meta = 20;
+ repeated uint64 tags = 21;
+}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_no_delete_unless_name_reserved/3.proto b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_no_delete_unless_name_reserved/3.proto
new file mode 100644
index 0000000000..aabbf2b6d3
--- /dev/null
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_no_delete_unless_name_reserved/3.proto
@@ -0,0 +1,21 @@
+syntax = "proto2";
+
+package a;
+
+message Foo {
+ extensions 10 to 100;
+
+ message Bar {
+ optional int32 len = 1;
+
+ extend Foo {
+ optional string str = 10;
+ repeated string labels = 11;
+ }
+ }
+}
+
+extend Foo {
+ optional bytes meta = 20;
+ repeated uint64 tags = 21;
+}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_no_delete_unless_number_reserved/3.proto b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_no_delete_unless_number_reserved/3.proto
new file mode 100644
index 0000000000..aabbf2b6d3
--- /dev/null
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_no_delete_unless_number_reserved/3.proto
@@ -0,0 +1,21 @@
+syntax = "proto2";
+
+package a;
+
+message Foo {
+ extensions 10 to 100;
+
+ message Bar {
+ optional int32 len = 1;
+
+ extend Foo {
+ optional string str = 10;
+ repeated string labels = 11;
+ }
+ }
+}
+
+extend Foo {
+ optional bytes meta = 20;
+ repeated uint64 tags = 21;
+}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_cardinality/3.proto b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_cardinality/3.proto
new file mode 100644
index 0000000000..aabbf2b6d3
--- /dev/null
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_cardinality/3.proto
@@ -0,0 +1,21 @@
+syntax = "proto2";
+
+package a;
+
+message Foo {
+ extensions 10 to 100;
+
+ message Bar {
+ optional int32 len = 1;
+
+ extend Foo {
+ optional string str = 10;
+ repeated string labels = 11;
+ }
+ }
+}
+
+extend Foo {
+ optional bytes meta = 20;
+ repeated uint64 tags = 21;
+}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_cpp_string_type/2.proto b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_cpp_string_type/2.proto
index 761ba4f594..995cb27d66 100644
--- a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_cpp_string_type/2.proto
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_cpp_string_type/2.proto
@@ -33,4 +33,20 @@ message Bar {
string default5 = 35;
bytes default6 = 36;
repeated string default7 = 37;
+
+ extensions 100 to 300;
+
+ message Frobnitz {
+ extend Bar {
+ repeated string ext_str1 = 100;
+ string ext_str2 = 101 [ctype=STRING_PIECE];
+ repeated bytes ext_byt1 = 102 [features.(pb.cpp).string_type=VIEW];
+ bytes ext_byt2 = 103;
+ }
+ }
}
+
+extend Bar {
+ repeated string ext_str1 = 200 [features.(pb.cpp).string_type=STRING];
+ repeated string ext_str2 = 201;
+}
\ No newline at end of file
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_cpp_string_type/3.proto b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_cpp_string_type/3.proto
index 87442acd54..291719237f 100644
--- a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_cpp_string_type/3.proto
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_cpp_string_type/3.proto
@@ -18,4 +18,19 @@ message Baz {
map map1 = 11;
map map2 = 12;
map map3 = 13;
+
+ extensions 100 to 200;
+
+ message Fizzbuzz {
+ extend Baz {
+ repeated string ext_str1 = 100;
+ string ext_str2 = 101 [ctype=STRING_PIECE];
+ repeated bytes ext_byt1 = 102 [features.(pb.cpp).string_type=VIEW];
+ bytes ext_byt2 = 103;
+ }
+ }
+}
+
+extend Baz {
+ repeated string ext_str = 200;
}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_ctype/2.proto b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_ctype/2.proto
index 072de89958..492288a991 100644
--- a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_ctype/2.proto
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_ctype/2.proto
@@ -39,3 +39,11 @@ message Nine2 {
optional string one = 1 [ctype = STRING_PIECE];
required string two = 2 [ctype = CORD];
}
+
+message Ten2 {
+ extensions 1 to 100;
+}
+
+extend Ten2 {
+ optional string ten_one = 1 [ctype = STRING_PIECE];
+}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_default/1.proto b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_default/1.proto
index 8876de0551..49e7becc79 100644
--- a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_default/1.proto
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_default/1.proto
@@ -133,4 +133,24 @@ message Baz {
repeated uint64 uints = 3;
optional Bar bar = 4;
repeated Bar bars = 5;
+
+ extensions 100 to 200;
+
+ extend Baz {
+ optional string s = 200;
+ optional bool b = 199 [default = true];
+ optional fixed32 f32 = 198 [default = 123];
+ optional sint64 s64 = 197 [default = -456];
+ optional float f = 196 [default = 0.1020304];
+ }
+}
+
+extend Baz {
+ optional uint32 u1 = 100 [default = 3456];
+ optional uint32 u2 = 101 [default = 9876];
+ optional bytes b1 = 102 [default = "abcdef"];
+ optional bytes b2 = 103 [default = "a1b2c3"];
+ repeated Baz bb1 = 104;
+ optional string s1 = 105 [default = "abc"];
+ optional string s2 = 106 [default = "xyz"];
}
\ No newline at end of file
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_default/2.proto b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_default/2.proto
index 41d7dd44a0..b0daf4439e 100644
--- a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_default/2.proto
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_default/2.proto
@@ -2,6 +2,8 @@ syntax = "proto3";
package a;
+import "google/protobuf/descriptor.proto";
+
message Fizz {
string s1 = 1;
string s2 = 2;
@@ -97,3 +99,9 @@ message Buzz {
Buzz buzz = 4;
repeated Buzz buzzes = 5;
}
+
+extend google.protobuf.MessageOptions {
+ uint32 num = 10000;
+ repeated string strings = 10101;
+ Buzz buzz = 20202;
+}
\ No newline at end of file
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_java_utf8_validation/1.proto b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_java_utf8_validation/1.proto
index 1914278342..fb266a0d65 100644
--- a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_java_utf8_validation/1.proto
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_java_utf8_validation/1.proto
@@ -1,8 +1,8 @@
syntax = "proto2";
-
package a;
message A1 {
+ extensions 100 to 1000;
optional string s1 = 1;
repeated string s2 = 2;
map s3 = 3;
@@ -66,4 +66,16 @@ message A6 {
repeated double other2 = 12;
map other3 = 13;
optional A6 other4 = 14;
+
+ extensions 100 to 1000;
+
+ extend A1 {
+ optional string a1_s1 = 100;
+ repeated string a1_s2 = 101;
+ }
+}
+
+extend A6 {
+ optional string a6_s1 = 100;
+ repeated string a6_s2 = 101;
}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_java_utf8_validation/2.proto b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_java_utf8_validation/2.proto
index a81733ef26..4614e98d7e 100644
--- a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_java_utf8_validation/2.proto
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_java_utf8_validation/2.proto
@@ -1,10 +1,10 @@
syntax = "proto2";
-
package a;
option java_string_check_utf8 = true;
message B1 {
+ extensions 100 to 1000;
optional string s1 = 1;
repeated string s2 = 2;
map s3 = 3;
@@ -68,4 +68,16 @@ message B6 {
repeated double other2 = 12;
map other3 = 13;
optional B6 other4 = 14;
+
+ extensions 100 to 1000;
+
+ extend B1 {
+ optional string b1_s1 = 100;
+ repeated string b1_s2 = 101;
+ }
+}
+
+extend B6 {
+ optional string b6_s1 = 100;
+ repeated string b6_s2 = 101;
}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_java_utf8_validation/4.proto b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_java_utf8_validation/4.proto
index ac264e3b7f..5b77348220 100644
--- a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_java_utf8_validation/4.proto
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_java_utf8_validation/4.proto
@@ -1,10 +1,10 @@
edition = "2023";
-
package a;
import "google/protobuf/java_features.proto";
message D1 {
+ extensions 100 to 1000;
string s1 = 1;
repeated string s2 = 2;
map s3 = 3;
@@ -68,6 +68,18 @@ message D6 {
repeated double other2 = 12;
map other3 = 13;
D6 other4 = 14;
+
+ extensions 100 to 1000;
+
+ extend D1 {
+ string d1_s1 = 100 [features.(pb.java).utf8_validation=VERIFY];
+ repeated string d1_s2 = 101;
+ }
+}
+
+extend D6 {
+ string d6_s1 = 100 [features.(pb.java).utf8_validation=VERIFY];
+ repeated string d6_s2 = 101;
}
message D7 {
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_java_utf8_validation/5.proto b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_java_utf8_validation/5.proto
index c1edc6f4a8..a7564be7eb 100644
--- a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_java_utf8_validation/5.proto
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_java_utf8_validation/5.proto
@@ -1,5 +1,4 @@
edition = "2023";
-
package a;
import "google/protobuf/java_features.proto";
@@ -7,6 +6,7 @@ import "google/protobuf/java_features.proto";
option features.(pb.java).utf8_validation = VERIFY;
message E1 {
+ extensions 100 to 1000;
string s1 = 1;
repeated string s2 = 2;
map s3 = 3;
@@ -70,6 +70,18 @@ message E6 {
repeated double other2 = 12;
map other3 = 13;
E6 other4 = 14;
+
+ extensions 100 to 1000;
+
+ extend E1 {
+ string e1_s1 = 100 [features.(pb.java).utf8_validation=DEFAULT];
+ repeated string e1_s2 = 101;
+ }
+}
+
+extend E6 {
+ string e6_s1 = 100 [features.(pb.java).utf8_validation=DEFAULT];
+ repeated string e6_s2 = 101;
}
message E7 {
@@ -92,4 +104,6 @@ message E8 {
repeated double other2 = 12;
map other3 = 13;
E8 other4 = 14;
+
+ extensions 100 to 1000;
}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_java_utf8_validation/6.proto b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_java_utf8_validation/6.proto
index 9666b11da4..3bc4318e0f 100644
--- a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_java_utf8_validation/6.proto
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_java_utf8_validation/6.proto
@@ -1,5 +1,4 @@
edition = "2023";
-
package a;
import "google/protobuf/java_features.proto";
@@ -8,6 +7,7 @@ option features.utf8_validation = NONE;
option features.(pb.java).utf8_validation = VERIFY;
message F1 {
+ extensions 100 to 1000;
string s1 = 1;
repeated string s2 = 2;
map s3 = 3;
@@ -71,6 +71,18 @@ message F6 {
repeated double other2 = 12;
map other3 = 13;
F6 other4 = 14;
+
+ extensions 100 to 1000;
+
+ extend F1 {
+ string f1_s1 = 100 [features.(pb.java).utf8_validation=DEFAULT];
+ repeated string f1_s2 = 101;
+ }
+}
+
+extend F6 {
+ string f6_s1 = 100 [features.(pb.java).utf8_validation=DEFAULT];
+ repeated string f6_s2 = 101;
}
message F7 {
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_jstype/1.proto b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_jstype/1.proto
index 519ec3a5d3..0f4cb0c7d1 100644
--- a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_jstype/1.proto
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_jstype/1.proto
@@ -1,6 +1,6 @@
syntax = "proto3";
-
package a;
+import "google/protobuf/descriptor.proto";
message One {
int64 one = 1 [jstype = JS_NORMAL];
@@ -38,4 +38,12 @@ message Three {
message Nine {
int64 one = 1 [jstype = JS_NORMAL];
int64 two = 2 [jstype = JS_STRING];
+
+ extend google.protobuf.FileOptions {
+ uint64 f1 = 10001;
+ sint64 f2 = 10002 [jstype = JS_STRING];
+ int64 f3 = 10003 [jstype = JS_NUMBER];
+ fixed64 f4 = 10004 [jstype = JS_NORMAL];
+ sfixed64 f5 = 10005 [jstype = JS_STRING];
+ }
}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_jstype/2.proto b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_jstype/2.proto
index 8629da4224..307cc03058 100644
--- a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_jstype/2.proto
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_jstype/2.proto
@@ -38,4 +38,17 @@ message Three2 {
message Nine2 {
optional int64 one = 1 [jstype = JS_NUMBER];
required int64 two = 2 [jstype = JS_STRING];
+
+ extensions 100 to 1000;
+}
+
+extend Nine2 {
+ optional sint64 s1 = 101 [jstype = JS_NORMAL];
+ optional sint64 s2 = 102 [jstype = JS_NUMBER];
+ optional sint64 s3 = 103 [jstype = JS_STRING];
+ optional sint64 s4 = 104;
+ optional sint64 s5 = 105;
+ optional sint64 s6 = 106 [jstype = JS_STRING];
+ optional sint64 s7 = 107 [jstype = JS_NUMBER];
+ optional sint64 s8 = 108 [jstype = JS_NORMAL];
}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_name/1.proto b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_name/1.proto
index e44c469ede..ce7c0f65f8 100644
--- a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_name/1.proto
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_name/1.proto
@@ -1,6 +1,6 @@
syntax = "proto3";
-
package a;
+import "google/protobuf/descriptor.proto";
message One {
int32 one = 1;
@@ -46,4 +46,12 @@ message Nine {
int32 one = 1;
int32 two = 2;
int32 three = 3;
+
+ extend google.protobuf.MessageOptions {
+ string msg_str_opt = 10101;
+ }
+}
+
+extend google.protobuf.FileOptions {
+ string file_str_opt = 10101;
}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_name/2.proto b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_name/2.proto
index ea9ae23a30..8b7a70bf7f 100644
--- a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_name/2.proto
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_name/2.proto
@@ -1,49 +1,64 @@
-syntax = "proto3";
+syntax = "proto2";
package a;
message One2 {
- int32 one = 1;
- int32 two = 2;
- int32 three = 3;
+ optional int32 one = 1;
+ optional int32 two = 2;
+ optional int32 three = 3;
}
message Two2 {
- int32 one = 1;
- int32 two = 2;
- int32 three = 3;
+ optional int32 one = 1;
+ optional int32 two = 2;
+ optional int32 three = 3;
}
message Three2 {
message Four2 {
message Five2 {
- int32 one = 1;
- int32 two = 2;
- int32 three = 3;
+ optional int32 one = 1;
+ optional int32 two = 2;
+ optional int32 three = 3;
}
message Six2 {
- int32 one = 1;
- int32 two = 2;
- int32 three = 3;
+ optional int32 one = 1;
+ optional int32 two = 2;
+ optional int32 three = 3;
}
}
message Seven2 {
- int32 one = 1;
- int32 two = 2;
- int32 three = 3;
+ optional int32 one = 1;
+ optional int32 two = 2;
+ optional int32 three = 3;
}
message Eight2 {
- int32 one = 1;
- int32 two = 2;
- int32 three = 3;
+ optional int32 one = 1;
+ optional int32 two = 2;
+ optional int32 three = 3;
+ extend Three2 {
+ repeated uint64 uint_opt = 101;
+ optional string str_opt = 102;
+ optional bool bool_opt = 103;
+ }
}
- int32 one = 1;
- int32 two = 2;
- int32 three = 3;
+ optional int32 one = 1;
+ optional int32 two = 2;
+ optional int32 three = 3;
+
+ extensions 100 to 1000;
}
message Nine2 {
- int32 one = 1;
- int32 two = 2;
- int32 three = 3;
+ optional int32 one = 1;
+ optional int32 two = 2;
+ optional int32 three = 3;
+
+ extensions 100 to 1000;
+}
+
+extend Nine2 {
+ repeated uint64 uint_opt = 101;
+ optional string str_opt = 102;
+ optional bool bool_opt = 103;
}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_type/1.proto b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_type/1.proto
index ea1190d417..16112834ea 100644
--- a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_type/1.proto
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_type/1.proto
@@ -1,6 +1,6 @@
syntax = "proto3";
-
package a;
+import "google/protobuf/descriptor.proto";
message One {
int32 one = 1;
@@ -68,3 +68,7 @@ message Ten {
int64 one = 1;
int64 two = 2;
}
+
+extend google.protobuf.FieldOptions {
+ repeated Ten ten_option = 10101;
+}
\ No newline at end of file
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_type/2.proto b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_type/2.proto
index 3153e6a452..236885969e 100644
--- a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_type/2.proto
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_type/2.proto
@@ -57,4 +57,12 @@ message Nine2 {
message Ten2 {
optional group Foo = 1 {
}
+
+ extensions 100 to 1000;
+
+ extend Ten2 {
+ optional string ten2_str = 100;
+ optional Ten2 ten2_msg = 101;
+ optional uint32 ten2_uint32 = 102;
+ }
}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_type/3.proto b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_type/3.proto
index 45c2618d23..c65eee157a 100644
--- a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_type/3.proto
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_same_type/3.proto
@@ -7,4 +7,12 @@ message One3 {
One3 delimited = 2 [features.message_encoding=DELIMITED];
One3 normal_b = 3;
One3 delimited_b = 4 [features.message_encoding=DELIMITED];
+
+ extensions 100 to 1000;
}
+
+extend One3 {
+ string one3_str = 100;
+ One3 one3_msg = 101;
+ uint32 one3_uint32 = 102;
+}
\ No newline at end of file
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_wire_compatible_cardinality/3.proto b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_wire_compatible_cardinality/3.proto
new file mode 100644
index 0000000000..aabbf2b6d3
--- /dev/null
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_wire_compatible_cardinality/3.proto
@@ -0,0 +1,21 @@
+syntax = "proto2";
+
+package a;
+
+message Foo {
+ extensions 10 to 100;
+
+ message Bar {
+ optional int32 len = 1;
+
+ extend Foo {
+ optional string str = 10;
+ repeated string labels = 11;
+ }
+ }
+}
+
+extend Foo {
+ optional bytes meta = 20;
+ repeated uint64 tags = 21;
+}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_wire_compatible_type/1.proto b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_wire_compatible_type/1.proto
index b8ac336ef9..a09d820120 100644
--- a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_wire_compatible_type/1.proto
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_wire_compatible_type/1.proto
@@ -1,6 +1,6 @@
syntax = "proto3";
-
package a;
+import "google/protobuf/descriptor.proto";
message CompatiblePrimitives {
int32 int32_field_1 = 1;
@@ -83,3 +83,9 @@ message StringBytes {
string string_field_1 = 1;
bytes bytes_field_1 = 2;
}
+
+extend google.protobuf.FieldOptions {
+ repeated Bar bar_option = 10101;
+ string str_option = 10102;
+ bytes byt_option = 10103;
+}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_wire_compatible_type/2.proto b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_wire_compatible_type/2.proto
index 0fe8765bd1..35545844a8 100644
--- a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_wire_compatible_type/2.proto
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_wire_compatible_type/2.proto
@@ -7,4 +7,18 @@ message Message {
Message delimited = 2 [features.message_encoding=DELIMITED];
Message normal_b = 3;
Message delimited_b = 4 [features.message_encoding=DELIMITED];
+
+ extensions 100 to 1000;
+
+ extend Message {
+ string str = 100;
+ Message msg = 101;
+ uint32 uint32 = 102;
+ }
+}
+
+extend Message {
+ string str = 200;
+ Message msg = 201;
+ uint32 uint32 = 202;
}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_wire_json_compatible_cardinality/3.proto b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_wire_json_compatible_cardinality/3.proto
new file mode 100644
index 0000000000..aabbf2b6d3
--- /dev/null
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_wire_json_compatible_cardinality/3.proto
@@ -0,0 +1,21 @@
+syntax = "proto2";
+
+package a;
+
+message Foo {
+ extensions 10 to 100;
+
+ message Bar {
+ optional int32 len = 1;
+
+ extend Foo {
+ optional string str = 10;
+ repeated string labels = 11;
+ }
+ }
+}
+
+extend Foo {
+ optional bytes meta = 20;
+ repeated uint64 tags = 21;
+}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_wire_json_compatible_type/1.proto b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_wire_json_compatible_type/1.proto
index db70d36483..cc6a5142f2 100644
--- a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_wire_json_compatible_type/1.proto
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_wire_json_compatible_type/1.proto
@@ -1,6 +1,6 @@
syntax = "proto3";
-
package a;
+import "google/protobuf/descriptor.proto";
message CompatiblePrimitives {
int32 int32_field_1 = 1;
@@ -86,3 +86,9 @@ message StringBytes {
string string_field_1 = 1;
bytes bytes_field_1 = 2;
}
+
+extend google.protobuf.FieldOptions {
+ repeated Bar bar_option = 10101;
+ string str_option = 10102;
+ bytes byt_option = 10103;
+}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_wire_json_compatible_type/2.proto b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_wire_json_compatible_type/2.proto
index 0fe8765bd1..35545844a8 100644
--- a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_wire_json_compatible_type/2.proto
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_field_wire_json_compatible_type/2.proto
@@ -7,4 +7,18 @@ message Message {
Message delimited = 2 [features.message_encoding=DELIMITED];
Message normal_b = 3;
Message delimited_b = 4 [features.message_encoding=DELIMITED];
+
+ extensions 100 to 1000;
+
+ extend Message {
+ string str = 100;
+ Message msg = 101;
+ uint32 uint32 = 102;
+ }
+}
+
+extend Message {
+ string str = 200;
+ Message msg = 201;
+ uint32 uint32 = 202;
}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_package_extension_no_delete/1.proto b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_package_extension_no_delete/1.proto
new file mode 100644
index 0000000000..e44c469ede
--- /dev/null
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_package_extension_no_delete/1.proto
@@ -0,0 +1,49 @@
+syntax = "proto3";
+
+package a;
+
+message One {
+ int32 one = 1;
+ int32 two = 2;
+ int32 three = 3;
+}
+
+message Two {
+ int32 one = 1;
+ int32 two = 2;
+ int32 three = 3;
+}
+
+message Three {
+ message Four {
+ message Five {
+ int32 one = 1;
+ int32 two = 2;
+ int32 three = 3;
+ }
+ message Six {
+ int32 one = 1;
+ int32 two = 2;
+ int32 three = 3;
+ }
+ }
+ message Seven {
+ int32 one = 1;
+ int32 two = 2;
+ int32 three = 3;
+ }
+ message Eight {
+ int32 one = 1;
+ int32 two = 2;
+ int32 three = 3;
+ }
+ int32 one = 1;
+ int32 two = 2;
+ int32 three = 3;
+}
+
+message Nine {
+ int32 one = 1;
+ int32 two = 2;
+ int32 three = 3;
+}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_package_extension_no_delete/2.proto b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_package_extension_no_delete/2.proto
new file mode 100644
index 0000000000..d9826fc340
--- /dev/null
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_package_extension_no_delete/2.proto
@@ -0,0 +1,22 @@
+syntax = "proto2";
+
+package a;
+
+message Foo {
+ extensions 10 to 100;
+
+ message Bar {
+ optional int32 len = 1;
+
+ extend Foo {
+ optional string str = 10;
+ repeated string labels = 11;
+ }
+ }
+}
+
+extend Foo {
+ optional bytes meta = 20;
+ repeated uint64 tags = 21;
+ optional Foo ch = 22;
+}
diff --git a/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_package_extension_no_delete/3.proto b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_package_extension_no_delete/3.proto
new file mode 100644
index 0000000000..af0b4ef3f4
--- /dev/null
+++ b/private/bufpkg/bufcheck/bufbreaking/testdata_previous/breaking_package_extension_no_delete/3.proto
@@ -0,0 +1,22 @@
+syntax = "proto2";
+
+package b;
+
+message Fizz {
+ extensions 10 to 100;
+
+ message Buzz {
+ optional int32 len = 1;
+
+ extend Fizz {
+ optional string str = 10;
+ repeated string labels = 11;
+ }
+ }
+}
+
+extend Fizz {
+ optional bytes meta = 20;
+ repeated uint64 tags = 21;
+ optional Fizz child = 22;
+}
diff --git a/private/bufpkg/bufprotosource/bufprotosource.go b/private/bufpkg/bufprotosource/bufprotosource.go
index ceca0153c6..86f9570f01 100644
--- a/private/bufpkg/bufprotosource/bufprotosource.go
+++ b/private/bufpkg/bufprotosource/bufprotosource.go
@@ -121,6 +121,7 @@ type NamedDescriptor interface {
type ContainerDescriptor interface {
Enums() []Enum
Messages() []Message
+ Extensions() []Field
}
// OptionExtensionDescriptor contains options and option extensions.
@@ -585,7 +586,7 @@ func PackageToFiles(files ...File) (map[string][]File, error) {
// ForEachEnum calls f on each Enum in the given ContainerDescriptor, including nested Enums.
//
-// Returns error and stops iterating if f returns error
+// Returns error and stops iterating if f returns error.
// Never returns error unless f returns error.
func ForEachEnum(f func(Enum) error, containerDescriptor ContainerDescriptor) error {
for _, enum := range containerDescriptor.Enums() {
@@ -601,6 +602,25 @@ func ForEachEnum(f func(Enum) error, containerDescriptor ContainerDescriptor) er
return nil
}
+// ForEachExtension calls f on each extension Field in the given ContainerDescriptor,
+// including nested extensions.
+//
+// Returns error and stops iterating if f returns error.
+// Never returns error unless f returns error.
+func ForEachExtension(f func(Field) error, containerDescriptor ContainerDescriptor) error {
+ for _, extension := range containerDescriptor.Extensions() {
+ if err := f(extension); err != nil {
+ return err
+ }
+ }
+ for _, message := range containerDescriptor.Messages() {
+ if err := ForEachExtension(f, message); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
// ForEachMessage calls f on each Message in the given ContainerDescriptor, including nested Messages.
//
// Returns error and stops iterating if f returns error
@@ -640,6 +660,30 @@ func NestedNameToEnum(containerDescriptor ContainerDescriptor) (map[string]Enum,
return nestedNameToEnum, nil
}
+// NestedNameToExtension maps the Enums in the ContainerDescriptor to a map from
+// nested name to extension Field.
+//
+// Returns error if extensions do not have unique nested names within the
+// ContainerDescriptor, which should generally never happen for properly-formed
+// ContainerDescriptors.
+func NestedNameToExtension(containerDescriptor ContainerDescriptor) (map[string]Field, error) {
+ nestedNameToExtension := make(map[string]Field)
+ if err := ForEachExtension(
+ func(extension Field) error {
+ nestedName := extension.NestedName()
+ if _, ok := nestedNameToExtension[nestedName]; ok {
+ return fmt.Errorf("duplicate extension: %q", nestedName)
+ }
+ nestedNameToExtension[nestedName] = extension
+ return nil
+ },
+ containerDescriptor,
+ ); err != nil {
+ return nil, err
+ }
+ return nestedNameToExtension, nil
+}
+
// FullNameToEnum maps the Enums in the Files to a map from full name to enum.
//
// Returns error if the Enums do not have unique full names within the Files,
@@ -695,6 +739,37 @@ func PackageToNestedNameToEnum(files ...File) (map[string]map[string]Enum, error
return packageToNestedNameToEnum, nil
}
+// PackageToNestedNameToExtension maps the extension Fields in the Files to a map
+// from package to nested name to Field.
+//
+// Returns error if the extension do not have unique nested names within the packages,
+// which should generally never happen for properly-formed Files.
+func PackageToNestedNameToExtension(files ...File) (map[string]map[string]Field, error) {
+ packageToNestedNameToExtension := make(map[string]map[string]Field)
+ for _, file := range files {
+ if err := ForEachExtension(
+ func(enum Field) error {
+ pkg := enum.File().Package()
+ nestedName := enum.NestedName()
+ nestedNameToExtension, ok := packageToNestedNameToExtension[pkg]
+ if !ok {
+ nestedNameToExtension = make(map[string]Field)
+ packageToNestedNameToExtension[pkg] = nestedNameToExtension
+ }
+ if _, ok := nestedNameToExtension[nestedName]; ok {
+ return fmt.Errorf("duplicate extension in package %q: %q", pkg, nestedName)
+ }
+ nestedNameToExtension[nestedName] = enum
+ return nil
+ },
+ file,
+ ); err != nil {
+ return nil, err
+ }
+ }
+ return packageToNestedNameToExtension, nil
+}
+
// NameToEnumValue maps the EnumValues in the Enum to a map from name to EnumValue.
//
// Returns error if the EnumValues do not have unique names within the Enum,
@@ -815,8 +890,7 @@ func PackageToNestedNameToMessage(files ...File) (map[string]map[string]Message,
// NumberToMessageField maps the Fields in the Message to a map from number to Field.
//
-// TODO: is this right?
-// Includes extensions.
+// Does not includes extensions.
//
// Returns error if the Fields do not have unique numbers within the Message,
// which should generally never happen for properly-formed Messages.
@@ -829,27 +903,13 @@ func NumberToMessageField(message Message) (map[int]Field, error) {
}
numberToMessageField[number] = messageField
}
- for _, messageField := range message.Extensions() {
- if messageField.Extendee() != message.FullName() {
- // TODO: ideally we want this field to be returned when
- // the Extendee message is passed into some function,
- // need to investigate what index is necessary for that.
- continue
- }
- number := messageField.Number()
- if _, ok := numberToMessageField[number]; ok {
- return nil, fmt.Errorf("duplicate message field: %d", number)
- }
- numberToMessageField[number] = messageField
- }
return numberToMessageField, nil
}
// NumberToMessageFieldForLabel maps the Fields with the given label in the message
// to a map from number to Field.
//
-// TODO: is this right?
-// Includes extensions.
+// Does not includes extensions.
//
// Returns error if the Fields do not have unique numbers within the Message,
// which should generally never happen for properly-formed Messages.