JuliaCon 2025

ReLint: an extensible Lint checker
2025-07-23 , Lawrence Room 120 - REPL Main Stage

StaticLint.jl is a popular linter for Julia. Despite its wide acceptance, StaticLint suffers from many shortcomings related to extensibility and poor interoperability.
ReLint is an MIT open-source Lint checker used at RelationalAI. ReLint is fast, extensible, offers tools to match abstract syntax trees, and can be used by a GitHub workflow or a pre-commit hook. ReLint has been successfully employed to enforce programming conventions on a large code base (over 300K LOC).


Despite its wide acceptance, StaticLint.jl has some shortcomings that got in the way of our use:

  • Adding new rules is cumbersome,
  • StaticLint cannot be easily run by a GitHub action at each commit or used to gate a commit if changes violate some lint rules.

This talk presents and demonstrates ReLint, an open-source Lint checker used at RelationalAI, released under the MIT License. ReLint is fast, extensible, offers tools to match abstract syntax trees, and can be used by a GitHub workflow or a pre-commit hook.

Example 1: Consider the scenario: we would like to forbid the use of @async in a codebase to favor @spawn. We can define an empty struct as follows:

struct AsyncRule <: LintRule end

And the function check can then be overloaded:

check(::AsyncRule, x::EXPR) = 
      generic_check(x, "@async hole_variable", "Use `@spawn` instead of `@async`.")

The pattern to be matched is provided as a Julia string code "@async hole_variable". The pseudo-variable hole_variable matches any expression. Each instance of this pattern in a source code produces the error "Use @spawn instead of @async." accompanied by the exact location in the source code.

ReLint can be run on a file or a folder using ReLint.run_lint("path/to/folder_or_julia_file/"). A Julia code containing the instruction @async foo() + bar() will be reported by ReLint. Such a report can be automatically posted as a comment in a Pull Request or used to prevent the code from being committed.

Example 2: A less trivial rule is forbidding the use of Threads.nthreads() in a const variable. This is important if an application is built on a particular machine but runs on a different machine with a different spec.

function check(::InitializingWithFunctionRule, x::EXPR, markers::Dict{Symbol,Symbol})
    # Threads.nthreads() must not be used in a const field, but it is allowed elsewhere
    haskey(markers, :const) || return
    generic_check(x, "Threads.nthreads()", "`Threads.nthreads()` should not be used in a constant variable.")
end

A line of code such as const MAX_QUEUE = max(2*Threads.nthreads(), 16) will be reported by RAILint.

Example 3: As a last example, consider the case of branches that cannot be reached:

if foo()
    bar()
elseif foo()
   zork()
end

Obviously, the zork() function will never be executed because the two sequential if have the same condition. This error may not be obvious in the presence of dozens of lines of code in each branch. An instance of this pattern can be easily caught with:

function check(t::UnreachableBranchRule, x::EXPR)
    generic_check(
        t,
        x,
        "if hole_variableA \
            hole_variable \
         elseif hole_variableA \
            hole_variable \
         end",
        "Unreachable branch.")
end

Multiple uses of the pseudo-variable hole_variableA impose the matched code to be identical, AST-wise.

This presentation: The main features of ReLint, accompanied by several scenarios, will be demonstrated. We will also cover extensibility, embedding ReLint in a GitHub action (useful to have a Lint report for every commit), and pre-commit (useful to gate a commit if it contains some rule violations). ReLint has been used in an industrial environment to enforce programming conventions and security rules for over 1 year in a large, critical application.

Alexandre Bergel is Computer Scientist at RelationalAI, Switzerland. Until 2022, he was Associate Professor and researcher at the University of Chile. Alexandre Bergel and his collaborators carry out research in software engineering. His interests include designing tools and methodologies to improve the overall performance and internal quality of software systems and databases by employing profiling, visualization, and artificial intelligence techniques.

Alexandre Bergel has authored over 170 articles, published in international and peer-reviewed scientific forums, including the most competitive conferences and journals in the field of software engineering. Alexandre has participated in over 175 program committees of international events. Several of his research prototypes have been turned into products and adopted by major companies in the semiconductor industry, certification of critical software systems, and the aerospace industry.

Alexandre gave talks to prominent research institutes, including NASA JPL and the Deutsches Zentrum für Luft- und Raumfahrt (DLR).

Alexandre is a member of the editorial board of Empirical Software Engineering. Alexandre authored 4 books: Agile Visualization with Pharo, Agile Artificial Intelligence in Pharo, Agile Visualization, and co-authored the book Deep Into Pharo.

His webpage is: https://bergel.eu