diff --git a/rusqlite-macros/src/lib.rs b/rusqlite-macros/src/lib.rs index 2369639..7bfda9d 100644 --- a/rusqlite-macros/src/lib.rs +++ b/rusqlite-macros/src/lib.rs @@ -1,6 +1,6 @@ //! Private implementation details of `rusqlite`. -use proc_macro::{Delimiter, Literal, TokenStream, TokenTree}; +use proc_macro::{Delimiter, Group, Literal, Span, TokenStream, TokenTree}; use fallible_iterator::FallibleIterator; use sqlite3_parser::ast::{ParameterInfo, ToTokens}; @@ -58,15 +58,19 @@ fn try_bind(input: TokenStream) -> Result { return Err("Mixing named and numbered parameters is not supported.".to_string()); } + let call_site = literal.span(); let mut res = TokenStream::new(); for (i, name) in info.names.iter().enumerate() { //eprintln!("(i: {}, name: {})", i + 1, &name[1..]); res.extend(Some(stmt.clone())); - res.extend(parse_ts(&format!( - ".raw_bind_parameter({}, &{})?;", - i + 1, - &name[1..] - ))); + res.extend(respan( + parse_ts(&format!( + ".raw_bind_parameter({}, &{})?;", + i + 1, + &name[1..] + )), + call_site, + )); } Ok(res) @@ -93,6 +97,24 @@ fn strip_matches<'a>(s: &'a str, pattern: &str) -> &'a str { .unwrap_or(s) } +fn respan(ts: TokenStream, span: Span) -> TokenStream { + let mut res = TokenStream::new(); + for tt in ts { + let tt = match tt { + TokenTree::Ident(mut ident) => { + ident.set_span(ident.span().resolved_at(span).located_at(span)); + TokenTree::Ident(ident) + } + TokenTree::Group(group) => { + TokenTree::Group(Group::new(group.delimiter(), respan(group.stream(), span))) + } + _ => tt, + }; + res.extend(Some(tt)) + } + res +} + fn parse_ts(s: &str) -> TokenStream { s.parse().unwrap() } diff --git a/src/lib.rs b/src/lib.rs index 49ab436..3f6f1c4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -215,11 +215,26 @@ macro_rules! named_params { } /// Captured identifiers in SQL +/// +/// * only SQLite `$x` / `@x` / `:x` syntax works (Rust `&x` syntax does not +/// work). +/// * `$x.y` expression does not work. +/// +/// # Example +/// +/// ```rust, no_run +/// # use rusqlite::{prepare_and_bind, Connection, Result, Statement}; +/// +/// fn misc(db: &Connection) -> Result { +/// let name = "Lisa"; +/// let age = 8; +/// let smart = true; +/// Ok(prepare_and_bind!(db, "SELECT $name, @age, :smart;")) +/// } +/// ``` #[macro_export] macro_rules! prepare_and_bind { ($conn:expr, $sql:literal) => {{ - #[cfg(trick_rust_analyzer_into_highlighting_interpolated_bits)] - format_args!($sql); let mut stmt = $conn.prepare($sql)?; $crate::__bind!(stmt $sql); stmt @@ -227,11 +242,13 @@ macro_rules! prepare_and_bind { } /// Captured identifiers in SQL +/// +/// * only SQLite `$x` / `@x` / `:x` syntax works (Rust `&x` syntax does not +/// work). +/// * `$x.y` expression does not work. #[macro_export] macro_rules! prepare_cached_and_bind { ($conn:expr, $sql:literal) => {{ - #[cfg(trick_rust_analyzer_into_highlighting_interpolated_bits)] - format_args!($sql); let mut stmt = $conn.prepare_cached($sql)?; $crate::__bind!(stmt $sql); stmt