We have a large multi-module Swift codebase where we want to support strict imports, meaning files only import what they use, and they import everything they use (not particularly at the
class level with
import class Foo.bar, but at least
import Foo). We have a few specific reasons for wanting this to be enforced:
- When reading a diff it makes it clear when a new dependency on a module is being introduced
- When this is explicitly defined we can enforce what types of modules are allowed to import what other types of modules, for example we don't want modules with business logic importing our UI layer etc
- By trimming unused imports, we can eliminate unnecessary intermodule dependencies, simplifying the dependency graph and speeding up compilation
We've hit a bunch of roadblocks trying to support this over time and I'm curious what folks think about a solution to this problem could be.
Currently we use SwiftLint's
unused_import rule for this. It works by requesting the cursor info from every location in every source file, and cross referencing that with the imports in the file. Unfortunately this approach has some false negatives due to cases like this bug (I don't want to harp to much on the specific bugs since I'm not sure they'll ever be fixed given source breaking concerns + other reasons I'm probably unaware of). There was also an attempt to switch to using the SourceKit index instead, but it seems there were similar issues there. There are also some other difficulties with these approaches in terms of outlier cases like custom operators, and name collisions from multiple modules defining type with the same name. If the SourceKit index had a more complete set of dependencies containing these outliers, we could rely on it for this kind of tooling.
I've also done a bit of testing with other approaches such as trying to dependencies determine from the symbols in the binary, fetching info in the swiftmodule file, using
-emit-imported-modules, or using
-emit-loaded-module-trace, but I haven't been able to come up with any alternatives that successfully cover the cases mentioned above.
We briefly brainstormed some solutions to this that I would love to have folks input on.
- We could ignore the specifics of this issue, and just try and fix the “bugs” causing these. This would likely make the SourceKit index usable for this use case
- We could make the compiler produce a warning for unused / implicit imports (potentially with a flag if that felt too aggressive by default)
- We could provide another
-emitflag that dumped more complete info about the modules a specific file depended on
Overall I'm curious about a few things:
- Is this something people agree we should solve?
- Do any of the solutions above sound reasonable, are there other potential solutions to solving this?
Thanks to @jpsim for help writing this up