diff --git a/crates/hir-expand/src/builtin/fn_macro.rs b/crates/hir-expand/src/builtin/fn_macro.rs index 0b844c0a63..391b891ad6 100644 --- a/crates/hir-expand/src/builtin/fn_macro.rs +++ b/crates/hir-expand/src/builtin/fn_macro.rs @@ -460,10 +460,10 @@ fn compile_error_expand( let err = match &*tt.token_trees { [tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { symbol: text, - span, + span: _, kind: tt::LitKind::Str | tt::LitKind::StrRaw(_), suffix: _, - }))] => ExpandError::other(*span, Box::from(unescape_str(text).as_str())), + }))] => ExpandError::other(span, Box::from(unescape_str(text).as_str())), _ => ExpandError::other(span, "`compile_error!` argument must be a string"), }; @@ -706,18 +706,19 @@ fn relative_file( fn parse_string(tt: &tt::Subtree) -> Result<(Symbol, Span), ExpandError> { tt.token_trees .first() + .ok_or(tt.delimiter.open.cover(tt.delimiter.close)) .and_then(|tt| match tt { tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { symbol: text, span, kind: tt::LitKind::Str, suffix: _, - })) => Some((unescape_str(text), *span)), + })) => Ok((unescape_str(text), *span)), // FIXME: We wrap expression fragments in parentheses which can break this expectation // here // Remove this once we handle none delims correctly - tt::TokenTree::Subtree(t) if t.delimiter.kind == DelimiterKind::Parenthesis => { - t.token_trees.first().and_then(|tt| match tt { + tt::TokenTree::Subtree(tt) if tt.delimiter.kind == DelimiterKind::Parenthesis => { + tt.token_trees.first().and_then(|tt| match tt { tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { symbol: text, span, @@ -727,9 +728,11 @@ fn parse_string(tt: &tt::Subtree) -> Result<(Symbol, Span), ExpandError> { _ => None, }) } - _ => None, + .ok_or(tt.delimiter.open.cover(tt.delimiter.close)), + ::tt::TokenTree::Leaf(l) => Err(*l.span()), + ::tt::TokenTree::Subtree(tt) => Err(tt.delimiter.open.cover(tt.delimiter.close)), }) - .ok_or(ExpandError::other(tt.delimiter.open, "expected string literal")) + .map_err(|span| ExpandError::other(span, "expected string literal")) } fn include_expand( @@ -763,7 +766,8 @@ pub fn include_input_to_file_id( arg_id: MacroCallId, arg: &tt::Subtree, ) -> Result { - relative_file(db, arg_id, parse_string(arg)?.0.as_str(), false, arg.delimiter.open) + let (s, span) = parse_string(arg)?; + relative_file(db, arg_id, s.as_str(), false, span) } fn include_bytes_expand( diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 5ee59efdbc..266ef2a55c 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -837,13 +837,17 @@ fn macro_call_diagnostics( let node = InFile::new(file_id, db.ast_id_map(file_id).get_erased(loc.kind.erased_ast_id())); let (message, error) = err.render_to_string(db.upcast()); - let precise_location = Some( - err.span().range - + db.ast_id_map(err.span().anchor.file_id.into()) - .get_erased(err.span().anchor.ast_id) - .text_range() - .start(), - ); + let precise_location = if err.span().anchor.file_id == file_id { + Some( + err.span().range + + db.ast_id_map(err.span().anchor.file_id.into()) + .get_erased(err.span().anchor.ast_id) + .text_range() + .start(), + ) + } else { + None + }; acc.push(MacroError { node, precise_location, message, error }.into()); } @@ -1798,13 +1802,17 @@ impl DefWithBody { BodyDiagnostic::MacroError { node, err } => { let (message, error) = err.render_to_string(db.upcast()); - let precise_location = Some( - err.span().range - + db.ast_id_map(err.span().anchor.file_id.into()) - .get_erased(err.span().anchor.ast_id) - .text_range() - .start(), - ); + let precise_location = if err.span().anchor.file_id == node.file_id { + Some( + err.span().range + + db.ast_id_map(err.span().anchor.file_id.into()) + .get_erased(err.span().anchor.ast_id) + .text_range() + .start(), + ) + } else { + None + }; MacroError { node: (*node).map(|it| it.into()), precise_location, diff --git a/crates/ide-diagnostics/src/handlers/macro_error.rs b/crates/ide-diagnostics/src/handlers/macro_error.rs index 08efd1a204..e59b63f288 100644 --- a/crates/ide-diagnostics/src/handlers/macro_error.rs +++ b/crates/ide-diagnostics/src/handlers/macro_error.rs @@ -48,7 +48,7 @@ macro_rules! include { () => {} } macro_rules! compile_error { () => {} } include!("doesntexist"); -//^^^^^^^ error: failed to load file `doesntexist` + //^^^^^^^^^^^^^ error: failed to load file `doesntexist` compile_error!("compile_error macro works"); //^^^^^^^^^^^^^ error: compile_error macro works @@ -128,7 +128,7 @@ macro_rules! env { () => {} } macro_rules! concat { () => {} } include!(concat!(env!("OUT_DIR"), "/out.rs")); -//^^^^^^^ error: `OUT_DIR` not set, enable "build scripts" to fix + //^^^^^^^^^ error: `OUT_DIR` not set, enable "build scripts" to fix "#, ); } @@ -163,20 +163,25 @@ macro_rules! include {} #[rustc_builtin_macro] macro_rules! compile_error {} +#[rustc_builtin_macro] +macro_rules! concat {} fn main() { // Test a handful of built-in (eager) macros: include!(invalid); - //^^^^^^^ error: expected string literal + //^^^^^^^ error: expected string literal include!("does not exist"); - //^^^^^^^ error: failed to load file `does not exist` + //^^^^^^^^^^^^^^^^ error: failed to load file `does not exist` + + include!(concat!("does ", "not ", "exist")); + //^^^^^^^^^^^^^^^^^^^^^^^^^^ error: failed to load file `does not exist` env!(invalid); - //^^^ error: expected string literal + //^^^^^^^ error: expected string literal env!("OUT_DIR"); - //^^^ error: `OUT_DIR` not set, enable "build scripts" to fix + //^^^^^^^^^ error: `OUT_DIR` not set, enable "build scripts" to fix compile_error!("compile_error works"); //^^^^^^^^^^^^^ error: compile_error works @@ -201,7 +206,7 @@ fn f() { m!(); m!(hi); - //^ error: leftover tokens + //^ error: leftover tokens } "#, ); diff --git a/crates/span/src/lib.rs b/crates/span/src/lib.rs index 057fab89c7..b4e21d64f8 100644 --- a/crates/span/src/lib.rs +++ b/crates/span/src/lib.rs @@ -33,6 +33,16 @@ pub const FIXUP_ERASED_FILE_AST_ID_MARKER: ErasedFileAstId = pub type Span = SpanData; +impl Span { + pub fn cover(self, other: Span) -> Span { + if self.anchor != other.anchor { + return self; + } + let range = self.range.cover(other.range); + Span { range, ..self } + } +} + /// Spans represent a region of code, used by the IDE to be able link macro inputs and outputs /// together. Positions in spans are relative to some [`SpanAnchor`] to make them more incremental /// friendly.