General configurations
JET offers extensive customization options through its configuration system. All entry points covered in JET's default error analysis and the optimization analysis accept the configuration parameters outlined below as keyword arguments (or optional parameters for interactive macros). For instance, you can analyze the call sum("julia") with the sourceinfo configuration set to :full:
@report_call sourceinfo=:full sum("julia")Or equivalently:
report_call(sum, (String,); sourceinfo=:full)You can also analyze a top-level script path/to/file.jl while specifying the target_modules configuration:
report_file("path/to/file.jl";
target_modules = (Main,))The documented objects listed below (such as "JET.configured_reports") represent internal configuration structures. While you won't interact with these objects directly, their documentation describes the available configuration options that you can pass as keyword arguments to JET's analysis functions.
Configurations for analysis result
JET.configured_reports — Function
Configurations for JET's analysis results.
The target_modules and ignored_modules values must be nothing or an iterator whose elements are any of the following matchers for a report::InferenceErrorReport:
m::ModuleorJET.LastFrameModule(m::Module): matches when the module of the report's innermost stack frame ismor one of its submodulesname::SymbolorJET.LastFrameModule(name::Symbol): matches when the module of the report's innermost stack frame, or one of that module's parents, is namednameJET.AnyFrameModule(m::Module): matches when the module of any stack frame ismor one of its submodulesJET.AnyFrameModule(name::Symbol): matches when the module of any stack frame, or one of that module's parents, is namednameJET.LastFrameModuleExact(m::Module): matches when the module of the innermost stack frame is exactlymJET.LastFrameModuleExact(name::Symbol): matches when the module of the innermost stack frame is named exactlynameJET.AnyFrameModuleExact(m::Module): matches when the module of any stack frame is exactlymJET.AnyFrameModuleExact(name::Symbol): matches when the module of any stack frame is named exactlynameJET.LastFrameMethod(meth), wheremethis aFunction,Method, orSymbol: matches when the innermost stack frame belongs to the function, is the exact method, or has the method name, respectivelyJET.AnyFrameMethod(meth), wheremethis aFunction,Method, orSymbol: matches when any stack frame belongs to the function, is the exact method, or has the method name, respectively- a user-defined
T <: JET.ReportMatcher: matches according to an extension ofJET.match_report(::T, report::InferenceErrorReport)
The non-Exact matchers accept a module and its submodules, where containment follows lexical nesting but stops at namespace roots: Base, Core, and package root modules are not considered submodules of Main. target_modules = (Main,) therefore matches only code defined interactively in the REPL or in an analyzed script, without also matching reports from Base.
target_modules = nothing
Filters reports by the contexts in which problems should be reported. By default, JET retains all detected problems. When an iterator is supplied, JET retains a report only when at least one matcher matches it.
ignored_modules = nothing
Filters reports by the contexts in which problems should be ignored. By default, JET ignores no detected problems. When an iterator is supplied, JET removes a report when at least one matcher matches it. This filter is applied aftertarget_modules.
report_config = nothing
Selects the report-filtering strategy. With the defaultnothing, JET builds its standard configuration fromtarget_modulesandignored_modules. With any other value, JET instead callsJET.configured_reports(report_config, reports)directly. This completely bypassestarget_modulesandignored_modules, even when they are supplied. Custom configuration types must extendJET.configured_reports(::T, ::Vector{InferenceErrorReport}).
Examples
julia> function foo(a)
r1 = sum(a) # => Base: MethodError(+(::Char, ::Char)), MethodError(zero(::Type{Char}))
r2 = undefsum(a) # => Main: UndefVarError(:undefsum)
return r1, r2
end;
# By default, JET prints all collected reports:
julia> @report_call foo("julia")
═════ 3 possible errors found ═════
┌ foo(a::String) @ Main ./REPL[14]:2
│┌ sum(a::String) @ Base ./reduce.jl:564
││┌ sum(a::String; kw::@Kwargs{}) @ Base ./reduce.jl:564
│││┌ sum(f::typeof(identity), a::String) @ Base ./reduce.jl:535
││││┌ sum(f::typeof(identity), a::String; kw::@Kwargs{}) @ Base ./reduce.jl:535
│││││┌ mapreduce(f::typeof(identity), op::typeof(Base.add_sum), itr::String) @ Base ./reduce.jl:307
││││││┌ mapreduce(f::typeof(identity), op::typeof(Base.add_sum), itr::String; kw::@Kwargs{}) @ Base ./reduce.jl:307
│││││││┌ mapfoldl(f::typeof(identity), op::typeof(Base.add_sum), itr::String) @ Base ./reduce.jl:175
││││││││┌ mapfoldl(f::typeof(identity), op::typeof(Base.add_sum), itr::String; init::Base._InitialValue) @ Base ./reduce.jl:175
│││││││││┌ mapfoldl_impl(f::typeof(identity), op::typeof(Base.add_sum), nt::Base._InitialValue, itr::String) @ Base ./reduce.jl:44
││││││││││┌ foldl_impl(op::Base.BottomRF{typeof(Base.add_sum)}, nt::Base._InitialValue, itr::String) @ Base ./reduce.jl:48
│││││││││││┌ _foldl_impl(op::Base.BottomRF{typeof(Base.add_sum)}, init::Base._InitialValue, itr::String) @ Base ./reduce.jl:62
││││││││││││┌ (::Base.BottomRF{typeof(Base.add_sum)})(acc::Char, x::Char) @ Base ./reduce.jl:86
│││││││││││││┌ add_sum(x::Char, y::Char) @ Base ./reduce.jl:24
││││││││││││││ no matching method found `+(::Char, ::Char)`: (x::Char + y::Char)
│││││││││││││└────────────────────
││││││││││┌ foldl_impl(op::Base.BottomRF{typeof(Base.add_sum)}, nt::Base._InitialValue, itr::String) @ Base ./reduce.jl:49
│││││││││││┌ reduce_empty_iter(op::Base.BottomRF{typeof(Base.add_sum)}, itr::String) @ Base ./reduce.jl:383
││││││││││││┌ reduce_empty_iter(op::Base.BottomRF{typeof(Base.add_sum)}, itr::String, ::Base.HasEltype) @ Base ./reduce.jl:384
│││││││││││││┌ reduce_empty(op::Base.BottomRF{typeof(Base.add_sum)}, ::Type{Char}) @ Base ./reduce.jl:360
││││││││││││││┌ reduce_empty(::typeof(Base.add_sum), ::Type{Char}) @ Base ./reduce.jl:352
│││││││││││││││┌ reduce_empty(::typeof(+), ::Type{Char}) @ Base ./reduce.jl:343
││││││││││││││││ no matching method found `zero(::Type{Char})`: zero(T::Type{Char})
│││││││││││││││└────────────────────
┌ foo(a::String) @ Main ./REPL[14]:3
│ `Main.undefsum` is not defined: undefsum
└────────────────────
# With `target_modules=(Main,)`, JET reports only problems detected in code
# defined interactively in the REPL:
julia> @report_call target_modules=(Main,) foo("julia")
═════ 1 possible error found ═════
┌ foo(a::String) @ Main ./REPL[14]:3
│ `Main.undefsum` is not defined: undefsum
└────────────────────
# With `ignored_modules=(Base,)`, JET ignores errors detected in `Base`:
julia> @report_call ignored_modules=(Base,) foo("julia")
═════ 1 possible error found ═════
┌ foo(a::String) @ Main ./REPL[14]:3
│ `Main.undefsum` is not defined: undefsum
└────────────────────
# Alternatively, use a Symbol to specify the module by name:
julia> @report_call ignored_modules=(:Base,) foo("julia")
═════ 1 possible error found ═════
┌ foo(a::String) @ Main ./REPL[14]:3
│ `Main.undefsum` is not defined: undefsum
└────────────────────Configurations for top-level analysis
JET.ToplevelConfig — Type
Configuration options for top-level analysis. These options apply to all entry points described in the top-level analysis entry points section.
context::Module = Main
The module context in which JET simulates top-level execution.This option is useful for analyzing a submodule's source file without starting analysis from the root module. For example, analyze
Base.MathwithBaseas its parent module:julia> report_file(JET.fullbasepath("math.jl"); context = Base, analyze_from_definitions = true)By default, JET virtualizes
context. This allows repeated analysis in the same session without errors such asinvalid redefinition of constant ...and prevents analyzed definitions from being written directly into the original module. Seevirtualize_module_contextfor details.
analyze_from_definitions::Union{Bool,Symbol} = false
Iftrue, after top-level processing completes, JET starts analysis from collected signatures of top-level definitions, such as method signatures. It does so only when no serious top-level error occurred, such as an error during macro expansion.This is useful for packages that contain definitions but no top-level call sites that exercise them. It allows JET to begin analysis from method or type definitions without requiring a separate driver file that calls the package.
When set to
name::Symbol, JET uses the interpreted signatures of methods namednameas analysis entry points. For example, a script that uses@maincan setanalyze_from_definitions = :main.Warning This feature is experimental and may produce many false-positive errors, especially for large packages with many dependencies. When a file containing top-level call sites is available, such as
test/runtests.jl, analyzing that file is generally preferable. Concrete call sites usually produce more accurate results than potentially abstract method signatures.Also see:
report_file,report_package
concretization_patterns = Any[]
Accepts an iterable of surface-syntax expression patterns that customize which top-level code blocks JET concretely executes. JET normalizes each supplied pattern, collects the patterns in aVector{Any}, and combines them with built-in patterns that concretize type-alias assignments.JET splits top-level input into code blocks and processes them sequentially to simulate Julia's top-level execution. Within each block, JET concretely interprets statements required to establish top-level definitions and their dependencies. It analyzes the remaining statements abstractly rather than executing application code and its possible side effects.
JET does not currently track concrete-value dependencies between separately processed blocks. Concretization can therefore fail when a selected statement needs a global value assigned in another block that JET left for abstract interpretation instead of concretely executing.
This can occur when macro expansion accesses a global variable, as in:
test/fixtures/concretization_patterns.jl
# JET doesn't conretize this by default, but just analyzes its type const GLOBAL_CODE_STORE = Dict() macro with_code_record(a) GLOBAL_CODE_STORE[__source__] = a # record the code location in the global store esc(a) end # here JET will try to actually expand `@with_code_record`, # but since `GLOBAL_CODE_STORE` didn't get concretized (i.e. instantiated), JET analysis will fail at this point @with_code_record foo(a) = identity(a) foo(10) # top-level callsite, abstracted awayTo work around this limitation, list surface-syntax expression patterns in
concretization_patterns. Before macro expansion and lowering, JET matches each top-level block against these patterns. When a pattern matches, JET concretely executes the entire block, overriding its default per-statement selection.JET uses MacroTools.jl expression patterns, so any pattern accepted by
MacroTools.@capturecan be used. For example:concretization_patterns = [:(const GLOBAL_CODE_STORE = Dict())]This ensures that the assignment to
GLOBAL_CODE_STOREis concretely executed before later macro expansion needs its value.To inspect JET's concretization plan, set the
:JET_LOGGER_LEVELproperty oftoplevel_loggerto1("debug"). Debug output shows:- which blocks match
concretization_patternsand are concretely executed - which statements JET selects by default, where
tmarks concretely interpreted statements andfmarks abstractly analyzed statements
julia> report_file("test/fixtures/concretization_patterns.jl"; concretization_patterns = [:(const GLOBAL_CODE_STORE = Dict())], toplevel_logger = IOContext(stdout, :JET_LOGGER_LEVEL => 1))[toplevel-debug] virtualized the context of Main (took 0.003 sec) [toplevel-debug] entered into test/fixtures/concretization_patterns.jl [toplevel-debug] concretization pattern `const GLOBAL_CODE_STORE = Dict()` matched `const GLOBAL_CODE_STORE = Dict()` at test/fixtures/concretization_patterns.jl:2 [toplevel-debug] concretization plan at test/fixtures/concretization_patterns.jl:4: 1 f 1 ─ $(Expr(:thunk, CodeInfo( @ none within `top-level scope` 1 ─ return $(Expr(:method, Symbol("@with_code_record"))) ))) 2 t │ $(Expr(:method, Symbol("@with_code_record"))) 3 t │ %3 = Core.Typeof(var"@with_code_record") 4 t │ %4 = Core.svec(%3, Core.LineNumberNode, Core.Module, Core.Any) 5 t │ %5 = Core.svec() 6 t │ %6 = Core.svec(%4, %5, $(QuoteNode(:(#= test/fixtures/concretization_patterns.jl:4 =#)))) 7 t │ $(Expr(:method, Symbol("@with_code_record"), :(%6), CodeInfo( @ test/fixtures/concretization_patterns.jl:5 within `none` 1 ─ $(Expr(:meta, :nospecialize, :(a))) │ Base.setindex!(GLOBAL_CODE_STORE, a, __source__) │ @ test/fixtures/concretization_patterns.jl:6 within `none` │ %3 = esc(a) └── return %3 ))) 8 f └── return var"@with_code_record" [toplevel-debug] concretization plan at test/fixtures/concretization_patterns.jl:11: 1 f 1 ─ $(Expr(:thunk, CodeInfo( @ none within `top-level scope` 1 ─ return $(Expr(:method, :foo)) ))) 2 t │ $(Expr(:method, :foo)) 3 t │ %3 = Core.Typeof(foo) 4 t │ %4 = Core.svec(%3, Core.Any) 5 t │ %5 = Core.svec() 6 t │ %6 = Core.svec(%4, %5, $(QuoteNode(:(#= test/fixtures/concretization_patterns.jl:11 =#)))) 7 t │ $(Expr(:method, :foo, :(%6), CodeInfo( @ test/fixtures/concretization_patterns.jl:11 within `none` 1 ─ %1 = identity(a) └── return %1 ))) 8 f └── return foo [toplevel-debug] concretization plan at test/fixtures/concretization_patterns.jl:13: 1 f 1 ─ %1 = foo(10) 2 f └── return %1 [toplevel-debug] exited from test/fixtures/concretization_patterns.jl (took 0.032 sec)Also see: the
toplevel_loggersection below andvirtual_process.- which blocks match
toplevel_logger::Union{Nothing,IO} = nothing
If anIOobject is provided, JET writes top-level analysis logs to it. Set the logging level with the:JET_LOGGER_LEVELIOproperty. Supported logging levels are0("info" level, default),1("debug" level).Examples:
Write logs to
stdoutat the default level:julia> report_file(filename; toplevel_logger = stdout)Write logs to
io::IOBufferat the "debug" level:julia> logger = IOContext( io, :JET_LOGGER_LEVEL => 1) julia> report_file(filename; toplevel_logger = logger)
virtualize::Bool = true
Whentrue, JET processes input in a virtualized version of the root module context.Disabling virtualization is intended mainly for testing or debugging, because top-level processing can mutate
contextand repeated analysis can trigger redefinition errors. Seevirtualize_module_contextfor implementation details.
Print configurations
JET.PrintConfig — Type
Configurations for report printing. These configurations apply when JET's analysis results are displayed in the REPL.
sourceinfo::Symbol = :default
Controls how file paths are displayed in stack traces and error reports.:full- Expand all file paths to absolute paths:default- Show paths as-is, prefixing./only for relative paths:compact- Show basename only for absolute paths, relative paths unchanged:minimal- For inference reports, show only@ Module, without a file path or line number. For top-level reports, treat this as:compact.:none- For inference reports, omit the entire@ Module path:linelocation. For top-level reports, treat this as:compactbecause the source location is essential.
print_toplevel_success::Bool = false
Iftrue, print a message when no top-level errors are found.
print_inference_success::Bool = true
Iftrue, print a message when no errors are found by an abstract-interpretation-based analysis pass.
stacktrace_types_limit::Union{Nothing, Int} = nothing
Ifnothing, limit the type depth of argument types in stack traces based on the display size. If a positiveInt, limit the type depth to the given depth. If a non-positiveInt, do not limit the type depth.
Configurations for VSCode integration
JET.VSCode.VSCodeConfig — Type
Configurations for the VS Code integration. These configurations are active only when used in the integrated Julia REPL.
vscode_console_output::Union{Nothing,IO} = nothing
JET shows the analysis result in VS Code's "PROBLEMS" pane and inline annotations. If anIOobject is supplied, JET also prints the result to that stream. When this option isnothing, the result appears only in the integrated views.