11//! 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 } ;
34use syntax:: ast:: { self , IsString } ;
45
5- use crate :: { CompletionItem , CompletionItemKind } ;
6+ use crate :: { context :: CompletionContext , CompletionItem , CompletionItemKind } ;
67
78use super :: Completions ;
89const CARGO_DEFINED_VARS : & [ ( & str , & str ) ] = & [
@@ -27,8 +28,12 @@ const CARGO_DEFINED_VARS: &[(&str, &str)] = &[
2728( "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" )
2829] ;
2930
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 ) ?;
3237 let range = expanded. text_range_between_quotes ( ) ?;
3338
3439 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
4045 Some ( ( ) )
4146}
4247
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 ,
4760 }
48-
49- Some ( ( ) )
5061}
5162
5263#[ cfg( test) ]
@@ -58,19 +69,29 @@ mod tests {
5869 "CARGO_BIN_NAME" ,
5970 & format ! (
6071 r#"
72+ #[rustc_builtin_macro]
73+ macro_rules! {} {{
74+ ($var:literal) => {{ 0 }}
75+ }}
76+
6177 fn main() {{
6278 let foo = {}!("CAR$0");
6379 }}
6480 "# ,
65- macro_name
81+ macro_name, macro_name
6682 ) ,
6783 & format ! (
6884 r#"
85+ #[rustc_builtin_macro]
86+ macro_rules! {} {{
87+ ($var:literal) => {{ 0 }}
88+ }}
89+
6990 fn main() {{
7091 let foo = {}!("CARGO_BIN_NAME");
7192 }}
7293 "# ,
73- macro_name
94+ macro_name, macro_name
7495 ) ,
7596 ) ;
7697 }
@@ -112,4 +133,20 @@ mod tests {
112133 let completions = completion_list ( fixture) ;
113134 assert ! ( completions. is_empty( ) , "Completions weren't empty: {}" , completions) ;
114135 }
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+ }
115152}
0 commit comments