From 75f4c54d8c1304966dff2e1d45cff88ac96a9d0f Mon Sep 17 00:00:00 2001 From: btwotwo <10519967+btwotwo@users.noreply.github.com> Date: Wed, 5 Oct 2022 00:02:03 +0200 Subject: [PATCH 01/16] Add stub for cargo environment variables auto completion --- crates/ide-completion/src/completions.rs | 1 + .../src/completions/env_vars.rs | 60 +++++++++++++++++++ crates/ide-completion/src/lib.rs | 1 + 3 files changed, 62 insertions(+) create mode 100644 crates/ide-completion/src/completions/env_vars.rs diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs index 97b90c62dd7..296dfc14250 100644 --- a/crates/ide-completion/src/completions.rs +++ b/crates/ide-completion/src/completions.rs @@ -19,6 +19,7 @@ pub(crate) mod snippet; pub(crate) mod r#type; pub(crate) mod use_; pub(crate) mod vis; +pub(crate) mod env_vars; use std::iter; diff --git a/crates/ide-completion/src/completions/env_vars.rs b/crates/ide-completion/src/completions/env_vars.rs new file mode 100644 index 00000000000..6cfbd0f922f --- /dev/null +++ b/crates/ide-completion/src/completions/env_vars.rs @@ -0,0 +1,60 @@ +//! Completes environment variables defined by Cargo (https://doc.rust-lang.org/cargo/reference/environment-variables.html) + +use syntax::{ast, AstToken, AstNode, TextRange, TextSize}; + +use crate::{context::CompletionContext, CompletionItem, CompletionItemKind}; + +use super::Completions; + +pub(crate) fn complete_cargo_env_vars( + acc: &mut Completions, + ctx: &CompletionContext<'_>, + original: &ast::String +) { + if !is_env_macro(original) { + return; + } + + let start = ctx.original_token.text_range().start() + TextSize::from(1); + let cursor = ctx.position.offset; + + CompletionItem::new(CompletionItemKind::Binding, TextRange::new(start, cursor), "CARGO").add_to(acc); +} + +fn is_env_macro(string: &ast::String) -> bool { + //todo: replace copypaste from format_string with separate function + (|| { + let macro_call = string.syntax().parent_ancestors().find_map(ast::MacroCall::cast)?; + let name = macro_call.path()?.segment()?.name_ref()?; + + if !matches!(name.text().as_str(), + "env" | "option_env") { + return None; + } + + + Some(()) + })() + .is_some() +} + +#[cfg(test)] +mod tests { + use expect_test::{expect, Expect}; + use crate::tests::{check_edit}; + + #[test] + fn completes_env_variables() { + check_edit("CARGO", + r#" + fn main() { + let foo = env!("CA$0); + } + "# + ,r#" + fn main() { + let foo = env!("CARGO); + } + "#) + } +} \ No newline at end of file diff --git a/crates/ide-completion/src/lib.rs b/crates/ide-completion/src/lib.rs index 8d21f4fce0a..cd9da0d3dcf 100644 --- a/crates/ide-completion/src/lib.rs +++ b/crates/ide-completion/src/lib.rs @@ -183,6 +183,7 @@ pub fn completions( CompletionAnalysis::String { original, expanded: Some(expanded) } => { completions::extern_abi::complete_extern_abi(acc, ctx, expanded); completions::format_string::format_string(acc, ctx, original, expanded); + completions::env_vars::complete_cargo_env_vars(acc, ctx, original) } CompletionAnalysis::UnexpandedAttrTT { colon_prefix, From 07621ce09676b5a2267c8305ba85b3d37e1403f4 Mon Sep 17 00:00:00 2001 From: btwotwo <10519967+btwotwo@users.noreply.github.com> Date: Thu, 6 Oct 2022 16:25:12 +0200 Subject: [PATCH 02/16] Use expanded version of text for env var completion --- crates/ide-completion/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ide-completion/src/lib.rs b/crates/ide-completion/src/lib.rs index cd9da0d3dcf..c5718ec40f5 100644 --- a/crates/ide-completion/src/lib.rs +++ b/crates/ide-completion/src/lib.rs @@ -183,7 +183,7 @@ pub fn completions( CompletionAnalysis::String { original, expanded: Some(expanded) } => { completions::extern_abi::complete_extern_abi(acc, ctx, expanded); completions::format_string::format_string(acc, ctx, original, expanded); - completions::env_vars::complete_cargo_env_vars(acc, ctx, original) + completions::env_vars::complete_cargo_env_vars(acc, expanded); } CompletionAnalysis::UnexpandedAttrTT { colon_prefix, From 08b0c92105fc9a0139a30f8215c7ca44e55ac7b0 Mon Sep 17 00:00:00 2001 From: btwotwo <10519967+btwotwo@users.noreply.github.com> Date: Thu, 6 Oct 2022 16:26:49 +0200 Subject: [PATCH 03/16] Add helper method to get a macro name from passed string --- crates/ide-db/src/syntax_helpers/node_ext.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/crates/ide-db/src/syntax_helpers/node_ext.rs b/crates/ide-db/src/syntax_helpers/node_ext.rs index b890e2b58df..78d6bd008af 100644 --- a/crates/ide-db/src/syntax_helpers/node_ext.rs +++ b/crates/ide-db/src/syntax_helpers/node_ext.rs @@ -2,8 +2,8 @@ use itertools::Itertools; use parser::T; use syntax::{ - ast::{self, HasLoopBody, PathSegmentKind, VisibilityKind}, - AstNode, Preorder, RustLanguage, WalkEvent, + ast::{self, HasLoopBody, PathSegmentKind, VisibilityKind, NameRef}, + AstNode, Preorder, RustLanguage, WalkEvent, AstToken, }; pub fn expr_as_name_ref(expr: &ast::Expr) -> Option { @@ -457,3 +457,10 @@ pub fn parse_tt_as_comma_sep_paths(input: ast::TokenTree) -> Option Option { + let macro_call = string.syntax().parent_ancestors().find_map(ast::MacroCall::cast)?; + let name = macro_call.path()?.segment()?.name_ref()?; + + Some(name) +} \ No newline at end of file From ddf68ea7c330ec4fef8564062af2a2e15dd5f45e Mon Sep 17 00:00:00 2001 From: btwotwo <10519967+btwotwo@users.noreply.github.com> Date: Thu, 6 Oct 2022 16:27:27 +0200 Subject: [PATCH 04/16] Add const list of cargo-defined env variables with descriptions --- .../src/completions/env_vars.rs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/crates/ide-completion/src/completions/env_vars.rs b/crates/ide-completion/src/completions/env_vars.rs index 6cfbd0f922f..eba39bba3d5 100644 --- a/crates/ide-completion/src/completions/env_vars.rs +++ b/crates/ide-completion/src/completions/env_vars.rs @@ -5,6 +5,27 @@ use syntax::{ast, AstToken, AstNode, TextRange, TextSize}; use crate::{context::CompletionContext, CompletionItem, CompletionItemKind}; use super::Completions; +const CARGO_DEFINED_VARS: &[(&str, &str)] = &[ +("CARGO","Path to the cargo binary performing the build"), +("CARGO_MANIFEST_DIR","The directory containing the manifest of your package"), +("CARGO_PKG_VERSION","The full version of your package"), +("CARGO_PKG_VERSION_MAJOR","The major version of your package"), +("CARGO_PKG_VERSION_MINOR","The minor version of your package"), +("CARGO_PKG_VERSION_PATCH","The patch version of your package"), +("CARGO_PKG_VERSION_PRE","The pre-release version of your package"), +("CARGO_PKG_AUTHORS","Colon separated list of authors from the manifest of your package"), +("CARGO_PKG_NAME","The name of your package"), +("CARGO_PKG_DESCRIPTION","The description from the manifest of your package"), +("CARGO_PKG_HOMEPAGE","The home page from the manifest of your package"), +("CARGO_PKG_REPOSITORY","The repository from the manifest of your package"), +("CARGO_PKG_LICENSE","The license from the manifest of your package"), +("CARGO_PKG_LICENSE_FILE","The license file from the manifest of your package"), +("CARGO_PKG_RUST_VERSION","The Rust version from the manifest of your package. Note that this is the minimum Rust version supported by the package, not the current Rust version"), +("CARGO_CRATE_NAME","The name of the crate that is currently being compiled"), +("CARGO_BIN_NAME","The name of the binary that is currently being compiled (if it is a binary). This name does not include any file extension, such as .exe"), +("CARGO_PRIMARY_PACKAGE","This environment variable will be set if the package being built is primary. Primary packages are the ones the user selected on the command-line, either with -p flags or the defaults based on the current directory and the default workspace members. This environment variable will not be set when building dependencies. This is only set when compiling the package (not when running binaries or tests)"), +("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") +]; pub(crate) fn complete_cargo_env_vars( acc: &mut Completions, From bc7080884c92148fdb47701067d1f1dbdef4ed1d Mon Sep 17 00:00:00 2001 From: btwotwo <10519967+btwotwo@users.noreply.github.com> Date: Thu, 6 Oct 2022 16:27:53 +0200 Subject: [PATCH 05/16] Add tests for env var completion --- .../src/completions/env_vars.rs | 54 +++++++++++++++---- 1 file changed, 44 insertions(+), 10 deletions(-) diff --git a/crates/ide-completion/src/completions/env_vars.rs b/crates/ide-completion/src/completions/env_vars.rs index eba39bba3d5..43d67f093e9 100644 --- a/crates/ide-completion/src/completions/env_vars.rs +++ b/crates/ide-completion/src/completions/env_vars.rs @@ -61,21 +61,55 @@ fn is_env_macro(string: &ast::String) -> bool { #[cfg(test)] mod tests { - use expect_test::{expect, Expect}; - use crate::tests::{check_edit}; + use crate::tests::{check_edit, completion_list}; + + fn check(macro_name: &str) { + check_edit("CARGO_BIN_NAME",&format!(r#" + fn main() {{ + let foo = {}!("CAR$0"); + }} + "#, macro_name), &format!(r#" + fn main() {{ + let foo = {}!("CARGO_BIN_NAME"); + }} + "#, macro_name)); + } + #[test] + fn completes_env_variable_in_env() { + check("env") + } #[test] - fn completes_env_variables() { - check_edit("CARGO", - r#" + fn completes_env_variable_in_option_env() { + check("option_env"); + } + + #[test] + fn doesnt_complete_in_random_strings() { + let fixture = r#" fn main() { - let foo = env!("CA$0); + let foo = "CA$0"; } - "# - ,r#" + "#; + + let completions = completion_list(fixture); + assert!(completions.is_empty(), "Completions weren't empty: {}", completions); + } + + #[test] + fn doesnt_complete_in_random_macro() { + let fixture = r#" + macro_rules! bar { + ($($arg:tt)*) => { 0 } + } + fn main() { - let foo = env!("CARGO); + let foo = bar!("CA$0"); + } - "#) + "#; + + let completions = completion_list(fixture); + assert!(completions.is_empty(), "Completions weren't empty: {}", completions); } } \ No newline at end of file From 55c5014b760a5d4bf3c6dc6a19818278ec83816e Mon Sep 17 00:00:00 2001 From: btwotwo <10519967+btwotwo@users.noreply.github.com> Date: Thu, 6 Oct 2022 16:28:33 +0200 Subject: [PATCH 06/16] Replace if with option, add detail for each env variable completion --- .../src/completions/env_vars.rs | 47 ++++++++----------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/crates/ide-completion/src/completions/env_vars.rs b/crates/ide-completion/src/completions/env_vars.rs index 43d67f093e9..e2e0bc142c0 100644 --- a/crates/ide-completion/src/completions/env_vars.rs +++ b/crates/ide-completion/src/completions/env_vars.rs @@ -1,8 +1,8 @@ //! Completes environment variables defined by Cargo (https://doc.rust-lang.org/cargo/reference/environment-variables.html) +use ide_db::syntax_helpers::node_ext::get_outer_macro_name; +use syntax::ast::{self, IsString}; -use syntax::{ast, AstToken, AstNode, TextRange, TextSize}; - -use crate::{context::CompletionContext, CompletionItem, CompletionItemKind}; +use crate::{CompletionItem, CompletionItemKind}; use super::Completions; const CARGO_DEFINED_VARS: &[(&str, &str)] = &[ @@ -29,34 +29,27 @@ const CARGO_DEFINED_VARS: &[(&str, &str)] = &[ pub(crate) fn complete_cargo_env_vars( acc: &mut Completions, - ctx: &CompletionContext<'_>, - original: &ast::String -) { - if !is_env_macro(original) { - return; - } + expanded: &ast::String, +) -> Option<()> { + guard_env_macro(expanded)?; + let range = expanded.text_range_between_quotes()?; - let start = ctx.original_token.text_range().start() + TextSize::from(1); - let cursor = ctx.position.offset; + CARGO_DEFINED_VARS.iter().for_each(|(var, detail)| { + let mut item = CompletionItem::new(CompletionItemKind::Keyword, range, *var); + item.detail(*detail); + item.add_to(acc); + }); - CompletionItem::new(CompletionItemKind::Binding, TextRange::new(start, cursor), "CARGO").add_to(acc); + Some(()) } -fn is_env_macro(string: &ast::String) -> bool { - //todo: replace copypaste from format_string with separate function - (|| { - let macro_call = string.syntax().parent_ancestors().find_map(ast::MacroCall::cast)?; - let name = macro_call.path()?.segment()?.name_ref()?; +fn guard_env_macro(string: &ast::String) -> Option<()> { + let name = get_outer_macro_name(string)?; + if !matches!(name.text().as_str(), "env" | "option_env") { + return None; + } - if !matches!(name.text().as_str(), - "env" | "option_env") { - return None; - } - - - Some(()) - })() - .is_some() + Some(()) } #[cfg(test)] @@ -112,4 +105,4 @@ mod tests { let completions = completion_list(fixture); assert!(completions.is_empty(), "Completions weren't empty: {}", completions); } -} \ No newline at end of file +} From 386f46a5a1bff654fc8b29ea84dcd18c29c75c18 Mon Sep 17 00:00:00 2001 From: btwotwo <10519967+btwotwo@users.noreply.github.com> Date: Thu, 6 Oct 2022 16:29:23 +0200 Subject: [PATCH 07/16] Use helper method in is_format_string --- crates/ide-db/src/syntax_helpers/format_string.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/ide-db/src/syntax_helpers/format_string.rs b/crates/ide-db/src/syntax_helpers/format_string.rs index f48a5700866..894a4b0fbb3 100644 --- a/crates/ide-db/src/syntax_helpers/format_string.rs +++ b/crates/ide-db/src/syntax_helpers/format_string.rs @@ -1,9 +1,10 @@ //! Tools to work with format string literals for the `format_args!` family of macros. use syntax::{ - ast::{self, IsString}, - AstNode, AstToken, TextRange, TextSize, + ast::{self, IsString}, TextRange, TextSize, }; +use super::node_ext::get_outer_macro_name; + pub fn is_format_string(string: &ast::String) -> bool { // Check if `string` is a format string argument of a macro invocation. // `string` is a string literal, mapped down into the innermost macro expansion. @@ -14,8 +15,7 @@ pub fn is_format_string(string: &ast::String) -> bool { // This setup lets us correctly highlight the components of `concat!("{}", "bla")` format // strings. It still fails for `concat!("{", "}")`, but that is rare. (|| { - let macro_call = string.syntax().parent_ancestors().find_map(ast::MacroCall::cast)?; - let name = macro_call.path()?.segment()?.name_ref()?; + let name = get_outer_macro_name(string)?; if !matches!( name.text().as_str(), From c660a5ce3444a8c84eeb3afe8ea7cdf12b025e25 Mon Sep 17 00:00:00 2001 From: btwotwo <10519967+btwotwo@users.noreply.github.com> Date: Thu, 6 Oct 2022 16:42:31 +0200 Subject: [PATCH 08/16] Remove unnecessary dereference --- crates/ide-completion/src/completions/env_vars.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ide-completion/src/completions/env_vars.rs b/crates/ide-completion/src/completions/env_vars.rs index e2e0bc142c0..3c573a5f984 100644 --- a/crates/ide-completion/src/completions/env_vars.rs +++ b/crates/ide-completion/src/completions/env_vars.rs @@ -35,7 +35,7 @@ pub(crate) fn complete_cargo_env_vars( let range = expanded.text_range_between_quotes()?; CARGO_DEFINED_VARS.iter().for_each(|(var, detail)| { - let mut item = CompletionItem::new(CompletionItemKind::Keyword, range, *var); + let mut item = CompletionItem::new(CompletionItemKind::Keyword, range, var); item.detail(*detail); item.add_to(acc); }); From 8a92910f9765689a6422de42a208724f8d3935e8 Mon Sep 17 00:00:00 2001 From: btwotwo <10519967+btwotwo@users.noreply.github.com> Date: Thu, 6 Oct 2022 16:53:17 +0200 Subject: [PATCH 09/16] Formatting --- .../src/completions/env_vars.rs | 21 ++++++++++++------- .../src/syntax_helpers/format_string.rs | 3 ++- crates/ide-db/src/syntax_helpers/node_ext.rs | 6 +++--- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/crates/ide-completion/src/completions/env_vars.rs b/crates/ide-completion/src/completions/env_vars.rs index 3c573a5f984..809f6d4b7c1 100644 --- a/crates/ide-completion/src/completions/env_vars.rs +++ b/crates/ide-completion/src/completions/env_vars.rs @@ -27,10 +27,7 @@ const CARGO_DEFINED_VARS: &[(&str, &str)] = &[ ("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") ]; -pub(crate) fn complete_cargo_env_vars( - acc: &mut Completions, - expanded: &ast::String, -) -> Option<()> { +pub(crate) fn complete_cargo_env_vars(acc: &mut Completions, expanded: &ast::String) -> Option<()> { guard_env_macro(expanded)?; let range = expanded.text_range_between_quotes()?; @@ -57,15 +54,25 @@ mod tests { use crate::tests::{check_edit, completion_list}; fn check(macro_name: &str) { - check_edit("CARGO_BIN_NAME",&format!(r#" + check_edit( + "CARGO_BIN_NAME", + &format!( + r#" fn main() {{ let foo = {}!("CAR$0"); }} - "#, macro_name), &format!(r#" + "#, + macro_name + ), + &format!( + r#" fn main() {{ let foo = {}!("CARGO_BIN_NAME"); }} - "#, macro_name)); + "#, + macro_name + ), + ); } #[test] fn completes_env_variable_in_env() { diff --git a/crates/ide-db/src/syntax_helpers/format_string.rs b/crates/ide-db/src/syntax_helpers/format_string.rs index 894a4b0fbb3..8836167630a 100644 --- a/crates/ide-db/src/syntax_helpers/format_string.rs +++ b/crates/ide-db/src/syntax_helpers/format_string.rs @@ -1,6 +1,7 @@ //! Tools to work with format string literals for the `format_args!` family of macros. use syntax::{ - ast::{self, IsString}, TextRange, TextSize, + ast::{self, IsString}, + TextRange, TextSize, }; use super::node_ext::get_outer_macro_name; diff --git a/crates/ide-db/src/syntax_helpers/node_ext.rs b/crates/ide-db/src/syntax_helpers/node_ext.rs index 78d6bd008af..5265b3d1615 100644 --- a/crates/ide-db/src/syntax_helpers/node_ext.rs +++ b/crates/ide-db/src/syntax_helpers/node_ext.rs @@ -2,8 +2,8 @@ use itertools::Itertools; use parser::T; use syntax::{ - ast::{self, HasLoopBody, PathSegmentKind, VisibilityKind, NameRef}, - AstNode, Preorder, RustLanguage, WalkEvent, AstToken, + ast::{self, HasLoopBody, NameRef, PathSegmentKind, VisibilityKind}, + AstNode, AstToken, Preorder, RustLanguage, WalkEvent, }; pub fn expr_as_name_ref(expr: &ast::Expr) -> Option { @@ -463,4 +463,4 @@ pub fn get_outer_macro_name(string: &ast::String) -> Option { let name = macro_call.path()?.segment()?.name_ref()?; Some(name) -} \ No newline at end of file +} From f458ea15d637db94d1e95c595aab6c1d7f3937e0 Mon Sep 17 00:00:00 2001 From: btwotwo <10519967+btwotwo@users.noreply.github.com> Date: Thu, 6 Oct 2022 21:34:19 +0200 Subject: [PATCH 10/16] Make helper method less specific --- crates/ide-db/src/syntax_helpers/format_string.rs | 4 ++-- crates/ide-db/src/syntax_helpers/node_ext.rs | 8 +++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/crates/ide-db/src/syntax_helpers/format_string.rs b/crates/ide-db/src/syntax_helpers/format_string.rs index 8836167630a..b3e227ddd27 100644 --- a/crates/ide-db/src/syntax_helpers/format_string.rs +++ b/crates/ide-db/src/syntax_helpers/format_string.rs @@ -4,7 +4,7 @@ use syntax::{ TextRange, TextSize, }; -use super::node_ext::get_outer_macro_name; +use super::node_ext::get_outer_macro; pub fn is_format_string(string: &ast::String) -> bool { // Check if `string` is a format string argument of a macro invocation. @@ -16,7 +16,7 @@ pub fn is_format_string(string: &ast::String) -> bool { // This setup lets us correctly highlight the components of `concat!("{}", "bla")` format // strings. It still fails for `concat!("{", "}")`, but that is rare. (|| { - let name = get_outer_macro_name(string)?; + let name = get_outer_macro(string)?.path()?.segment()?.name_ref()?; if !matches!( name.text().as_str(), diff --git a/crates/ide-db/src/syntax_helpers/node_ext.rs b/crates/ide-db/src/syntax_helpers/node_ext.rs index 5265b3d1615..9cfcdfb77b9 100644 --- a/crates/ide-db/src/syntax_helpers/node_ext.rs +++ b/crates/ide-db/src/syntax_helpers/node_ext.rs @@ -2,7 +2,7 @@ use itertools::Itertools; use parser::T; use syntax::{ - ast::{self, HasLoopBody, NameRef, PathSegmentKind, VisibilityKind}, + ast::{self, HasLoopBody, MacroCall, PathSegmentKind, VisibilityKind}, AstNode, AstToken, Preorder, RustLanguage, WalkEvent, }; @@ -458,9 +458,7 @@ pub fn parse_tt_as_comma_sep_paths(input: ast::TokenTree) -> Option Option { +pub fn get_outer_macro(string: &ast::String) -> Option { let macro_call = string.syntax().parent_ancestors().find_map(ast::MacroCall::cast)?; - let name = macro_call.path()?.segment()?.name_ref()?; - - Some(name) + Some(macro_call) } From e2d3c1506f10861f9c864e3611fa41387c11546b Mon Sep 17 00:00:00 2001 From: btwotwo <10519967+btwotwo@users.noreply.github.com> Date: Thu, 6 Oct 2022 21:35:01 +0200 Subject: [PATCH 11/16] Restrict auto-completion for only built-in macros --- .../src/completions/env_vars.rs | 61 +++++++++++++++---- 1 file changed, 49 insertions(+), 12 deletions(-) diff --git a/crates/ide-completion/src/completions/env_vars.rs b/crates/ide-completion/src/completions/env_vars.rs index 809f6d4b7c1..5483fdf31a2 100644 --- a/crates/ide-completion/src/completions/env_vars.rs +++ b/crates/ide-completion/src/completions/env_vars.rs @@ -1,8 +1,9 @@ //! Completes environment variables defined by Cargo (https://doc.rust-lang.org/cargo/reference/environment-variables.html) -use ide_db::syntax_helpers::node_ext::get_outer_macro_name; +use hir::Semantics; +use ide_db::{syntax_helpers::node_ext::get_outer_macro, RootDatabase}; use syntax::ast::{self, IsString}; -use crate::{CompletionItem, CompletionItemKind}; +use crate::{context::CompletionContext, CompletionItem, CompletionItemKind}; use super::Completions; const CARGO_DEFINED_VARS: &[(&str, &str)] = &[ @@ -27,8 +28,12 @@ const CARGO_DEFINED_VARS: &[(&str, &str)] = &[ ("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") ]; -pub(crate) fn complete_cargo_env_vars(acc: &mut Completions, expanded: &ast::String) -> Option<()> { - guard_env_macro(expanded)?; +pub(crate) fn complete_cargo_env_vars( + acc: &mut Completions, + ctx: &CompletionContext<'_>, + expanded: &ast::String, +) -> Option<()> { + guard_env_macro(expanded, &ctx.sema, &ctx.db)?; let range = expanded.text_range_between_quotes()?; 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 Some(()) } -fn guard_env_macro(string: &ast::String) -> Option<()> { - let name = get_outer_macro_name(string)?; - if !matches!(name.text().as_str(), "env" | "option_env") { - return None; - } +fn guard_env_macro( + string: &ast::String, + semantics: &Semantics<'_, RootDatabase>, + db: &RootDatabase, +) -> Option<()> { + let call = get_outer_macro(string)?; + let name = call.path()?.segment()?.name_ref()?; + let makro = semantics.resolve_macro_call(&call)?; - Some(()) + match name.text().as_str() { + "env" | "option_env" if makro.kind(db) == hir::MacroKind::BuiltIn => Some(()), + _ => None, + } } #[cfg(test)] @@ -58,19 +69,29 @@ mod tests { "CARGO_BIN_NAME", &format!( r#" + #[rustc_builtin_macro] + macro_rules! {} {{ + ($var:literal) => {{ 0 }} + }} + fn main() {{ let foo = {}!("CAR$0"); }} "#, - macro_name + macro_name, macro_name ), &format!( r#" + #[rustc_builtin_macro] + macro_rules! {} {{ + ($var:literal) => {{ 0 }} + }} + fn main() {{ let foo = {}!("CARGO_BIN_NAME"); }} "#, - macro_name + macro_name, macro_name ), ); } @@ -112,4 +133,20 @@ mod tests { let completions = completion_list(fixture); assert!(completions.is_empty(), "Completions weren't empty: {}", completions); } + + #[test] + fn doesnt_complete_for_shadowed_macro() { + let fixture = r#" + macro_rules! env { + ($var:literal) => { 0 } + } + + fn main() { + let foo = env!("CA$0"); + } + "#; + + let completions = completion_list(fixture); + assert!(completions.is_empty(), "Completions weren't empty: {}", completions) + } } From 3732d159b2e7dec436f8bb91c53832e56b1f2720 Mon Sep 17 00:00:00 2001 From: btwotwo <10519967+btwotwo@users.noreply.github.com> Date: Thu, 6 Oct 2022 21:35:12 +0200 Subject: [PATCH 12/16] Pass context to env vars completion --- crates/ide-completion/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/ide-completion/src/lib.rs b/crates/ide-completion/src/lib.rs index c5718ec40f5..9d0044e55f5 100644 --- a/crates/ide-completion/src/lib.rs +++ b/crates/ide-completion/src/lib.rs @@ -183,7 +183,7 @@ pub fn completions( CompletionAnalysis::String { original, expanded: Some(expanded) } => { completions::extern_abi::complete_extern_abi(acc, ctx, expanded); completions::format_string::format_string(acc, ctx, original, expanded); - completions::env_vars::complete_cargo_env_vars(acc, expanded); + completions::env_vars::complete_cargo_env_vars(acc, ctx, expanded); } CompletionAnalysis::UnexpandedAttrTT { colon_prefix, From 3c39668ca464919ed4180049945b3e2b9bf6191a Mon Sep 17 00:00:00 2001 From: btwotwo <10519967+btwotwo@users.noreply.github.com> Date: Tue, 11 Oct 2022 19:23:41 +0200 Subject: [PATCH 13/16] Remove extra parameter, access Db through semantics --- crates/ide-completion/src/completions/env_vars.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/ide-completion/src/completions/env_vars.rs b/crates/ide-completion/src/completions/env_vars.rs index 5483fdf31a2..a3a97130394 100644 --- a/crates/ide-completion/src/completions/env_vars.rs +++ b/crates/ide-completion/src/completions/env_vars.rs @@ -18,7 +18,8 @@ const CARGO_DEFINED_VARS: &[(&str, &str)] = &[ ("CARGO_PKG_NAME","The name of your package"), ("CARGO_PKG_DESCRIPTION","The description from the manifest of your package"), ("CARGO_PKG_HOMEPAGE","The home page from the manifest of your package"), -("CARGO_PKG_REPOSITORY","The repository from the manifest of your package"), + ("CARGO_PKG_REPOSITORY", +"The repository from the manifest of your package"), ("CARGO_PKG_LICENSE","The license from the manifest of your package"), ("CARGO_PKG_LICENSE_FILE","The license file from the manifest of your package"), ("CARGO_PKG_RUST_VERSION","The Rust version from the manifest of your package. Note that this is the minimum Rust version supported by the package, not the current Rust version"), @@ -33,7 +34,7 @@ pub(crate) fn complete_cargo_env_vars( ctx: &CompletionContext<'_>, expanded: &ast::String, ) -> Option<()> { - guard_env_macro(expanded, &ctx.sema, &ctx.db)?; + guard_env_macro(expanded, &ctx.sema)?; let range = expanded.text_range_between_quotes()?; CARGO_DEFINED_VARS.iter().for_each(|(var, detail)| { @@ -48,11 +49,11 @@ pub(crate) fn complete_cargo_env_vars( fn guard_env_macro( string: &ast::String, semantics: &Semantics<'_, RootDatabase>, - db: &RootDatabase, ) -> Option<()> { let call = get_outer_macro(string)?; let name = call.path()?.segment()?.name_ref()?; let makro = semantics.resolve_macro_call(&call)?; + let db = semantics.db; match name.text().as_str() { "env" | "option_env" if makro.kind(db) == hir::MacroKind::BuiltIn => Some(()), From a807cc3afbd5bd081f4f5ff0bad1b5c403d7a690 Mon Sep 17 00:00:00 2001 From: btwotwo <10519967+btwotwo@users.noreply.github.com> Date: Tue, 11 Oct 2022 19:28:42 +0200 Subject: [PATCH 14/16] Rename `get_outer_macro` to `macro_call_for_string_token` --- crates/ide-completion/src/completions/env_vars.rs | 7 +++---- crates/ide-db/src/syntax_helpers/format_string.rs | 5 ++--- crates/ide-db/src/syntax_helpers/node_ext.rs | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/crates/ide-completion/src/completions/env_vars.rs b/crates/ide-completion/src/completions/env_vars.rs index a3a97130394..14dc17321f9 100644 --- a/crates/ide-completion/src/completions/env_vars.rs +++ b/crates/ide-completion/src/completions/env_vars.rs @@ -1,11 +1,10 @@ //! Completes environment variables defined by Cargo (https://doc.rust-lang.org/cargo/reference/environment-variables.html) use hir::Semantics; -use ide_db::{syntax_helpers::node_ext::get_outer_macro, RootDatabase}; +use ide_db::{syntax_helpers::node_ext::macro_call_for_string_token, RootDatabase}; use syntax::ast::{self, IsString}; -use crate::{context::CompletionContext, CompletionItem, CompletionItemKind}; +use crate::{context::CompletionContext, CompletionItem, CompletionItemKind, completions::Completions}; -use super::Completions; const CARGO_DEFINED_VARS: &[(&str, &str)] = &[ ("CARGO","Path to the cargo binary performing the build"), ("CARGO_MANIFEST_DIR","The directory containing the manifest of your package"), @@ -50,7 +49,7 @@ fn guard_env_macro( string: &ast::String, semantics: &Semantics<'_, RootDatabase>, ) -> Option<()> { - let call = get_outer_macro(string)?; + let call = macro_call_for_string_token(string)?; let name = call.path()?.segment()?.name_ref()?; let makro = semantics.resolve_macro_call(&call)?; let db = semantics.db; diff --git a/crates/ide-db/src/syntax_helpers/format_string.rs b/crates/ide-db/src/syntax_helpers/format_string.rs index b3e227ddd27..caa579e322b 100644 --- a/crates/ide-db/src/syntax_helpers/format_string.rs +++ b/crates/ide-db/src/syntax_helpers/format_string.rs @@ -3,8 +3,7 @@ use syntax::{ ast::{self, IsString}, TextRange, TextSize, }; - -use super::node_ext::get_outer_macro; +use crate::syntax_helpers::node_ext::macro_call_for_string_token; pub fn is_format_string(string: &ast::String) -> bool { // Check if `string` is a format string argument of a macro invocation. @@ -16,7 +15,7 @@ pub fn is_format_string(string: &ast::String) -> bool { // This setup lets us correctly highlight the components of `concat!("{}", "bla")` format // strings. It still fails for `concat!("{", "}")`, but that is rare. (|| { - let name = get_outer_macro(string)?.path()?.segment()?.name_ref()?; + let name = macro_call_for_string_token(string)?.path()?.segment()?.name_ref()?; if !matches!( name.text().as_str(), diff --git a/crates/ide-db/src/syntax_helpers/node_ext.rs b/crates/ide-db/src/syntax_helpers/node_ext.rs index 9cfcdfb77b9..39710b8f13e 100644 --- a/crates/ide-db/src/syntax_helpers/node_ext.rs +++ b/crates/ide-db/src/syntax_helpers/node_ext.rs @@ -458,7 +458,7 @@ pub fn parse_tt_as_comma_sep_paths(input: ast::TokenTree) -> Option Option { +pub fn macro_call_for_string_token(string: &ast::String) -> Option { let macro_call = string.syntax().parent_ancestors().find_map(ast::MacroCall::cast)?; Some(macro_call) } From b5e87ac11177165da100b21fbc2f83736add6ac8 Mon Sep 17 00:00:00 2001 From: btwotwo <10519967+btwotwo@users.noreply.github.com> Date: Tue, 11 Oct 2022 19:29:09 +0200 Subject: [PATCH 15/16] Fix formatting for cargo vars list --- .../src/completions/env_vars.rs | 39 +++++++++---------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/crates/ide-completion/src/completions/env_vars.rs b/crates/ide-completion/src/completions/env_vars.rs index 14dc17321f9..d7cccf0bedc 100644 --- a/crates/ide-completion/src/completions/env_vars.rs +++ b/crates/ide-completion/src/completions/env_vars.rs @@ -6,26 +6,25 @@ use syntax::ast::{self, IsString}; use crate::{context::CompletionContext, CompletionItem, CompletionItemKind, completions::Completions}; const CARGO_DEFINED_VARS: &[(&str, &str)] = &[ -("CARGO","Path to the cargo binary performing the build"), -("CARGO_MANIFEST_DIR","The directory containing the manifest of your package"), -("CARGO_PKG_VERSION","The full version of your package"), -("CARGO_PKG_VERSION_MAJOR","The major version of your package"), -("CARGO_PKG_VERSION_MINOR","The minor version of your package"), -("CARGO_PKG_VERSION_PATCH","The patch version of your package"), -("CARGO_PKG_VERSION_PRE","The pre-release version of your package"), -("CARGO_PKG_AUTHORS","Colon separated list of authors from the manifest of your package"), -("CARGO_PKG_NAME","The name of your package"), -("CARGO_PKG_DESCRIPTION","The description from the manifest of your package"), -("CARGO_PKG_HOMEPAGE","The home page from the manifest of your package"), - ("CARGO_PKG_REPOSITORY", -"The repository from the manifest of your package"), -("CARGO_PKG_LICENSE","The license from the manifest of your package"), -("CARGO_PKG_LICENSE_FILE","The license file from the manifest of your package"), -("CARGO_PKG_RUST_VERSION","The Rust version from the manifest of your package. Note that this is the minimum Rust version supported by the package, not the current Rust version"), -("CARGO_CRATE_NAME","The name of the crate that is currently being compiled"), -("CARGO_BIN_NAME","The name of the binary that is currently being compiled (if it is a binary). This name does not include any file extension, such as .exe"), -("CARGO_PRIMARY_PACKAGE","This environment variable will be set if the package being built is primary. Primary packages are the ones the user selected on the command-line, either with -p flags or the defaults based on the current directory and the default workspace members. This environment variable will not be set when building dependencies. This is only set when compiling the package (not when running binaries or tests)"), -("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") + ("CARGO","Path to the cargo binary performing the build"), + ("CARGO_MANIFEST_DIR","The directory containing the manifest of your package"), + ("CARGO_PKG_VERSION","The full version of your package"), + ("CARGO_PKG_VERSION_MAJOR","The major version of your package"), + ("CARGO_PKG_VERSION_MINOR","The minor version of your package"), + ("CARGO_PKG_VERSION_PATCH","The patch version of your package"), + ("CARGO_PKG_VERSION_PRE","The pre-release version of your package"), + ("CARGO_PKG_AUTHORS","Colon separated list of authors from the manifest of your package"), + ("CARGO_PKG_NAME","The name of your package"), + ("CARGO_PKG_DESCRIPTION","The description from the manifest of your package"), + ("CARGO_PKG_HOMEPAGE","The home page from the manifest of your package"), + ("CARGO_PKG_REPOSITORY","The repository from the manifest of your package"), + ("CARGO_PKG_LICENSE","The license from the manifest of your package"), + ("CARGO_PKG_LICENSE_FILE","The license file from the manifest of your package"), + ("CARGO_PKG_RUST_VERSION","The Rust version from the manifest of your package. Note that this is the minimum Rust version supported by the package, not the current Rust version"), + ("CARGO_CRATE_NAME","The name of the crate that is currently being compiled"), + ("CARGO_BIN_NAME","The name of the binary that is currently being compiled (if it is a binary). This name does not include any file extension, such as .exe"), + ("CARGO_PRIMARY_PACKAGE","This environment variable will be set if the package being built is primary. Primary packages are the ones the user selected on the command-line, either with -p flags or the defaults based on the current directory and the default workspace members. This environment variable will not be set when building dependencies. This is only set when compiling the package (not when running binaries or tests)"), + ("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") ]; pub(crate) fn complete_cargo_env_vars( From 2b2b9f8c7332314b151fd8287a8b02c34a20a5a7 Mon Sep 17 00:00:00 2001 From: btwotwo <10519967+btwotwo@users.noreply.github.com> Date: Tue, 11 Oct 2022 19:53:22 +0200 Subject: [PATCH 16/16] Formatting --- crates/ide-completion/src/completions/env_vars.rs | 9 ++++----- crates/ide-db/src/syntax_helpers/format_string.rs | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/crates/ide-completion/src/completions/env_vars.rs b/crates/ide-completion/src/completions/env_vars.rs index d7cccf0bedc..09e95e53de6 100644 --- a/crates/ide-completion/src/completions/env_vars.rs +++ b/crates/ide-completion/src/completions/env_vars.rs @@ -3,7 +3,9 @@ use hir::Semantics; use ide_db::{syntax_helpers::node_ext::macro_call_for_string_token, RootDatabase}; use syntax::ast::{self, IsString}; -use crate::{context::CompletionContext, CompletionItem, CompletionItemKind, completions::Completions}; +use crate::{ + completions::Completions, context::CompletionContext, CompletionItem, CompletionItemKind, +}; const CARGO_DEFINED_VARS: &[(&str, &str)] = &[ ("CARGO","Path to the cargo binary performing the build"), @@ -44,10 +46,7 @@ pub(crate) fn complete_cargo_env_vars( Some(()) } -fn guard_env_macro( - string: &ast::String, - semantics: &Semantics<'_, RootDatabase>, -) -> Option<()> { +fn guard_env_macro(string: &ast::String, semantics: &Semantics<'_, RootDatabase>) -> Option<()> { let call = macro_call_for_string_token(string)?; let name = call.path()?.segment()?.name_ref()?; let makro = semantics.resolve_macro_call(&call)?; diff --git a/crates/ide-db/src/syntax_helpers/format_string.rs b/crates/ide-db/src/syntax_helpers/format_string.rs index caa579e322b..2d6927cee99 100644 --- a/crates/ide-db/src/syntax_helpers/format_string.rs +++ b/crates/ide-db/src/syntax_helpers/format_string.rs @@ -1,9 +1,9 @@ //! Tools to work with format string literals for the `format_args!` family of macros. +use crate::syntax_helpers::node_ext::macro_call_for_string_token; use syntax::{ ast::{self, IsString}, TextRange, TextSize, }; -use crate::syntax_helpers::node_ext::macro_call_for_string_token; pub fn is_format_string(string: &ast::String) -> bool { // Check if `string` is a format string argument of a macro invocation.