Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Prune imports no longer used by retained entities in the schema #2797

Merged
merged 6 commits into from
Jan 26, 2024

Conversation

mpeyper
Copy link
Collaborator

@mpeyper mpeyper commented Jan 25, 2024

This is a WIP but wanted to get it up to get some feedback on the approach. This started with a discussion with @swankjesse after I discovered that imports are not necessarily being pruned from files when they are no longer needed by retained entities in the schema.

The existing logic appear to retain an imports when the file is being retained in the schema at all, regarless of whether the import is still being used by the file (and not by another file).

This change attempts to resolve the referenced types in the file and resolve them back to their import locations so that the list of actually required imports can be determined.

It's currently passing all existing test cases as well as the new test we added to replicate the issue, but I'm not sure if I'm capturing all ways a type can be referenced at the moment. Any insights into wierd edge cases that I should consider are very welcome.

I'll add some comments in the files where I'm not sure about things.

@mpeyper mpeyper force-pushed the mpeyper/improve-import-pruning branch from b9b57e8 to afbcd98 Compare January 25, 2024 05:54
add(
"message.proto".toPath(),
"""
|import 'footer.proto';
Copy link
Collaborator Author

@mpeyper mpeyper Jan 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Previously this import was being retained even though AnotherMessage was being pruned, along with the need to keep importing it.

As discussed with @swankjesse, this test was good to replicate the issue, but I want to write more rubust cases around the different ways an import can be referenced before we merge this.

@mpeyper mpeyper force-pushed the mpeyper/improve-import-pruning branch from afbcd98 to a0635fb Compare January 25, 2024 06:02
Comment on lines +156 to +161
val typeLocations = referencedTypes.map { type -> type.location }

val extensionLocations = referencedTypes
.filterIsInstance<MessageType>()
.flatMap { type -> type.extensionFields }
.map { field -> field.location }
Copy link
Collaborator Author

@mpeyper mpeyper Jan 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the existing test suite, the locations that have been important to retain are the locations of the referenced types themselves (typeLocations), and the locations of any extensions used (extensionLocations).

Am I missing any other places I should be looking for locations?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's cool


val referencedImports = (typeLocations + extensionLocations).map { it.path }.toSet()

val retainedImports = imports.filter { referencedImports.contains(it) }
Copy link
Collaborator Author

@mpeyper mpeyper Jan 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic here is that I want to ensure that I only remove existing imports and never add new ones. I had a case where google/protobuf/descriptor.proto was being inserted into the imports if I simply used referencedImports as the new retainedImports.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call

@mpeyper mpeyper force-pushed the mpeyper/improve-import-pruning branch from a0635fb to cf5b658 Compare January 25, 2024 06:17
@@ -46,7 +46,9 @@ internal class FileDescriptorGeneratorTest {
|package test;
|import "imported.proto";
|
|message Test {}
|message Test {
| optional Imported imported = 1;
Copy link
Collaborator Author

@mpeyper mpeyper Jan 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A side effect of this change is that descriptor.dependencies.size was 0 without including a usage of the imported file because the unused import is getting pruned away. I'm not sure how much of an issue that is in reality though?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no problem I think

@mpeyper mpeyper force-pushed the mpeyper/improve-import-pruning branch from cf5b658 to f1729a6 Compare January 25, 2024 08:18
Copy link
Member

@oldergod oldergod left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 LGTM

@@ -46,7 +46,9 @@ internal class FileDescriptorGeneratorTest {
|package test;
|import "imported.proto";
|
|message Test {}
|message Test {
| optional Imported imported = 1;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no problem I think

Comment on lines +156 to +161
val typeLocations = referencedTypes.map { type -> type.location }

val extensionLocations = referencedTypes
.filterIsInstance<MessageType>()
.flatMap { type -> type.extensionFields }
.map { field -> field.location }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's cool


val referencedImports = (typeLocations + extensionLocations).map { it.path }.toSet()

val retainedImports = imports.filter { referencedImports.contains(it) }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call

@mpeyper mpeyper force-pushed the mpeyper/improve-import-pruning branch 2 times, most recently from 120072f to a8be91b Compare January 26, 2024 02:43
@mpeyper mpeyper force-pushed the mpeyper/improve-import-pruning branch from a8be91b to a4647cb Compare January 26, 2024 03:01
@mpeyper mpeyper marked this pull request as ready for review January 26, 2024 11:37
@mpeyper
Copy link
Collaborator Author

mpeyper commented Jan 26, 2024

I've added a bunch of tests to test the various ways a type can be referenced and also to test the new import public pruning logic. I think I have all cases covered now and I think this is ok to merge now.

Copy link
Member

@oldergod oldergod left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I pushed a commit to satisfy spotless. Very good tests, thank you

}

@Test
fun retainImportWhenUsedForNestedMessageField() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests are repetitive but also very good

@oldergod oldergod merged commit dc0c8e1 into master Jan 26, 2024
7 checks passed
@oldergod oldergod deleted the mpeyper/improve-import-pruning branch January 26, 2024 14:55
@oldergod
Copy link
Member

🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants