1
1
//! Completes environment variables defined by Cargo (https://doc.rust-lang.org/cargo/reference/environment-variables.html)
2
- use ide_db:: syntax_helpers:: node_ext:: get_outer_macro_name;
2
+ use hir:: Semantics ;
3
+ use ide_db:: { syntax_helpers:: node_ext:: get_outer_macro, RootDatabase } ;
3
4
use syntax:: ast:: { self , IsString } ;
4
5
5
- use crate :: { CompletionItem , CompletionItemKind } ;
6
+ use crate :: { context :: CompletionContext , CompletionItem , CompletionItemKind } ;
6
7
7
8
use super :: Completions ;
8
9
const CARGO_DEFINED_VARS : & [ ( & str , & str ) ] = & [
@@ -27,8 +28,12 @@ const CARGO_DEFINED_VARS: &[(&str, &str)] = &[
27
28
( "CARGO_TARGET_TMPDIR" , "Only set when building integration test or benchmark code. This is a path to a directory inside the target directory where integration tests or benchmarks are free to put any data needed by the tests/benches. Cargo initially creates this directory but doesn't manage its content in any way, this is the responsibility of the test code" )
28
29
] ;
29
30
30
- pub ( crate ) fn complete_cargo_env_vars ( acc : & mut Completions , expanded : & ast:: String ) -> Option < ( ) > {
31
- guard_env_macro ( expanded) ?;
31
+ pub ( crate ) fn complete_cargo_env_vars (
32
+ acc : & mut Completions ,
33
+ ctx : & CompletionContext < ' _ > ,
34
+ expanded : & ast:: String ,
35
+ ) -> Option < ( ) > {
36
+ guard_env_macro ( expanded, & ctx. sema , & ctx. db ) ?;
32
37
let range = expanded. text_range_between_quotes ( ) ?;
33
38
34
39
CARGO_DEFINED_VARS . iter ( ) . for_each ( |( var, detail) | {
@@ -40,13 +45,19 @@ pub(crate) fn complete_cargo_env_vars(acc: &mut Completions, expanded: &ast::Str
40
45
Some ( ( ) )
41
46
}
42
47
43
- fn guard_env_macro ( string : & ast:: String ) -> Option < ( ) > {
44
- let name = get_outer_macro_name ( string) ?;
45
- if !matches ! ( name. text( ) . as_str( ) , "env" | "option_env" ) {
46
- return None ;
48
+ fn guard_env_macro (
49
+ string : & ast:: String ,
50
+ semantics : & Semantics < ' _ , RootDatabase > ,
51
+ db : & RootDatabase ,
52
+ ) -> Option < ( ) > {
53
+ let call = get_outer_macro ( string) ?;
54
+ let name = call. path ( ) ?. segment ( ) ?. name_ref ( ) ?;
55
+ let makro = semantics. resolve_macro_call ( & call) ?;
56
+
57
+ match name. text ( ) . as_str ( ) {
58
+ "env" | "option_env" if makro. kind ( db) == hir:: MacroKind :: BuiltIn => Some ( ( ) ) ,
59
+ _ => None ,
47
60
}
48
-
49
- Some ( ( ) )
50
61
}
51
62
52
63
#[ cfg( test) ]
@@ -58,19 +69,29 @@ mod tests {
58
69
"CARGO_BIN_NAME" ,
59
70
& format ! (
60
71
r#"
72
+ #[rustc_builtin_macro]
73
+ macro_rules! {} {{
74
+ ($var:literal) => {{ 0 }}
75
+ }}
76
+
61
77
fn main() {{
62
78
let foo = {}!("CAR$0");
63
79
}}
64
80
"# ,
65
- macro_name
81
+ macro_name, macro_name
66
82
) ,
67
83
& format ! (
68
84
r#"
85
+ #[rustc_builtin_macro]
86
+ macro_rules! {} {{
87
+ ($var:literal) => {{ 0 }}
88
+ }}
89
+
69
90
fn main() {{
70
91
let foo = {}!("CARGO_BIN_NAME");
71
92
}}
72
93
"# ,
73
- macro_name
94
+ macro_name, macro_name
74
95
) ,
75
96
) ;
76
97
}
@@ -112,4 +133,20 @@ mod tests {
112
133
let completions = completion_list ( fixture) ;
113
134
assert ! ( completions. is_empty( ) , "Completions weren't empty: {}" , completions) ;
114
135
}
136
+
137
+ #[ test]
138
+ fn doesnt_complete_for_shadowed_macro ( ) {
139
+ let fixture = r#"
140
+ macro_rules! env {
141
+ ($var:literal) => { 0 }
142
+ }
143
+
144
+ fn main() {
145
+ let foo = env!("CA$0");
146
+ }
147
+ "# ;
148
+
149
+ let completions = completion_list ( fixture) ;
150
+ assert ! ( completions. is_empty( ) , "Completions weren't empty: {}" , completions)
151
+ }
115
152
}
0 commit comments