Skip to content

RFC 011: Precise Error Spans in F-Strings

Status: Planned

Summary

Improve error messages for f-string interpolation expressions to point to the specific {expr} that caused the error, rather than the entire f-string.

Motivation

Currently, when an error occurs in an f-string expression like:

println(f"[Producer {id}] Sending message {i}")

The error points to the entire f-string:

type error: Unknown symbol 'i'
  --> file.incn:16:17
     |
  16 |         println(f"[Producer {id}] Sending message {i}")
     |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Ideally, it should point directly to {i}:

type error: Unknown symbol 'i'
  --> file.incn:16:49
     |
  16 |         println(f"[Producer {id}] Sending message {i}")
     |                                                   ^^^

Current Implementation

The lexer stores f-string parts without position information:

pub enum FStringPart {
    Literal(String),
    Expr(String),  // Just the expression text, no offset
}

The parser uses the f-string token's span for all expression parts.

Proposed Changes

1. Lexer Changes

Add offset tracking to FStringPart:

pub enum FStringPart {
    Literal(String),
    Expr {
        text: String,
        offset: usize,  // Byte offset from start of f-string content
    },
}

Update scan_fstring() to track the current offset within the f-string and store it with each expression.

2. Parser Changes

Update convert_fstring_parts() to compute precise spans:

fn convert_fstring_parts(&self, parts: &[LexFStringPart], fstring_start: usize) -> Vec<FStringPart> {
    parts.iter().map(|p| match p {
        LexFStringPart::Expr { text, offset } => {
            let start = fstring_start + offset;
            let end = start + text.len() + 2; // +2 for { }
            FStringPart::Expr(Spanned::new(expr, Span::new(start, end)))
        }
        // ...
    })
}

Complexity

~40 lines of changes across:

  • crates/incan_syntax/src/lexer/strings.rs - Track offsets in scan_fstring()
  • crates/incan_syntax/src/ast.rs - Update FStringPart (if needed)
  • crates/incan_syntax/src/parser.rs - Compute precise spans

Priority

Low - The current implementation (pointing to the f-string) is already usable. This is a polish improvement.

References

  • Issue discovered while testing various examples
  • Current fix: Use f-string token span instead of Span::default()