about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLukas Wirth <lukastw97@gmail.com>2021-12-11 15:55:25 +0100
committerLukas Wirth <lukastw97@gmail.com>2021-12-11 15:55:25 +0100
commit0001a425702f3c3f163d25f63cf3eebd321097dc (patch)
treec4b786074a8c716c2d6cce55350037ffb6222d47
parent03f6d92ae4796ca0f18aab689f0c0bf311e2072c (diff)
downloadrust-0001a425702f3c3f163d25f63cf3eebd321097dc.tar.gz
rust-0001a425702f3c3f163d25f63cf3eebd321097dc.zip
fix: Fix lint completions not working for unclosed attributes
-rw-r--r--crates/ide_completion/src/completions/attribute.rs18
-rw-r--r--crates/ide_completion/src/tests/attribute.rs14
-rw-r--r--crates/ide_db/src/helpers.rs32
3 files changed, 39 insertions, 25 deletions
diff --git a/crates/ide_completion/src/completions/attribute.rs b/crates/ide_completion/src/completions/attribute.rs
index f09015efb60..d92e311915a 100644
--- a/crates/ide_completion/src/completions/attribute.rs
+++ b/crates/ide_completion/src/completions/attribute.rs
@@ -30,16 +30,12 @@ pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext)
         None => None,
     };
     match (name_ref, attribute.token_tree()) {
-        (Some(path), Some(token_tree)) => match path.text().as_str() {
-            "repr" => repr::complete_repr(acc, ctx, token_tree),
-            "derive" => {
-                derive::complete_derive(acc, ctx, &parse_tt_as_comma_sep_paths(token_tree)?)
-            }
-            "feature" => {
-                lint::complete_lint(acc, ctx, &parse_tt_as_comma_sep_paths(token_tree)?, FEATURES)
-            }
+        (Some(path), Some(tt)) if tt.l_paren_token().is_some() => match path.text().as_str() {
+            "repr" => repr::complete_repr(acc, ctx, tt),
+            "derive" => derive::complete_derive(acc, ctx, &parse_tt_as_comma_sep_paths(tt)?),
+            "feature" => lint::complete_lint(acc, ctx, &parse_tt_as_comma_sep_paths(tt)?, FEATURES),
             "allow" | "warn" | "deny" | "forbid" => {
-                let existing_lints = parse_tt_as_comma_sep_paths(token_tree)?;
+                let existing_lints = parse_tt_as_comma_sep_paths(tt)?;
                 lint::complete_lint(acc, ctx, &existing_lints, DEFAULT_LINTS);
                 lint::complete_lint(acc, ctx, &existing_lints, CLIPPY_LINTS);
                 lint::complete_lint(acc, ctx, &existing_lints, RUSTDOC_LINTS);
@@ -49,8 +45,8 @@ pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext)
             }
             _ => (),
         },
-        (None, Some(_)) => (),
-        _ => complete_new_attribute(acc, ctx, attribute),
+        (_, Some(_)) => (),
+        (_, None) => complete_new_attribute(acc, ctx, attribute),
     }
     Some(())
 }
diff --git a/crates/ide_completion/src/tests/attribute.rs b/crates/ide_completion/src/tests/attribute.rs
index c3dce61e7d4..c90d4966f37 100644
--- a/crates/ide_completion/src/tests/attribute.rs
+++ b/crates/ide_completion/src/tests/attribute.rs
@@ -831,6 +831,20 @@ mod lint {
             r#"#[allow(rustdoc::bare_urls)] struct Test;"#,
         );
     }
+
+    #[test]
+    fn lint_unclosed() {
+        check_edit(
+            "deprecated",
+            r#"#[allow(dep$0 struct Test;"#,
+            r#"#[allow(deprecated struct Test;"#,
+        );
+        check_edit(
+            "bare_urls",
+            r#"#[allow(rustdoc::$0 struct Test;"#,
+            r#"#[allow(rustdoc::bare_urls struct Test;"#,
+        );
+    }
 }
 
 mod repr {
diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs
index 1b9cb7ff51c..7e78b7136cb 100644
--- a/crates/ide_db/src/helpers.rs
+++ b/crates/ide_db/src/helpers.rs
@@ -305,20 +305,24 @@ pub fn lint_eq_or_in_group(lint: &str, lint_is: &str) -> bool {
     }
 }
 
-/// Parses the input token tree as comma separated paths.
+/// Parses the input token tree as comma separated plain paths.
 pub fn parse_tt_as_comma_sep_paths(input: ast::TokenTree) -> Option<Vec<ast::Path>> {
-    let r_paren = input.r_paren_token()?;
-    let tokens = input
-        .syntax()
-        .children_with_tokens()
-        .skip(1)
-        .take_while(|it| it.as_token() != Some(&r_paren));
+    let r_paren = input.r_paren_token();
+    let tokens =
+        input.syntax().children_with_tokens().skip(1).map_while(|it| match it.into_token() {
+            // seeing a keyword means the attribute is unclosed so stop parsing here
+            Some(tok) if tok.kind().is_keyword() => None,
+            // don't include the right token tree parenthesis if it exists
+            tok @ Some(_) if tok == r_paren => None,
+            // only nodes that we can find are other TokenTrees, those are unexpected in this parse though
+            None => None,
+            Some(tok) => Some(tok),
+        });
     let input_expressions = tokens.into_iter().group_by(|tok| tok.kind() == T![,]);
-    Some(
-        input_expressions
-            .into_iter()
-            .filter_map(|(is_sep, group)| (!is_sep).then(|| group))
-            .filter_map(|mut tokens| ast::Path::parse(&tokens.join("")).ok())
-            .collect::<Vec<ast::Path>>(),
-    )
+    let paths = input_expressions
+        .into_iter()
+        .filter_map(|(is_sep, group)| (!is_sep).then(|| group))
+        .filter_map(|mut tokens| ast::Path::parse(&tokens.join("")).ok())
+        .collect();
+    Some(paths)
 }