Skip to content

Commit 1255219

Browse files
committed
Suggest correct path in include_bytes!
1 parent 6f435eb commit 1255219

File tree

4 files changed

+85
-32
lines changed

4 files changed

+85
-32
lines changed

compiler/rustc_builtin_macros/src/source_util.rs

Lines changed: 54 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,19 @@ use rustc_ast::ptr::P;
33
use rustc_ast::token;
44
use rustc_ast::tokenstream::TokenStream;
55
use rustc_ast_pretty::pprust;
6+
use rustc_data_structures::sync::Lrc;
67
use rustc_expand::base::{
7-
check_zero_tts, get_single_str_from_tts, parse_expr, resolve_path, DummyResult, ExtCtxt,
8-
MacEager, MacResult,
8+
check_zero_tts, get_single_str_from_tts, get_single_str_spanned_from_tts, parse_expr,
9+
resolve_path, DummyResult, ExtCtxt, MacEager, MacResult,
910
};
1011
use rustc_expand::module::DirOwnership;
1112
use rustc_parse::new_parser_from_file;
1213
use rustc_parse::parser::{ForceCollect, Parser};
1314
use rustc_session::lint::builtin::INCOMPLETE_INCLUDE;
1415
use rustc_span::symbol::Symbol;
1516
use rustc_span::{Pos, Span};
16-
1717
use smallvec::SmallVec;
18+
use std::path::Path;
1819
use std::rc::Rc;
1920

