@@ -3,15 +3,17 @@ use rustc_ast::ptr::P;
33use rustc_ast:: token;
44use rustc_ast:: tokenstream:: TokenStream ;
55use rustc_ast_pretty:: pprust;
6+ use rustc_errors:: PResult ;
67use rustc_expand:: base:: { self , * } ;
78use rustc_expand:: module:: DirOwnership ;
89use rustc_parse:: parser:: { ForceCollect , Parser } ;
910use rustc_parse:: { self , new_parser_from_file} ;
1011use rustc_session:: lint:: builtin:: INCOMPLETE_INCLUDE ;
1112use rustc_span:: symbol:: Symbol ;
12- use rustc_span:: { self , Pos , Span } ;
13+ use rustc_span:: { self , FileName , Pos , Span } ;
1314
1415use smallvec:: SmallVec ;
16+ use std:: path:: PathBuf ;
1517use std:: rc:: Rc ;
1618
1719// These macros all relate to the file system; they either return
@@ -102,7 +104,7 @@ pub fn expand_include<'cx>(
102104 return DummyResult :: any ( sp) ;
103105 } ;
104106 // The file will be added to the code map by the parser
105- let file = match cx . resolve_path ( file, sp) {
107+ let file = match resolve_path ( cx , file, sp) {
106108 Ok ( f) => f,
107109 Err ( mut err) => {
108110 err. emit ( ) ;
@@ -171,7 +173,7 @@ pub fn expand_include_str(
171173 let Some ( file) = get_single_str_from_tts ( cx, sp, tts, "include_str!" ) else {
172174 return DummyResult :: any ( sp) ;
173175 } ;
174- let file = match cx . resolve_path ( file, sp) {
176+ let file = match resolve_path ( cx , file, sp) {
175177 Ok ( f) => f,
176178 Err ( mut err) => {
177179 err. emit ( ) ;
@@ -205,7 +207,7 @@ pub fn expand_include_bytes(
205207 let Some ( file) = get_single_str_from_tts ( cx, sp, tts, "include_bytes!" ) else {
206208 return DummyResult :: any ( sp) ;
207209 } ;
208- let file = match cx . resolve_path ( file, sp) {
210+ let file = match resolve_path ( cx , file, sp) {
209211 Ok ( f) => f,
210212 Err ( mut err) => {
211213 err. emit ( ) ;
@@ -220,3 +222,40 @@ pub fn expand_include_bytes(
220222 }
221223 }
222224}
225+
226+ /// Resolves a `path` mentioned inside Rust code, returning an absolute path.
227+ ///
228+ /// This unifies the logic used for resolving `include_X!`.
229+ fn resolve_path < ' a > (
230+ cx : & mut ExtCtxt < ' a > ,
231+ path : impl Into < PathBuf > ,
232+ span : Span ,
233+ ) -> PResult < ' a , PathBuf > {
234+ let path = path. into ( ) ;
235+
236+ // Relative paths are resolved relative to the file in which they are found
237+ // after macro expansion (that is, they are unhygienic).
238+ if !path. is_absolute ( ) {
239+ let callsite = span. source_callsite ( ) ;
240+ let mut result = match cx. source_map ( ) . span_to_filename ( callsite) {
241+ FileName :: Real ( name) => name
242+ . into_local_path ( )
243+ . expect ( "attempting to resolve a file path in an external file" ) ,
244+ FileName :: DocTest ( path, _) => path,
245+ other => {
246+ return Err ( cx. struct_span_err (
247+ span,
248+ & format ! (
249+ "cannot resolve relative path in non-file source `{}`" ,
250+ cx. source_map( ) . filename_for_diagnostics( & other)
251+ ) ,
252+ ) ) ;
253+ }
254+ } ;
255+ result. pop ( ) ;
256+ result. push ( path) ;
257+ Ok ( result)
258+ } else {
259+ Ok ( path)
260+ }
261+ }
0 commit comments