@@ -3,15 +3,17 @@ use rustc_ast::ptr::P;
3
3
use rustc_ast:: token;
4
4
use rustc_ast:: tokenstream:: TokenStream ;
5
5
use rustc_ast_pretty:: pprust;
6
+ use rustc_errors:: PResult ;
6
7
use rustc_expand:: base:: { self , * } ;
7
8
use rustc_expand:: module:: DirOwnership ;
8
9
use rustc_parse:: parser:: { ForceCollect , Parser } ;
9
10
use rustc_parse:: { self , new_parser_from_file} ;
10
11
use rustc_session:: lint:: builtin:: INCOMPLETE_INCLUDE ;
11
12
use rustc_span:: symbol:: Symbol ;
12
- use rustc_span:: { self , Pos , Span } ;
13
+ use rustc_span:: { self , FileName , Pos , Span } ;
13
14
14
15
use smallvec:: SmallVec ;
16
+ use std:: path:: PathBuf ;
15
17
use std:: rc:: Rc ;
16
18
17
19
// These macros all relate to the file system; they either return
@@ -102,7 +104,7 @@ pub fn expand_include<'cx>(
102
104
return DummyResult :: any ( sp) ;
103
105
} ;
104
106
// 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) {
106
108
Ok ( f) => f,
107
109
Err ( mut err) => {
108
110
err. emit ( ) ;
@@ -171,7 +173,7 @@ pub fn expand_include_str(
171
173
let Some ( file) = get_single_str_from_tts ( cx, sp, tts, "include_str!" ) else {
172
174
return DummyResult :: any ( sp) ;
173
175
} ;
174
- let file = match cx . resolve_path ( file, sp) {
176
+ let file = match resolve_path ( cx , file, sp) {
175
177
Ok ( f) => f,
176
178
Err ( mut err) => {
177
179
err. emit ( ) ;
@@ -205,7 +207,7 @@ pub fn expand_include_bytes(
205
207
let Some ( file) = get_single_str_from_tts ( cx, sp, tts, "include_bytes!" ) else {
206
208
return DummyResult :: any ( sp) ;
207
209
} ;
208
- let file = match cx . resolve_path ( file, sp) {
210
+ let file = match resolve_path ( cx , file, sp) {
209
211
Ok ( f) => f,
210
212
Err ( mut err) => {
211
213
err. emit ( ) ;
@@ -220,3 +222,40 @@ pub fn expand_include_bytes(
220
222
}
221
223
}
222
224
}
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