@@ -90,15 +90,15 @@ register_builtin! {
90
90
( line, Line ) => line_expand,
91
91
( stringify, Stringify ) => stringify_expand,
92
92
( format_args, FormatArgs ) => format_args_expand,
93
- ( env, Env ) => env_expand,
94
- ( option_env, OptionEnv ) => option_env_expand,
95
93
// format_args_nl only differs in that it adds a newline in the end,
96
94
// so we use the same stub expansion for now
97
95
( format_args_nl, FormatArgsNl ) => format_args_expand,
98
96
99
97
EAGER :
100
98
( concat, Concat ) => concat_expand,
101
- ( include, Include ) => include_expand
99
+ ( include, Include ) => include_expand,
100
+ ( env, Env ) => env_expand,
101
+ ( option_env, OptionEnv ) => option_env_expand
102
102
}
103
103
104
104
fn line_expand (
@@ -137,31 +137,6 @@ fn stringify_expand(
137
137
Ok ( expanded)
138
138
}
139
139
140
- fn env_expand (
141
- _db : & dyn AstDatabase ,
142
- _id : LazyMacroId ,
143
- _tt : & tt:: Subtree ,
144
- ) -> Result < tt:: Subtree , mbe:: ExpandError > {
145
- // dummy implementation for type-checking purposes
146
- // we cannot use an empty string here, because for
147
- // `include!(concat!(env!("OUT_DIR"), "/foo.rs"))` will become
148
- // `include!("foo.rs"), which maybe infinite loop
149
- let expanded = quote ! { "__RA_UNIMPLEMENTATED__" } ;
150
-
151
- Ok ( expanded)
152
- }
153
-
154
- fn option_env_expand (
155
- _db : & dyn AstDatabase ,
156
- _id : LazyMacroId ,
157
- _tt : & tt:: Subtree ,
158
- ) -> Result < tt:: Subtree , mbe:: ExpandError > {
159
- // dummy implementation for type-checking purposes
160
- let expanded = quote ! { std:: option:: Option :: None :: <& str > } ;
161
-
162
- Ok ( expanded)
163
- }
164
-
165
140
fn column_expand (
166
141
_db : & dyn AstDatabase ,
167
142
_id : LazyMacroId ,
@@ -278,30 +253,36 @@ fn concat_expand(
278
253
279
254
fn relative_file ( db : & dyn AstDatabase , call_id : MacroCallId , path : & str ) -> Option < FileId > {
280
255
let call_site = call_id. as_file ( ) . original_file ( db) ;
281
- let path = RelativePath :: new ( & path) ;
282
256
283
- let res = db . resolve_relative_path ( call_site , & path ) ? ;
284
- // Prevent include itself
285
- if res == call_site {
286
- return None ;
257
+ // Handle trivial case
258
+ if let Some ( res ) = db . resolve_relative_path ( call_site , & RelativePath :: new ( & path ) ) {
259
+ // Prevent include itself
260
+ return if res == call_site { None } else { Some ( res ) } ;
287
261
}
288
- Some ( res)
262
+
263
+ // Extern paths ?
264
+ let krate = db. relevant_crates ( call_site) . get ( 0 ) ?. clone ( ) ;
265
+ let ( extern_source_id, relative_file) = db. crate_graph ( ) [ krate] . env . extern_path ( path) ?;
266
+
267
+ db. resolve_extern_path ( extern_source_id, & relative_file)
289
268
}
290
269
291
- fn include_expand (
292
- db : & dyn AstDatabase ,
293
- arg_id : EagerMacroId ,
294
- tt : & tt:: Subtree ,
295
- ) -> Result < ( tt:: Subtree , FragmentKind ) , mbe:: ExpandError > {
296
- let path = tt
297
- . token_trees
270
+ fn parse_string ( tt : & tt:: Subtree ) -> Result < String , mbe:: ExpandError > {
271
+ tt. token_trees
298
272
. get ( 0 )
299
273
. and_then ( |tt| match tt {
300
274
tt:: TokenTree :: Leaf ( tt:: Leaf :: Literal ( it) ) => unquote_str ( & it) ,
301
275
_ => None ,
302
276
} )
303
- . ok_or_else ( || mbe:: ExpandError :: ConversionError ) ?;
277
+ . ok_or_else ( || mbe:: ExpandError :: ConversionError )
278
+ }
304
279
280
+ fn include_expand (
281
+ db : & dyn AstDatabase ,
282
+ arg_id : EagerMacroId ,
283
+ tt : & tt:: Subtree ,
284
+ ) -> Result < ( tt:: Subtree , FragmentKind ) , mbe:: ExpandError > {
285
+ let path = parse_string ( tt) ?;
305
286
let file_id =
306
287
relative_file ( db, arg_id. into ( ) , & path) . ok_or_else ( || mbe:: ExpandError :: ConversionError ) ?;
307
288
@@ -314,12 +295,58 @@ fn include_expand(
314
295
Ok ( ( res, FragmentKind :: Items ) )
315
296
}
316
297
298
+ fn get_env_inner ( db : & dyn AstDatabase , arg_id : EagerMacroId , key : & str ) -> Option < String > {
299
+ let call_id: MacroCallId = arg_id. into ( ) ;
300
+ let original_file = call_id. as_file ( ) . original_file ( db) ;
301
+
302
+ let krate = db. relevant_crates ( original_file) . get ( 0 ) ?. clone ( ) ;
303
+ db. crate_graph ( ) [ krate] . env . get ( key)
304
+ }
305
+
306
+ fn env_expand (
307
+ db : & dyn AstDatabase ,
308
+ arg_id : EagerMacroId ,
309
+ tt : & tt:: Subtree ,
310
+ ) -> Result < ( tt:: Subtree , FragmentKind ) , mbe:: ExpandError > {
311
+ let key = parse_string ( tt) ?;
312
+
313
+ // FIXME:
314
+ // If the environment variable is not defined int rustc, then a compilation error will be emitted.
315
+ // We might do the same if we fully support all other stuffs.
316
+ // But for now on, we should return some dummy string for better type infer purpose.
317
+ // However, we cannot use an empty string here, because for
318
+ // `include!(concat!(env!("OUT_DIR"), "/foo.rs"))` will become
319
+ // `include!("foo.rs"), which might go to infinite loop
320
+ let s = get_env_inner ( db, arg_id, & key) . unwrap_or ( "__RA_UNIMPLEMENTATED__" . to_string ( ) ) ;
321
+ let expanded = quote ! { #s } ;
322
+
323
+ Ok ( ( expanded, FragmentKind :: Expr ) )
324
+ }
325
+
326
+ fn option_env_expand (
327
+ db : & dyn AstDatabase ,
328
+ arg_id : EagerMacroId ,
329
+ tt : & tt:: Subtree ,
330
+ ) -> Result < ( tt:: Subtree , FragmentKind ) , mbe:: ExpandError > {
331
+ let key = parse_string ( tt) ?;
332
+ let expanded = match get_env_inner ( db, arg_id, & key) {
333
+ None => quote ! { std:: option:: Option :: None :: <& str > } ,
334
+ Some ( s) => quote ! { std:: option:: Some ( #s) } ,
335
+ } ;
336
+
337
+ Ok ( ( expanded, FragmentKind :: Expr ) )
338
+ }
339
+
317
340
#[ cfg( test) ]
318
341
mod tests {
319
342
use super :: * ;
320
- use crate :: { name:: AsName , test_db:: TestDB , AstNode , MacroCallId , MacroCallKind , MacroCallLoc } ;
343
+ use crate :: {
344
+ name:: AsName , test_db:: TestDB , AstNode , EagerCallLoc , MacroCallId , MacroCallKind ,
345
+ MacroCallLoc ,
346
+ } ;
321
347
use ra_db:: { fixture:: WithFixture , SourceDatabase } ;
322
348
use ra_syntax:: ast:: NameOwner ;
349
+ use std:: sync:: Arc ;
323
350
324
351
fn expand_builtin_macro ( ra_fixture : & str ) -> String {
325
352
let ( db, file_id) = TestDB :: with_single_file ( & ra_fixture) ;
@@ -330,27 +357,61 @@ mod tests {
330
357
let ast_id_map = db. ast_id_map ( file_id. into ( ) ) ;
331
358
332
359
let expander = find_by_name ( & macro_calls[ 0 ] . name ( ) . unwrap ( ) . as_name ( ) ) . unwrap ( ) ;
333
- let expander = expander. left ( ) . unwrap ( ) ;
334
360
335
- // the first one should be a macro_rules
336
- let def = MacroDefId {
337
- krate : Some ( CrateId ( 0 ) ) ,
338
- ast_id : Some ( AstId :: new ( file_id. into ( ) , ast_id_map. ast_id ( & macro_calls[ 0 ] ) ) ) ,
339
- kind : MacroDefKind :: BuiltIn ( expander) ,
340
- } ;
361
+ let file_id = match expander {
362
+ Either :: Left ( expander) => {
363
+ // the first one should be a macro_rules
364
+ let def = MacroDefId {
365
+ krate : Some ( CrateId ( 0 ) ) ,
366
+ ast_id : Some ( AstId :: new ( file_id. into ( ) , ast_id_map. ast_id ( & macro_calls[ 0 ] ) ) ) ,
367
+ kind : MacroDefKind :: BuiltIn ( expander) ,
368
+ } ;
341
369
342
- let loc = MacroCallLoc {
343
- def,
344
- kind : MacroCallKind :: FnLike ( AstId :: new (
345
- file_id. into ( ) ,
346
- ast_id_map. ast_id ( & macro_calls[ 1 ] ) ,
347
- ) ) ,
348
- } ;
370
+ let loc = MacroCallLoc {
371
+ def,
372
+ kind : MacroCallKind :: FnLike ( AstId :: new (
373
+ file_id. into ( ) ,
374
+ ast_id_map. ast_id ( & macro_calls[ 1 ] ) ,
375
+ ) ) ,
376
+ } ;
377
+
378
+ let id: MacroCallId = db. intern_macro ( loc) . into ( ) ;
379
+ id. as_file ( )
380
+ }
381
+ Either :: Right ( expander) => {
382
+ // the first one should be a macro_rules
383
+ let def = MacroDefId {
384
+ krate : Some ( CrateId ( 0 ) ) ,
385
+ ast_id : Some ( AstId :: new ( file_id. into ( ) , ast_id_map. ast_id ( & macro_calls[ 0 ] ) ) ) ,
386
+ kind : MacroDefKind :: BuiltInEager ( expander) ,
387
+ } ;
349
388
350
- let id: MacroCallId = db. intern_macro ( loc) . into ( ) ;
351
- let parsed = db. parse_or_expand ( id. as_file ( ) ) . unwrap ( ) ;
389
+ let args = macro_calls[ 1 ] . token_tree ( ) . unwrap ( ) ;
390
+ let parsed_args = mbe:: ast_to_token_tree ( & args) . unwrap ( ) . 0 ;
391
+
392
+ let arg_id = db. intern_eager_expansion ( {
393
+ EagerCallLoc {
394
+ def,
395
+ fragment : FragmentKind :: Expr ,
396
+ subtree : Arc :: new ( parsed_args. clone ( ) ) ,
397
+ file_id : file_id. into ( ) ,
398
+ }
399
+ } ) ;
400
+
401
+ let ( subtree, fragment) = expander. expand ( & db, arg_id, & parsed_args) . unwrap ( ) ;
402
+ let eager = EagerCallLoc {
403
+ def,
404
+ fragment,
405
+ subtree : Arc :: new ( subtree) ,
406
+ file_id : file_id. into ( ) ,
407
+ } ;
408
+
409
+ let id: MacroCallId = db. intern_eager_expansion ( eager. into ( ) ) . into ( ) ;
410
+ id. as_file ( )
411
+ }
412
+ } ;
352
413
353
- parsed . text ( ) . to_string ( )
414
+ db . parse_or_expand ( file_id ) . unwrap ( ) . to_string ( )
354
415
}
355
416
356
417
#[ test]
0 commit comments