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,))
Note

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_reportsFunction

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::Module or JET.LastFrameModule(m::Module): matches when the module of the report's innermost stack frame is m or one of its submodules
  • name::Symbol or JET.LastFrameModule(name::Symbol): matches when the module of the report's innermost stack frame, or one of that module's parents, is named name
  • JET.AnyFrameModule(m::Module): matches when the module of any stack frame is m or one of its submodules
  • JET.AnyFrameModule(name::Symbol): matches when the module of any stack frame, or one of that module's parents, is named name
  • JET.LastFrameModuleExact(m::Module): matches when the module of the innermost stack frame is exactly m
  • JET.LastFrameModuleExact(name::Symbol): matches when the module of the innermost stack frame is named exactly name
  • JET.AnyFrameModuleExact(m::Module): matches when the module of any stack frame is exactly m
  • JET.AnyFrameModuleExact(name::Symbol): matches when the module of any stack frame is named exactly name
  • JET.LastFrameMethod(meth), where meth is a Function, Method, or Symbol: matches when the innermost stack frame belongs to the function, is the exact method, or has the method name, respectively
  • JET.AnyFrameMethod(meth), where meth is a Function, Method, or Symbol: 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 of JET.match_report(::T, report::InferenceErrorReport)
Module containment stops at namespace roots

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 after target_modules.

  • report_config = nothing
    Selects the report-filtering strategy. With the default nothing, JET builds its standard configuration from target_modules and ignored_modules. With any other value, JET instead calls JET.configured_reports(report_config, reports) directly. This completely bypasses target_modules and ignored_modules, even when they are supplied. Custom configuration types must extend JET.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
└────────────────────

source

Configurations for top-level analysis

JET.ToplevelConfigType

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.Math with Base as 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 as invalid redefinition of constant ... and prevents analyzed definitions from being written directly into the original module. See virtualize_module_context for details.


  • analyze_from_definitions::Union{Bool,Symbol} = false
    If true, 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 named name as analysis entry points. For example, a script that uses @main can set analyze_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 a Vector{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 away
    

    To 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.@capture can be used. For example:

    concretization_patterns = [:(const GLOBAL_CODE_STORE = Dict())]

    This ensures that the assignment to GLOBAL_CODE_STORE is concretely executed before later macro expansion needs its value.

    To inspect JET's concretization plan, set the :JET_LOGGER_LEVEL property of toplevel_logger to 1 ("debug"). Debug output shows:

    • which blocks match concretization_patterns and are concretely executed
    • which statements JET selects by default, where t marks concretely interpreted statements and f marks 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_logger section below and virtual_process.


  • toplevel_logger::Union{Nothing,IO} = nothing
    If an IO object is provided, JET writes top-level analysis logs to it. Set the logging level with the :JET_LOGGER_LEVEL IO property. Supported logging levels are 0 ("info" level, default), 1 ("debug" level).

    Examples:

    • Write logs to stdout at the default level:

      julia> report_file(filename; toplevel_logger = stdout)
    • Write logs to io::IOBuffer at the "debug" level:

      julia> logger = IOContext(
                 io, :JET_LOGGER_LEVEL => 1)
      
      julia> report_file(filename; toplevel_logger = logger)

  • virtualize::Bool = true
    When true, 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 context and repeated analysis can trigger redefinition errors. See virtualize_module_context for implementation details.


source
JET.PrintConfigType

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:line location. For top-level reports, treat this as :compact because the source location is essential.

  • print_toplevel_success::Bool = false
    If true, print a message when no top-level errors are found.

  • print_inference_success::Bool = true
    If true, print a message when no errors are found by an abstract-interpretation-based analysis pass.

  • stacktrace_types_limit::Union{Nothing, Int} = nothing
    If nothing, limit the type depth of argument types in stack traces based on the display size. If a positive Int, limit the type depth to the given depth. If a non-positive Int, do not limit the type depth.

source

Configurations for VSCode integration

JET.VSCode.VSCodeConfigType

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 an IO object is supplied, JET also prints the result to that stream. When this option is nothing, the result appears only in the integrated views.

source