2021
// These macros all relate to the file system; they either return
@@ -180,32 +181,22 @@ pub fn expand_include_str(
180181
tts: TokenStream,
181182
) -> Box<dyn MacResult + 'static> {
182183
let sp = cx.with_def_site_ctxt(sp);
183-
let file = match get_single_str_from_tts(cx, sp, tts, "include_str!") {
184-
Ok(file) => file,
184+
let (path, path_span) = match get_single_str_spanned_from_tts(cx, sp, tts, "include_str!") {
185+
Ok(res) => res,
185186
Err(guar) => return DummyResult::any(sp, guar),
186187
};
187-
let file = match resolve_path(&cx.sess, file.as_str(), sp) {
188-
Ok(f) => f,
189-
Err(err) => {
190-
let guar = err.emit();
191-
return DummyResult::any(sp, guar);
192-
}
193-
};
194-
match cx.source_map().load_binary_file(&file) {
188+
match load_binary_file(cx, path.as_str(), sp, path_span) {
195189
Ok(bytes) => match std::str::from_utf8(&bytes) {
196190
Ok(src) => {
197191
let interned_src = Symbol::intern(src);
198192
MacEager::expr(cx.expr_str(sp, interned_src))
199193
}
200194
Err(_) => {
201-
let guar = cx.dcx().span_err(sp, format!("{} wasn't a utf-8 file", file.display()));
195+
let guar = cx.dcx().span_err(sp, format!("{path} wasn't a utf-8 file"));
202196
DummyResult::any(sp, guar)
203197
}
204198
},
205-
Err(e) => {
206-
let guar = cx.dcx().span_err(sp, format!("couldn't read {}: {}", file.display(), e));
207-
DummyResult::any(sp, guar)
208-
}
199+
Err(dummy) => dummy,
209200
}
210201
}
211202

@@ -215,25 +206,57 @@ pub fn expand_include_bytes(
215206
tts: TokenStream,
216207
) -> Box<dyn MacResult + 'static> {
217208
let sp = cx.with_def_site_ctxt(sp);
218-
let file = match get_single_str_from_tts(cx, sp, tts, "include_bytes!") {
219-
Ok(file) => file,
209+
let (path, path_span) = match get_single_str_spanned_from_tts(cx, sp, tts, "include_bytes!") {
210+
Ok(res) => res,
220211
Err(guar) => return DummyResult::any(sp, guar),
221212
};
222-
let file = match resolve_path(&cx.sess, file.as_str(), sp) {
223-
Ok(f) => f,
224-
Err(err) => {
225-
let guar = err.emit();
226-
return DummyResult::any(sp, guar);
227-
}
228-
};
229-
match cx.source_map().load_binary_file(&file) {
213+
match load_binary_file(cx, path.as_str(), sp, path_span) {
230214
Ok(bytes) => {
231215
let expr = cx.expr(sp, ast::ExprKind::IncludedBytes(bytes));
232216
MacEager::expr(expr)
233217
}
234-
Err(e) => {
235-
let guar = cx.dcx().span_err(sp, format!("couldn't read {}: {}", file.display(), e));
236-
DummyResult::any(sp, guar)
218+
Err(dummy) => dummy,
219+
}
220+
}
221+
222+
fn load_binary_file(
223+
cx: &mut ExtCtxt<'_>,
224+
original_path: &str,
225+
macro_span: Span,
226+
path_span: Span,
227+
) -> Result<Lrc<[u8]>, Box<dyn MacResult>> {
228+
let resolved_path = match resolve_path(&cx.sess, original_path, macro_span) {
229+
Ok(path) => path,
230+
Err(err) => {
231+
let guar = err.emit();
232+
return Err(DummyResult::any(macro_span, guar));
233+
}
234+
};
235+
match cx.source_map().load_binary_file(&resolved_path) {
236+
Ok(data) => Ok(data),
237+
Err(io_err) => {
238+
let mut err = cx.dcx().struct_span_err(
239+
macro_span,
240+
format!("couldn't read {}: {io_err}", resolved_path.display()),
241+
);
242+
if Path::new(original_path).is_relative() {
243+
for prefix in ["..", "../.."] {
244+
let parent_path = Path::new(prefix).join(original_path);
245+
if resolve_path(&cx.sess, &parent_path, macro_span)
246+
.map_or(false, |p| p.exists())
247+
{
248+
err.span_suggestion(
249+
path_span,
250+
"it's in a parent directory",
251+
format!("\"{}\"", parent_path.display().to_string().escape_debug()),
252+
rustc_lint_defs::Applicability::MachineApplicable,
253+
);
254+
break;
255+
}
256+
}
257+
}
258+
let guar = err.emit();
259+
Err(DummyResult::any(macro_span, guar))
237260
}
238261
}
239262
}

compiler/rustc_expand/src/base.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1338,6 +1338,15 @@ pub fn get_single_str_from_tts(
13381338
tts: TokenStream,
13391339
name: &str,
13401340
) -> Result<Symbol, ErrorGuaranteed> {
1341+
get_single_str_spanned_from_tts(cx, span, tts, name).map(|(s, _)| s)
1342+
}
1343+
1344+
pub fn get_single_str_spanned_from_tts(
1345+
cx: &mut ExtCtxt<'_>,
1346+
span: Span,
1347+
tts: TokenStream,
1348+
name: &str,
1349+
) -> Result<(Symbol, Span), ErrorGuaranteed> {
13411350
let mut p = cx.new_parser_from_tts(tts);
13421351
if p.token == token::Eof {
13431352
let guar = cx.dcx().emit_err(errors::OnlyOneArgument { span, name });
@@ -1349,7 +1358,12 @@ pub fn get_single_str_from_tts(
13491358
if p.token != token::Eof {
13501359
cx.dcx().emit_err(errors::OnlyOneArgument { span, name });
13511360
}
1352-
expr_to_string(cx, ret, "argument must be a string literal").map(|(s, _)| s)
1361+
expr_to_spanned_string(cx, ret, "argument must be a string literal")
1362+
.map_err(|err| match err {
1363+
Ok((err, _)) => err.emit(),
1364+
Err(guar) => guar,
1365+
})
1366+
.map(|(symbol, _style, span)| (symbol, span))
13531367
}
13541368

13551369
/// Extracts comma-separated expressions from `tts`.

tests/ui/include-macros/parent_dir.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
fn main() {
2+
let _ = include_str!("include-macros/file.txt"); //~ ERROR coudln't read
3+
//~^HELP parent directory
4+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error: couldn't read $DIR/include-macros/file.txt: No such file or directory (os error 2)
2+
--> $DIR/parent_dir.rs:2:13
3+
|
4+
LL | let _ = include_str!("include-macros/file.txt");
5+
| ^^^^^^^^^^^^^-------------------------^
6+
| |
7+
| help: it's in a parent directory: `"../include-macros/file.txt"`
8+
|
9+
= note: this error originates in the macro `include_str` (in Nightly builds, run with -Z macro-backtrace for more info)
10+
11+
error: aborting due to 1 previous error
12+

0 commit comments

Comments
 (0)