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.