-
Notifications
You must be signed in to change notification settings - Fork 568
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
Conversation
b9b57e8
to
afbcd98
Compare
add( | ||
"message.proto".toPath(), | ||
""" | ||
|import 'footer.proto'; |
There was a problem hiding this comment.
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.
wire-schema/src/commonMain/kotlin/com/squareup/wire/schema/ProtoFile.kt
Outdated
Show resolved
Hide resolved
afbcd98
to
a0635fb
Compare
wire-schema/src/commonMain/kotlin/com/squareup/wire/schema/ProtoFile.kt
Outdated
Show resolved
Hide resolved
val typeLocations = referencedTypes.map { type -> type.location } | ||
|
||
val extensionLocations = referencedTypes | ||
.filterIsInstance<MessageType>() | ||
.flatMap { type -> type.extensionFields } | ||
.map { field -> field.location } |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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) } |
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good call
a0635fb
to
cf5b658
Compare
@@ -46,7 +46,9 @@ internal class FileDescriptorGeneratorTest { | |||
|package test; | |||
|import "imported.proto"; | |||
| | |||
|message Test {} | |||
|message Test { | |||
| optional Imported imported = 1; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no problem I think
cf5b658
to
f1729a6
Compare
There was a problem hiding this 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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no problem I think
val typeLocations = referencedTypes.map { type -> type.location } | ||
|
||
val extensionLocations = referencedTypes | ||
.filterIsInstance<MessageType>() | ||
.flatMap { type -> type.extensionFields } | ||
.map { field -> field.location } |
There was a problem hiding this comment.
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
wire-schema/src/commonMain/kotlin/com/squareup/wire/schema/ProtoFile.kt
Outdated
Show resolved
Hide resolved
wire-schema/src/commonMain/kotlin/com/squareup/wire/schema/ProtoFile.kt
Outdated
Show resolved
Hide resolved
|
||
val referencedImports = (typeLocations + extensionLocations).map { it.path }.toSet() | ||
|
||
val retainedImports = imports.filter { referencedImports.contains(it) } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good call
120072f
to
a8be91b
Compare
a8be91b
to
a4647cb
Compare
I've added a bunch of tests to test the various ways a type can be referenced and also to test the new |
There was a problem hiding this 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() { |
There was a problem hiding this comment.
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
🚀 |
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.