Fix macro hygiene issue

This commit is contained in:
gwenn 2023-06-10 10:55:52 +02:00
parent 0e369ba878
commit f0670ccadd
2 changed files with 49 additions and 10 deletions

View File

@ -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<TokenStream> {
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()
}

View File

@ -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<Statement> {
/// 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