Internal details
Implementation strategy
- [DONE hackily] Figure out what names used in the module are being used to refer to bindings in global scope (as opposed to e.g. shadowing globals).
- We do this by parsing the code (thanks to JuliaSyntax), then reimplementing scoping rules on top of the parse tree
- This is finicky, but assuming scoping doesn't change, should be robust enough (once the long tail of edge cases are dealt with...)
- Currently, I don't handle the
global
keyword, so those may look like local variables and confuse things
- Currently, I don't handle the
- This means we need access to the raw source code;
pathof
works well for packages, but for local modules one has to pass the path themselves. Also doesn't seem to work well for stdlibs in the sysimage
- [DONE] Figure out what implicit imports are available in the module, and which module they come from
- done, via a magic
ccall
from Discourse, andBase.which
.
- done, via a magic
- [DONE] Figure out which names have been explicitly imported already
- Done via parsing
Then we can put this information together to figure out what names are actually being used from other modules, and whose usage could be made explicit, and also which existing explicit imports are not being used.
Internals
ExplicitImports.find_implicit_imports
— Functionfind_implicit_imports(mod::Module; skip=(mod, Base, Core))
Given a module mod
, returns a Dict{Symbol, @NamedTuple{source::Module,exporters::Vector{Module}}}
showing names exist in mod
's namespace which are available due to implicit exports by other modules. The dict's keys are those names, and the values are the source module that the name comes from, along with the modules which export the same binding that are available in mod
due to implicit imports.
In the case of ambiguities (two modules exporting the same name), the name is unavailable in the module, and hence the name will not be present in the dict.
This is powered by Base.which
.
ExplicitImports.get_names_used
— Functionget_names_used(file) -> FileAnalysis
Figures out which global names are used in file
, and what modules they are used within.
Traverses static include
statements.
Returns a FileAnalysis
object.
ExplicitImports.analyze_all_names
— Functionanalyze_all_names(file)
Returns a tuple of two items:
per_usage_info
: a table containing information about each name each time it was useduntainted_modules
: a set containing modules found and analyzed successfully
ExplicitImports.inspect_session
— FunctionExplicitImports.inspect_session([io::IO=stdout,]; skip=(Base, Core), inner=print_explicit_imports)
Experimental functionality to call inner
(defaulting to print_explicit_imports
) on each loaded package in the Julia session.
ExplicitImports.FileAnalysis
— TypeFileAnalysis
Contains structured analysis results.
Fields
- perusageinfo::Vector{PerUsageInfo}
needs_explicit_import::Set{@NamedTuple{name::Symbol,module_path::Vector{Symbol}, location::String}}
unnecessary_explicit_import::Set{@NamedTuple{name::Symbol,module_path::Vector{Symbol}, location::String}}
untainted_modules::Set{Vector{Symbol}}
: those which were analyzed and do not contain an unanalyzableinclude