Chapter 9.1 Logging and Diagnostics

RAPx provides two complementary systems for output: a leveled logging framework for development and debugging, and a diagnostic rendering utility for user-facing error reports with annotated source code.

Logging Macros

The logging system is implemented in rapx/src/utils/log.rs, built on the fern and log crates. It provides five macros at increasing severity:

MacroLevelUsage
rap_trace!TRACEFine-grained internal state dumps
rap_debug!DEBUGPath enumeration, constraint tracking, fact details
rap_info!INFOAnalysis summaries, verification results
rap_warn!WARNMissing contracts, potential unsoundness
rap_error!ERRORFatal failures; also rap_error_and_exit! to terminate

All macros use the target "RAPx", are defined as #[macro_export], and format output with timestamps and colored level prefixes:

HH:MM:SS|RAPx|LEVEL|: message

Example output:

21:50:18|RAPx|INFO|: Start analysis with RAP.
21:50:18|RAPx|INFO|: Alias found in Some("::foo"): {(0,1)}
21:50:20|RAPx|WARN|: no safety contracts found for std callee "core::ptr::read"
21:50:21|RAPx|INFO|: SafetyFlow summary: 42 function(s), 87 call edge(s)

Environment Variable

The log level is controlled by RAPX_LOG:

RAPX_LOG=info   cargo rapx analyze alias      # default — summary output
RAPX_LOG=debug  cargo rapx verify              # + path/fact detail
RAPX_LOG=trace  cargo rapx analyze dataflow    # + full internal state
RAPX_LOG=warn   cargo rapx check -uaf          # warnings only
RAPX_LOG=error  cargo rapx verify              # errors only

The default level (when RAPX_LOG is unset) is INFO.

Log Format

init_log() configures fern with:

  • Timestamps via chrono::Local
  • Colored output via fern::colors::ColoredLevelConfig: Trace=Cyan, Debug=Blue, Info=White+Green, Warn=Yellow, Error=Red
  • stderr output only (stdout is avoided due to compatibility issues)

Diagnostic Rendering

For user-facing error reports (e.g., "cloning detected here"), RAPx uses the annotate_snippets crate to render annotated source code with underlines and messages. The span utility functions in rapx/src/utils/span.rs bridge rustc's Span objects to annotate_snippets's offset-based API.

Span Utilities

FunctionInputOutput
span_to_source_code(span)SpanSource string at that span
span_to_first_line(span)SpanSpan extended to full first line
span_to_trimmed_span(span)SpanSpan with leading whitespace trimmed
span_to_filename(span)SpanFile path string
span_to_line_number(span)SpanLine number (usize)
relative_pos_range(span, sub_span)Span, SpanByte offset range of sub_span within span
are_spans_in_same_file(s1, s2)Span, Spanbool — same source file?
get_variable_name(body, local)&Body, usizeHuman-readable variable name from debug info
get_basic_block_span(body, bb)&Body, usizeSpan of a basic block's first statement or terminator

The key pattern: use relative_pos_range(background_span, target_span) to convert a rustc Span into a byte offset range that annotate_snippets can underline.

Rendering Example

A typical diagnostic report (from the memory-cloning checker in rapx/src/check/opt/memory_cloning/):

#![allow(unused)]
fn main() {
fn report_used_as_immutable(graph: &Graph, clone_span: Span, use_span: Span) {
    let code_source = span_to_source_code(graph.span);
    let filename = span_to_filename(clone_span);

    let snippet = Snippet::source(&code_source)
        .line_start(span_to_line_number(graph.span))
        .origin(&filename)
        .fold(true)
        .annotation(
            Level::Error
                .span(relative_pos_range(graph.span, clone_span))
                .label("Cloning happens here."),
        )
        .annotation(
            Level::Error
                .span(relative_pos_range(graph.span, use_span))
                .label("Used here"),
        );

    let message = Level::Warning
        .title("Unnecessary memory cloning detected")
        .snippet(snippet)
        .footer(Level::Help.title("Use borrowings instead."));

    let renderer = Renderer::styled();
    println!("{}", renderer.render(message));
}
}

When clone_span and use_span reference separate lines within a function, the renderer produces text like:

warning: Unnecessary memory cloning detected
  --> src/main.rs:15:9
   |
15 |       let cloned = value.clone();
   |                    ^^^^^^^^^^^^^ Cloning happens here.
...
25 |       process(&cloned);
   |               ^^^^^^^ Used here
   |
   = help: Use borrowings instead.

Building Messages

annotate_snippets messages are assembled from:

  • .title(): A one-line summary (Warning, Error, or Info level).
  • .snippet(): A Snippet pointing to source code with .line_start(), .origin() (file path), and one or more .annotation() calls.
  • .annotation(): A Level + .span(byte_range) + .label("explanation") triple.
  • .footer(): Optional footer with Level::Help for fix suggestions.
  • Renderer::styled(): Renders with ANSI colors for terminal output.