Diagnostic

JETLS reports various diagnostic messages (errors, warnings, hints) to help you catch potential issues in your Julia code. Each diagnostic has a unique code that identifies its category and type.

This document describes all available diagnostic codes, their meanings, default severity levels, and how to configure them to match your project's needs.

Codes

JETLS reports diagnostics using hierarchical codes in the format "category/kind", following the LSP specification. This structure allows fine-grained control over which diagnostics to show and at what severity level through configuration.

All available diagnostic codes are listed below. Each category (e.g., syntax/*, lowering/*) contains one or more specific diagnostic codes:

Severity levels

Each diagnostic has a severity level that indicates how serious the issue is. JETLS supports four severity levels defined by the LSP specification:

  • Error (1): invalid code that cannot be compiled or loaded (e.g. syntax errors, lowering errors)
  • Warning (2): code that is likely a bug (e.g. undefined variables, type mismatches)
  • Information (3): valid code that is probably unintentional (e.g. unused bindings, unreachable code)
  • Hint (4): stylistic suggestions where the code works as intended but could be written more cleanly (e.g. unsorted import names)

The LSP specification does not prescribe how clients should render each severity level, so the actual display varies by editor. In practice, most editors display Error, Warning, and Information with color-coded underlines (red, yellow, blue) and gutter markers, while Hint is typically rendered with a more subtle indicator such as faded text or an ellipsis (...).[vscode_severity]

You can change the severity of any diagnostic by configuring diagnostic section. Additionally, JETLS supports disabling diagnostics entirely using the special severity value "off" (or 0).

Sources

JETLS uses different diagnostic channels to balance analysis accuracy with response latency. Lightweight checks run as you edit for immediate feedback, while deeper analysis runs on save to avoid excessive resource consumption.

Each diagnostic has a source field that identifies which diagnostic channel it comes from. This section explains what each source means, helping you understand when diagnostics update. Additionally, some editors also allow filtering diagnostics by source.

Info

This section contains references to LSP protocol details. You don't need to understand these details to use JETLS effectively - the key takeaway is simply that different diagnostics update at different times (as you edit, when you save, or when you run tests via TestRunner integration).

JETLS uses three diagnostic sources:

  • JETLS/live: Diagnostics available on demand via the pull model diagnostic channels textDocument/diagnostic (for open files) and workspace/diagnostic (for unopened files when diagnostic.all_files is enabled). Most clients request these as you edit, providing real-time feedback without requiring a file save. Includes syntax errors and lowering-based analysis (syntax/*, lowering/*).
  • JETLS/save: Diagnostics published by JETLS after on-save full analysis via the push model channel textDocument/publishDiagnostics. These run full analysis including type inference and require loading your code. Includes top-level errors and inference-based analysis (toplevel/*, inference/*).
  • JETLS/extra: Diagnostics from external sources like the TestRunner integration (testrunner/*). Published via textDocument/publishDiagnostics.

Reference

This section provides detailed explanations for each diagnostic code. For every diagnostic, you'll find:

  • A description of what the diagnostic detects
  • Its default severity level and source
  • Code examples demonstrating when the diagnostic is reported
  • Example diagnostic messages (shown in code comments)

Here is a summary table of the diagnostics explained in this section:

CodeDefault SeveritySourceDescription
syntax/parse-errorErrorJETLS/liveSyntax parsing errors detected by JuliaSyntax.jl
lowering/errorErrorJETLS/liveGeneral lowering errors
lowering/macro-expansion-errorErrorJETLS/liveErrors during macro expansion
lowering/undef-global-varWarningJETLS/liveReferences to undefined global variables
lowering/undef-local-varWarning/InformationJETLS/liveReferences to undefined local variables
lowering/ambiguous-soft-scopeWarningJETLS/liveAssignment in soft scope shadows a global variable
lowering/captured-boxed-variableInformationJETLS/liveVariables captured by closures that require boxing
lowering/unused-argumentInformationJETLS/liveFunction arguments that are never used
lowering/unused-localInformationJETLS/liveLocal variables that are never used
lowering/unused-assignmentInformationJETLS/liveAssignments whose values are never read
lowering/unused-importInformationJETLS/liveImported names that are never used
lowering/unreachable-codeInformationJETLS/liveCode after a block terminator that is never reached
lowering/unsorted-import-namesHintJETLS/liveImport/export names not sorted alphabetically
toplevel/errorErrorJETLS/saveErrors during code loading
toplevel/method-overwriteWarningJETLS/saveMethod definitions that overwrite previous ones
toplevel/abstract-fieldInformationJETLS/saveStruct fields with abstract types
inference/undef-global-varWarningJETLS/saveReferences to undefined global variables
inference/field-errorWarningJETLS/saveAccess to non-existent struct fields
inference/bounds-errorWarningJETLS/saveOut-of-bounds field access by index
inference/method-errorWarningJETLS/saveNo matching method found for function calls
testrunner/test-failureErrorJETLS/extraTest failures from TestRunner integration

Syntax diagnostic (syntax/*)

Syntax parse error (syntax/parse-error)

Default severity: Error

Syntax parsing errors detected by JuliaSyntax.jl. These indicate invalid Julia syntax that prevents the code from being parsed.

Example:

function parse_error(x)
    println(x  # Expected `)` or `,` (JETLS syntax/parse-error)
end

Lowering diagnostic (lowering/*)

Lowering diagnostics are detected during Julia's lowering phase, which transforms parsed syntax into a simpler intermediate representation.

Lowering error (lowering/error)

Default severity: Error

General lowering errors that don't fit into more specific categories.

Example:

function lowering_error(x)
    $(x)  # `$` expression outside string or quote block (JETLS lowering/error)
end

Macro expansion error (lowering/macro-expansion-error)

Default severity: Error

Errors that occur when expanding macros during the lowering phase.

Example:

function macro_expand_error()
    @undefined_macro ex  # Macro name `@undefined_macro` not found (JETLS lowering/macro-expansion-error)
end

Errors that occur during actual macro expansion are also reported:

macro myinline(ex)
    Meta.isexpr(ex, :function) || error("Expected long function definition")
    return :(@inline $ex)
end
@myinline callsin(x) = sin(x)  # Error expanding macro
                               # Expected long function definition (JETLS lowering/macro-expansion-error)

Undefined global variable (lowering/undef-global-var)

Default severity: Warning

References to undefined global variables, detected during lowering analysis. This diagnostic provides immediate feedback as you type.

Example:

function undef_global_var()
    ret = sin(undefined_var)  # `Main.undefined_var` is not defined (JETLS lowering/undef-global-var)
    return ret
end

This diagnostic detects simple undefined global variable references. For more comprehensive detection (including qualified references like Base.undefvar), see inference/undef-global-var (source: JETLS/save).

Undefined local variable (lowering/undef-local-var)

Default severity: Warning or Information

References to local variables that may be used before being defined. This diagnostic provides immediate feedback based on CFG-aware analysis on lowered code.

The severity depends on the certainty of the undefined usage:

  • Warning: The variable is definitely used before any assignment (strict undef - guaranteed UndefVarError at runtime)
  • Information: The variable may be undefined depending on control flow (e.g., assigned only in one branch of an if statement)

Examples:

function strict_undef()
    println(x)  # Variable `x` is used before it is defined (JETLS lowering/undef-local-var)
                # Severity: Warning (strict undef)
    x = 1       # RelatedInformation: `x` is defined here
    return x
end

function maybe_undef(cond)
    if cond
        y = 1   # RelatedInformation: `y` is defined here
    end
    return y  # Variable `y` may be used before it is defined (JETLS lowering/undef-local-var)
              # Severity: Information (maybe undef)
end

The diagnostic is reported at the first use location, with relatedInformation pointing to definition sites to help understand the control flow.

Workaround: Using `@isdefined` guard

When a variable is conditionally assigned, you can rewrite the program logic using @isdefined so that the compiler can track the definedness:

function guarded(cond)
    if cond
        y = 42
    end
    if @isdefined(y)
        return sin(y)  # No diagnostic: compiler knows `y` is defined here
    end
end
Correlated condition analysis

The analysis recognizes correlated conditions: if a variable is assigned under a condition and later used under the same condition, no diagnostic is emitted. This works with simple variables, && chains, and nested if blocks:

function correlated(cond)
    if cond
        y = 42
    end
    if cond
        return sin(y)  # No diagnostic: analysis tracks that `cond`
                       # is the same in both branches
    end
end

This is limited to conditions that are simple local variables or && chains of local variables (e.g. if x && z). Compound expressions like if x > 0 are not tracked as correlated conditions.

Workaround: Using `@assert @isdefined` as a hint

There are cases where you know a variable is always defined at a certain point, but the analysis cannot prove it. This includes compound conditions (e.g. if !isnothing(x)), complex control flow, or general runtime invariants that the compiler cannot figure out statically. In such cases, you can use @assert @isdefined(var) "..." as a hint:

function compound_condition(x)
    if !isnothing(x)
        y = sin(x)
    end
    if !isnothing(x)
        @assert @isdefined(y) "compiler hint"
        return cos(y)  # No diagnostic after the assertion
    end
end

This hint allows the compiler to avoid generating unnecessary UndefVarError handling code, and also serves as documentation that you've verified the variable is defined at this point.

Ambiguous soft scope (lowering/ambiguous-soft-scope)

Default severity: Warning

Reported when a variable is assigned inside a for, while, or try/catch block at the top level of a file, and a global variable with the same name already exists[on_soft_scope]. This assignment is ambiguous because it behaves differently depending on where the code runs:

  • In the REPL or notebooks: assigns to the existing global
  • In a file: creates a new local variable, leaving the global unchanged

Example (A Common Confusion adapted from the Julia manual):

ambiguous-scope.jl

# Print the numbers 1 through 5
global i = 0
while i < 5
    i += 1  # Assignment to `i` in soft scope is ambiguous (JETLS lowering/ambiguous-soft-scope)
            # Variable `i` may be used before it is defined (JETLS lowering/undef-local-var)
    println(i)
end

This diagnostic matches the warning that Julia itself emits at runtime. Running the example above as a file produces:

julia ambiguous-scope.jl

┌ Warning: Assignment to `i` in soft scope is ambiguous because a global variable by the same name exists: `i` will be treated as a new local. Disambiguate by using `local i` to suppress this warning or `global i` to assign to the existing global variable.
└ @ ambiguous-scope.jl:4
ERROR: LoadError: UndefVarError: `i` not defined in local scope
Suggestion: check for an assignment to a local variable that shadows a global of the same name.
Stacktrace:
...
Why is `lowering/undef-local-var` also reported?

Since i += 1 desugars to i = i + 1, the new local i is read before being assigned, which also triggers lowering/undef-local-var and causes the UndefVarError shown above at runtime.

Code actions available

Two quick fixes are offered: "Insert global i declaration" (preferred) to assign to the existing global, and "Insert local i declaration" to explicitly mark the variable as local and suppress the warning.

Notebook mode

This diagnostic is suppressed for notebooks, where soft scope semantics are enabled (matching REPL behavior).

Captured boxed variable (lowering/captured-boxed-variable)

Default severity: Information

Reported when a variable is captured by a closure and requires "boxing" due to being assigned multiple times. Captured boxed variables are stored in heap-allocated containers (a.k.a. Core.Box), which can cause type instability and hinder compiler optimizations.[performance_tip]

Example:

function captured_variable()
    x = 1           # `x` is captured and boxed (JETLS lowering/captured-boxed-variable)
    f = () ->
        println(x)  # RelatedInformation: Closure at L3:9 captures `x`
    x = 2           # (`x` is reassigned after capture)
    return f
end

The diagnostic includes relatedInformation showing where the variable is captured:

function multi_capture()
    x = 1               # `x` is captured and boxed (JETLS lowering/captured-boxed-variable)
    f = () ->
        println(x)      # RelatedInformation: Closure at L3:9 captures `x`
    g = () ->
        println(x + 1)  # RelatedInformation: Closure at L5:9 captures `x`
    x = 2
    return f, g
end

Variables captured by closures but assigned only once before closure definition do not require boxing and are not reported:

function not_boxed()
    x = 1
    f = () -> x  # OK: `x` is only assigned once
    return f
end
Workaround

When you need to capture a variable that changes, consider using a let block:

function with_let()
    x = 1
    f = let x = x
        () -> x  # Captures the value of `x` at this point
    end
    x = 2
    return f()  # Returns 1, not 2
end

or mutable container like Ref to avoid direct assignment to the captured variable:

function with_mut()
    x = Ref(1)
    f = () -> x[]
    x[] = 2
    return f()
end
Box optimization difference from the flisp lowerer

The generation of captured boxes is an implementation detail of the code lowerer (JuliaLowering.jl) used internally by JETLS, and the conditions under which captured boxes are created may change in the future. The control flow dominance analysis used for captured variable detection in the current JuliaLowering.jl is quite primitive, so captured boxes may occur even when programmers don't expect them. Also note that the cases where the flisp lowerer (a.k.a. code_lowered) generates Core.Box do not necessarily match the cases where JETLS reports captured boxes.

Unused argument (lowering/unused-argument)

Default severity: Information

Function arguments that are declared but never used in the function body.

By default, arguments with names starting with _ are not reported; see allow_unused_underscore.

Example:

function unused_argument(x, y)  # Unused argument `y` (JETLS lowering/unused-argument)
    return x + 1
end
Code action available

You can use the "Prefix with '_'" code action to quickly rename unused arguments, indicating they are intentionally unused.

Unused local variable (lowering/unused-local)

Default severity: Information

Local variables that are never used anywhere in their scope.

By default, variables with names starting with _ are not reported; see allow_unused_underscore.

Example:

function unused_local()
    x = 10  # Unused local binding `x` (JETLS lowering/unused-local)
    return println(10)
end
Code action available

Several code actions are available for this diagnostic:

  • "Prefix with '_'" to indicate the variable is intentionally unused
  • "Delete assignment" to remove only the left-hand side (keeping the right-hand side expression)
  • "Delete statement" to remove the entire assignment statement

Unused assignment (lowering/unused-assignment)

Default severity: Information

Assignments to local variables whose values are never read. This diagnostic targets individual assignments where the value is overwritten or the function exits before the value is read.

This diagnostic does not overlap with lowering/unused-local: unused-local reports variables that are never used anywhere, while unused-assignment reports specific assignments to variables that are used elsewhere. For example:

function f(x::Bool)
    if x
        z = "Hi"
        println(z)  # z is used here, so `lowering/unused-local` is NOT reported
    end
    if x
        z = "Hey"   # but this assignment's value is never read → `lowering/lunused-assignment`
    end
end

Compare with a fully unused variable, which only triggers unused-local:

function g()
    y = 42  # y is never used anywhere → `lowering/unused-local`
end
Code action available

Two code actions are available for this diagnostic:

  • "Delete assignment" to remove only the left-hand side (keeping the right-hand side expression)
  • "Delete statement" to remove the entire assignment statement
Closure-captured variables

Variables captured by closures are excluded from this analysis to avoid false positives, since the CFG cannot precisely model when closures are called.

Unused import (lowering/unused-import)

Default severity: Information

Reported when an explicitly imported name is never used within the same module space. This diagnostic helps identify unnecessary imports that can be removed to keep your code clean.

Example:

using Base: sin, cos  # Unused import `cos` (JETLS lowering/unused-import)

examplefunc() = sin(1.0)  # Only `sin` is used

The diagnostic is reported for explicit imports (using M: name or import M: name), not for bulk imports like using M which bring in all exported names.

This diagnostic scans all files within the module space to detect usages, so an import is only reported as unused if the name is not used anywhere in your module.

Code action available

Use the "Remove unused import" code action to delete the unused name. If it's the only name in the statement, the entire statement is removed.

Limitation

Usages introduced only through macro expansion cannot be detected. For example, in the following code, sin appears unused even though it is used inside the macro-generated code:

using Base: sin  # Incorrectly reported as unused

macro gensincall(x)
    :(sin($(esc(x))))
end
@gensincall 42

Workarounds include using the binding directly in the macro body:

macro gensincall(x)
    f = sin  # `sin` is used here
    :($f($(esc(x))))
end

or passing the binding as part of the macro argument:

macro gencall(ex)
    :($(esc(ex)))
end
@gencall sin(42)  # `sin` is used here

Unreachable code (lowering/unreachable-code)

Default severity: Information

Reported when code appears after a statement that always exits the current block, making subsequent code unreachable. The unreachable code is rendered with the Unnecessary tag, which causes editors to display it as faded/grayed out.

Example:

function after_return()
    return 1
    x = 2  # Unreachable code (JETLS lowering/unreachable-code)
    y = 3  # Also unreachable
end

function after_throw()
    throw(ErrorException("error"))
    cleanup()  # Unreachable code (JETLS lowering/unreachable-code)
end

function all_branches_return(x)
    if x > 0
        return 1
    else
        return -1
    end
    println("unreachable")  # Unreachable code (JETLS lowering/unreachable-code)
end

function after_continue()
    for i = 1:10
        continue
        println(i)  # Unreachable code (JETLS lowering/unreachable-code)
    end
end
Code action available

A "Delete unreachable code" quick fix is available that removes the unreachable region along with surrounding whitespace, from the end of the terminating statement to the end of the dead code.

Unsorted import names (lowering/unsorted-import-names)

Default severity: Hint

Reported when names in import, using, export, or public statements are not sorted alphabetically. This is a style diagnostic that helps maintain consistent ordering of imports and exports.

Expected sort order:

  • Case-sensitive comparison (A < Z < a < z)
  • For as expressions like using Foo: bar as baz, sorted by original name (bar), not the alias
  • Relative imports: dots are included in the sort key (..Base < Base < Core)

Example:

import Foo: c, a, b  # Names are not sorted alphabetically (JETLS lowering/unsorted-import-names)

export bar, @foo  # Names are not sorted alphabetically (JETLS lowering/unsorted-import-names)
Code action available

The "Sort import names" code action automatically fixes the ordering. When the sorted result exceeds 92 characters ( Julia's conventional maximum line length), the code action wraps to multiple lines with 4-space continuation indent.

Top-level diagnostic (toplevel/*)

Top-level diagnostics are reported by JETLS's full analysis feature (source: JETLS/save), which runs when you save a file. To prevent excessive analysis on frequent saves, JETLS uses a debounce mechanism. See the [full_analysis] debounce configuration documentation to adjust the debounce period.

Top-level error (toplevel/error)

Default severity: Error

Errors that occur when JETLS loads your code for analysis. This diagnostic is commonly reported in several scenarios:

  • Missing package dependencies (the most frequent cause)
  • Type definition failures
  • References to undefined names at the top level
  • Other errors during module evaluation

Examples:

struct ToplevelError  # UndefVarError: `Unexisting` not defined in `JETLS`
                      # Suggestion: check for spelling errors or missing imports. (JETLS toplevel/error)
    x::Unexisting
end

using UnexistingPkg  # Package JETLS does not have UnexistingPkg in its dependencies:
                     # - You may have a partially installed environment. Try `Pkg.instantiate()`
                     # to ensure all packages in the environment are installed.
                     # - Or, if you have JETLS checked out for development and have
                     # added UnexistingPkg as a dependency but haven't updated your primary
                     # environment's manifest file, try `Pkg.resolve()`.
                     # - Otherwise you may need to report an issue with JETLS (JETLS toplevel/error)

These errors prevent JETLS from fully analyzing your code, which means Inference diagnostic will not be available until the top-level errors are resolved. To fix these errors, ensure your package environment is properly set up by running Pkg.instantiate() in your package directory, and verify that your package can be loaded successfully in a Julia REPL.

Method overwrite (toplevel/method-overwrite)

Default severity: Warning

Reported when a method with the same signature is defined multiple times within a package. This typically indicates an unintentional redefinition that overwrites the previous method.

Example:

function duplicate(x::Int)
    return x + 1
end

function duplicate(x::Int, y::Int=2)  # Method definition duplicate(x::Int) in module MyPkg overwritten
                                      # (JETLS toplevel/method-overwrite)
    return x + y
end

The diagnostic includes a link to the original definition location via relatedInformation, making it easy to navigate to the first definition.

Abstract field type (toplevel/abstract-field)

Default severity: Information

Reported when a struct field has an abstract type, which can cause performance issues due to type instability. Storing values in abstractly-typed fields often prevents the compiler from generating optimized code.

Example:

struct MyStruct
    xs::Vector{Integer}  # `MyStruct` has abstract field `xs::Vector{Integer}`
                         # (JETLS toplevel/abstract-field)
end

struct AnotherStruct
    data::AbstractVector{Int}  # `AnotherStruct` has abstract field `data::AbstractVector{Int}`
                               # (JETLS toplevel/abstract-field)
end

To fix this, use concrete types or parameterize your struct:

struct MyStruct
    xs::Vector{Int}  # Concrete element type
end

struct AnotherStruct{T<:AbstractVector{Int}}
    data::T  # Parameterized field allows concrete types
end
Tip

If you intentionally use abstract field types (e.g., in cases where data types are inherently only known at compile time[nospecialize_tip]), you can suppress this diagnostic using pattern-based configuration:

[[diagnostic.patterns]]
pattern = "`MyStruct` has abstract field `.*`"
match_by = "message"
match_type = "regex"
severity = "off"

Inference diagnostic (inference/*)

Inference diagnostics use JET.jl to perform type-aware analysis and detect potential errors through static analysis. These diagnostics are reported by JETLS's full analysis feature (source: JETLS/save), which runs when you save a file (similar to Top-level diagnostic).

Undefined global variable (inference/undef-global-var)

Default severity: Warning

References to undefined global variables, detected through full analysis. This diagnostic can detect comprehensive cases including qualified references (e.g., Base.undefvar). Position information is reported on a line basis.

Example:

function undef_global_var(x)
    Base.Math.sinkernel(x)  # `Base.Math.sinkernel` is not defined (JETLS inference/undef-global-var)
end

For faster feedback while editing, see lowering/undef-global-var (source: JETLS/live), which reports a subset of undefined variable cases with accurate position information.

Field error (inference/field-error)

Default severity: Warning

Access to non-existent struct fields. This diagnostic is reported when code attempts to access a field that doesn't exist on a struct type.

Example:

struct MyStruct
    property::Int
end
function field_error()
    x = MyStruct(42)
    return x.propert  # FieldError: type MyStruct has no field `propert`, available fields: `property` (JETLS inference/field-error)
end

Bounds error (inference/bounds-error)

Default severity: Warning

Out-of-bounds field access by index. This diagnostic is reported when code attempts to access a struct field using an integer index that is out of bounds, such as getfield(x, i) or tuple indexing tpl[i].

Note

This diagnostic is not reported for arrays, since the compiler doesn't track array shape information.

Example:

function bounds_error(tpl::Tuple{Int})
    return tpl[2]  # BoundsError: attempt to access Tuple{Int64} at index [2] (JETLS inference/bounds-error)
end

Method error (inference/method-error)

Default severity: Warning

Function calls where no matching method can be found for the inferred argument types. This diagnostic detects potential MethodErrors that would occur at runtime.

Examples:

function method_error_example()
    return sin(1, 2)  # no matching method found `sin(::Int64, ::Int64)` (JETLS inference/method-error)
end

When multiple union-split signatures fail to find matches, the diagnostic will report all failed signatures:

only_int(x::Int) = 2x

function union_split_method_error(x::Union{Int,String})
    return only_int(x)  # no matching method found `only_int(::String)` (1/2 union split)
                        # (JETLS inference/method-error)
end

TestRunner diagnostic (testrunner/*)

TestRunner diagnostics are reported when you manually run tests via code lens or code actions through the TestRunner integration (source: JETLS/extra). Unlike other diagnostics, these are not triggered automatically by editing or saving files.

Test failure (testrunner/test-failure)

Default severity: Error

Test failures reported by TestRunner integration that happened during running individual @testset blocks or @test cases.

Note

Diagnostics from @test cases automatically disappear after 10 seconds, while @testset diagnostics persist until you run the testset again, restructure testsets, or clear them manually.

Configuration

You can configure which diagnostics are shown and at what severity level under the [diagnostic] section. This allows you to customize JETLS's behavior to match your project's coding standards and preferences.

Common use cases

Suppress specific macro expansion errors:

[[diagnostic.patterns]]
pattern = "Macro name `MyPkg.@mymacro` not found"
match_by = "message"
match_type = "literal"
severity = "off"

Apply different settings for test files:

# Downgrade unused arguments to hints in test files
[[diagnostic.patterns]]
pattern = "lowering/unused-argument"
match_by = "code"
match_type = "literal"
severity = "hint"
path = "test/**/*.jl"

# Disable all diagnostics for generated code
[[diagnostic.patterns]]
pattern = ".*"
match_by = "code"
match_type = "regex"
severity = "off"
path = "gen/**/*.jl"

Disable unused variable warnings during prototyping:

[[diagnostic.patterns]]
pattern = "lowering/(unused-argument|unused-local|unused-assignment)"
match_by = "code"
match_type = "regex"
severity = "off"

Make inference diagnostic less intrusive:

[[diagnostic.patterns]]
pattern = "inference/.*"
match_by = "code"
match_type = "regex"
severity = "hint"

For complete configuration options, severity values, pattern matching syntax, and more examples, see the [diagnostic] configuration section in the JETLS configuration page.