From c1bf1f88ad4199e48d2615dbf9479e602b5e9eff Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Thu, 17 Jun 2021 21:15:35 +0200 Subject: [PATCH] Complete `repr` attribute parameters --- .../src/completions/attribute.rs | 2 + .../src/completions/attribute/repr.rs | 199 ++++++++++++++++++ crates/parser/src/grammar/params.rs | 3 +- 3 files changed, 202 insertions(+), 2 deletions(-) create mode 100644 crates/ide_completion/src/completions/attribute/repr.rs diff --git a/crates/ide_completion/src/completions/attribute.rs b/crates/ide_completion/src/completions/attribute.rs index f3b11e72dd..9780d01aef 100644 --- a/crates/ide_completion/src/completions/attribute.rs +++ b/crates/ide_completion/src/completions/attribute.rs @@ -17,12 +17,14 @@ use crate::{ mod derive; mod lint; +mod repr; pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { let attribute = ctx.attribute_under_caret.as_ref()?; match (attribute.path().and_then(|p| p.as_single_name_ref()), attribute.token_tree()) { (Some(path), Some(token_tree)) => match path.text().as_str() { "derive" => derive::complete_derive(acc, ctx, token_tree), + "repr" => repr::complete_repr(acc, ctx, token_tree), "feature" => lint::complete_lint(acc, ctx, token_tree, FEATURES), "allow" | "warn" | "deny" | "forbid" => { lint::complete_lint(acc, ctx, token_tree.clone(), DEFAULT_LINTS); diff --git a/crates/ide_completion/src/completions/attribute/repr.rs b/crates/ide_completion/src/completions/attribute/repr.rs new file mode 100644 index 0000000000..92a262a43f --- /dev/null +++ b/crates/ide_completion/src/completions/attribute/repr.rs @@ -0,0 +1,199 @@ +//! Completion for representations. + +use syntax::ast; + +use crate::{ + context::CompletionContext, + item::{CompletionItem, CompletionItemKind, CompletionKind}, + Completions, +}; + +pub(super) fn complete_repr( + acc: &mut Completions, + ctx: &CompletionContext, + derive_input: ast::TokenTree, +) { + if let Some(existing_reprs) = super::parse_comma_sep_input(derive_input) { + for repr_completion in REPR_COMPLETIONS { + if existing_reprs + .iter() + .any(|it| repr_completion.label == it || repr_completion.collides.contains(&&**it)) + { + continue; + } + let mut item = CompletionItem::new( + CompletionKind::Attribute, + ctx.source_range(), + repr_completion.label, + ); + item.kind(CompletionItemKind::Attribute); + if let Some(lookup) = repr_completion.lookup { + item.lookup_by(lookup); + } + if let Some((snippet, cap)) = repr_completion.snippet.zip(ctx.config.snippet_cap) { + item.insert_snippet(cap, snippet); + } + item.add_to(acc); + } + } +} + +struct ReprCompletion { + label: &'static str, + snippet: Option<&'static str>, + lookup: Option<&'static str>, + collides: &'static [&'static str], +} + +const fn attr(label: &'static str, collides: &'static [&'static str]) -> ReprCompletion { + ReprCompletion { label, snippet: None, lookup: None, collides } +} + +#[rustfmt::skip] +const REPR_COMPLETIONS: &[ReprCompletion] = &[ + ReprCompletion { label: "align($0)", snippet: Some("align($0)"), lookup: Some("align"), collides: &["transparent", "packed"] }, + attr("packed", &["transparent", "align"]), + attr("transparent", &["C", "u8", "u16", "u32", "u64", "u128", "usize", "i8", "i16", "i32", "i64", "i128", "isize"]), + attr("C", &["transparent"]), + attr("u8", &["transparent", "u16", "u32", "u64", "u128", "usize", "i8", "i16", "i32", "i64", "i128", "isize"]), + attr("u16", &["transparent", "u8", "u32", "u64", "u128", "usize", "i8", "i16", "i32", "i64", "i128", "isize"]), + attr("u32", &["transparent", "u8", "u16", "u64", "u128", "usize", "i8", "i16", "i32", "i64", "i128", "isize"]), + attr("u64", &["transparent", "u8", "u16", "u32", "u128", "usize", "i8", "i16", "i32", "i64", "i128", "isize"]), + attr("u128", &["transparent", "u8", "u16", "u32", "u64", "usize", "i8", "i16", "i32", "i64", "i128", "isize"]), + attr("usize", &["transparent", "u8", "u16", "u32", "u64", "u128", "i8", "i16", "i32", "i64", "i128", "isize"]), + attr("i8", &["transparent", "u8", "u16", "u32", "u64", "u128", "usize", "i16", "i32", "i64", "i128", "isize"]), + attr("i16", &["transparent", "u8", "u16", "u32", "u64", "u128", "usize", "i8", "i32", "i64", "i128", "isize"]), + attr("i32", &["transparent", "u8", "u16", "u32", "u64", "u128", "usize", "i8", "i16", "i64", "i128", "isize"]), + attr("i64", &["transparent", "u8", "u16", "u32", "u64", "u128", "usize", "i8", "i16", "i32", "i128", "isize"]), + attr("i28", &["transparent", "u8", "u16", "u32", "u64", "u128", "usize", "i8", "i16", "i32", "i64", "isize"]), + attr("isize", &["transparent", "u8", "u16", "u32", "u64", "u128", "usize", "i8", "i16", "i32", "i64", "i128"]), +]; + +#[cfg(test)] +mod tests { + use expect_test::{expect, Expect}; + + use crate::tests::completion_list; + + fn check(ra_fixture: &str, expect: Expect) { + let actual = completion_list(ra_fixture); + expect.assert_eq(&actual); + } + + #[test] + fn no_completion_for_incorrect_repr() { + check(r#"#[repr{$0)] struct Test;"#, expect![[]]) + } + + #[test] + fn empty() { + check( + r#"#[repr($0)] struct Test;"#, + expect![[r#" + at align($0) + at packed + at transparent + at C + at u8 + at u16 + at u32 + at u64 + at u128 + at usize + at i8 + at i16 + at i32 + at i64 + at i28 + at isize + "#]], + ); + } + + #[test] + fn transparent() { + check(r#"#[repr(transparent, $0)] struct Test;"#, expect![[r#""#]]); + } + + #[test] + fn align() { + check( + r#"#[repr(align(1), $0)] struct Test;"#, + expect![[r#" + at align($0) + at transparent + at C + at u8 + at u16 + at u32 + at u64 + at u128 + at usize + at i8 + at i16 + at i32 + at i64 + at i28 + at isize + "#]], + ); + } + + #[test] + fn packed() { + check( + r#"#[repr(packed, $0)] struct Test;"#, + expect![[r#" + at transparent + at C + at u8 + at u16 + at u32 + at u64 + at u128 + at usize + at i8 + at i16 + at i32 + at i64 + at i28 + at isize + "#]], + ); + } + + #[test] + fn c() { + check( + r#"#[repr(C, $0)] struct Test;"#, + expect![[r#" + at align($0) + at packed + at u8 + at u16 + at u32 + at u64 + at u128 + at usize + at i8 + at i16 + at i32 + at i64 + at i28 + at isize + "#]], + ); + } + + #[test] + fn prim() { + check( + r#"#[repr(usize, $0)] struct Test;"#, + expect![[r#" + at align($0) + at packed + at C + "#]], + ); + } +} diff --git a/crates/parser/src/grammar/params.rs b/crates/parser/src/grammar/params.rs index 01ee26a530..5a78675fb4 100644 --- a/crates/parser/src/grammar/params.rs +++ b/crates/parser/src/grammar/params.rs @@ -184,8 +184,7 @@ fn opt_self_param(p: &mut Parser, m: Marker) -> Result<(), Marker> { if !matches!( (p.current(), la1, la2, la3), (T![&], T![self], _, _) - | (T![&], T![mut], T![self], _) - | (T![&], LIFETIME_IDENT, T![self], _) + | (T![&], T![mut] | LIFETIME_IDENT, T![self], _) | (T![&], LIFETIME_IDENT, T![mut], T![self]) ) { return Err(m);