about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crates/base_db/src/fixture.rs2
-rw-r--r--crates/base_db/src/input.rs13
-rw-r--r--crates/cfg/src/lib.rs20
-rw-r--r--crates/hir/src/lib.rs4
-rw-r--r--crates/ide/src/lib.rs1
-rw-r--r--crates/ide_completion/src/completions/attribute.rs15
-rw-r--r--crates/ide_completion/src/completions/attribute/cfg.rs126
-rw-r--r--crates/project_model/src/workspace.rs4
8 files changed, 185 insertions, 0 deletions
diff --git a/crates/base_db/src/fixture.rs b/crates/base_db/src/fixture.rs
index 6ce3777106a..6d3b1266e6f 100644
--- a/crates/base_db/src/fixture.rs
+++ b/crates/base_db/src/fixture.rs
@@ -131,6 +131,7 @@ impl ChangeFixture {
                     meta.cfg,
                     meta.env,
                     Default::default(),
+                    Default::default(),
                 );
                 let prev = crates.insert(crate_name.clone(), crate_id);
                 assert!(prev.is_none());
@@ -160,6 +161,7 @@ impl ChangeFixture {
                 default_cfg,
                 Env::default(),
                 Default::default(),
+                Default::default(),
             );
         } else {
             for (from, to) in crate_deps {
diff --git a/crates/base_db/src/input.rs b/crates/base_db/src/input.rs
index 23cb0c839ff..d99388f7163 100644
--- a/crates/base_db/src/input.rs
+++ b/crates/base_db/src/input.rs
@@ -192,6 +192,7 @@ pub struct CrateData {
     pub env: Env,
     pub dependencies: Vec<Dependency>,
     pub proc_macro: Vec<ProcMacro>,
+    pub features: FxHashMap<String, Vec<String>>,
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
@@ -221,6 +222,7 @@ impl CrateGraph {
         cfg_options: CfgOptions,
         env: Env,
         proc_macro: Vec<ProcMacro>,
+        features: FxHashMap<String, Vec<String>>,
     ) -> CrateId {
         let data = CrateData {
             root_file_id: file_id,
@@ -230,6 +232,7 @@ impl CrateGraph {
             env,
             proc_macro,
             dependencies: Vec::new(),
+            features,
         };
         let crate_id = CrateId(self.arena.len() as u32);
         let prev = self.arena.insert(crate_id, data);
@@ -506,6 +509,7 @@ mod tests {
             CfgOptions::default(),
             Env::default(),
             Default::default(),
+            Default::default(),
         );
         let crate2 = graph.add_crate_root(
             FileId(2u32),
@@ -514,6 +518,7 @@ mod tests {
             CfgOptions::default(),
             Env::default(),
             Default::default(),
+            Default::default(),
         );
         let crate3 = graph.add_crate_root(
             FileId(3u32),
@@ -522,6 +527,7 @@ mod tests {
             CfgOptions::default(),
             Env::default(),
             Default::default(),
+            Default::default(),
         );
         assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok());
         assert!(graph.add_dep(crate2, CrateName::new("crate3").unwrap(), crate3).is_ok());
@@ -538,6 +544,7 @@ mod tests {
             CfgOptions::default(),
             Env::default(),
             Default::default(),
+            Default::default(),
         );
         let crate2 = graph.add_crate_root(
             FileId(2u32),
@@ -546,6 +553,7 @@ mod tests {
             CfgOptions::default(),
             Env::default(),
             Default::default(),
+            Default::default(),
         );
         assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok());
         assert!(graph.add_dep(crate2, CrateName::new("crate2").unwrap(), crate2).is_err());
@@ -561,6 +569,7 @@ mod tests {
             CfgOptions::default(),
             Env::default(),
             Default::default(),
+            Default::default(),
         );
         let crate2 = graph.add_crate_root(
             FileId(2u32),
@@ -569,6 +578,7 @@ mod tests {
             CfgOptions::default(),
             Env::default(),
             Default::default(),
+            Default::default(),
         );
         let crate3 = graph.add_crate_root(
             FileId(3u32),
@@ -577,6 +587,7 @@ mod tests {
             CfgOptions::default(),
             Env::default(),
             Default::default(),
+            Default::default(),
         );
         assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok());
         assert!(graph.add_dep(crate2, CrateName::new("crate3").unwrap(), crate3).is_ok());
@@ -592,6 +603,7 @@ mod tests {
             CfgOptions::default(),
             Env::default(),
             Default::default(),
+            Default::default(),
         );
         let crate2 = graph.add_crate_root(
             FileId(2u32),
@@ -600,6 +612,7 @@ mod tests {
             CfgOptions::default(),
             Env::default(),
             Default::default(),
+            Default::default(),
         );
         assert!(graph
             .add_dep(crate1, CrateName::normalize_dashes("crate-name-with-dashes"), crate2)
diff --git a/crates/cfg/src/lib.rs b/crates/cfg/src/lib.rs
index 916d39a0b4a..9a4baa63694 100644
--- a/crates/cfg/src/lib.rs
+++ b/crates/cfg/src/lib.rs
@@ -50,6 +50,26 @@ impl CfgOptions {
             self.enabled.remove(&atom);
         }
     }
+
+    pub fn get_cfg_keys(&self) -> Vec<&SmolStr> {
+        self.enabled
+            .iter()
+            .map(|x| match x {
+                CfgAtom::Flag(key) => key,
+                CfgAtom::KeyValue { key, .. } => key,
+            })
+            .collect()
+    }
+
+    pub fn get_cfg_values(&self, cfg_key: &str) -> Vec<&SmolStr> {
+        self.enabled
+            .iter()
+            .filter_map(|x| match x {
+                CfgAtom::KeyValue { key, value } if cfg_key == key => Some(value),
+                _ => None,
+            })
+            .collect()
+    }
 }
 
 #[derive(Clone, Debug, PartialEq, Eq)]
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 88490fea98f..2b2aaec940b 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -233,6 +233,10 @@ impl Crate {
     pub fn cfg(&self, db: &dyn HirDatabase) -> CfgOptions {
         db.crate_graph()[self.id].cfg_options.clone()
     }
+
+    pub fn features(&self, db: &dyn HirDatabase) -> Vec<String> {
+        db.crate_graph()[self.id].features.iter().map(|(feat, _)| feat.clone()).collect()
+    }
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 3798f32cc5c..0693869a208 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -220,6 +220,7 @@ impl Analysis {
             cfg_options,
             Env::default(),
             Default::default(),
+            Default::default(),
         );
         change.change_file(file_id, Some(Arc::new(text)));
         change.set_crate_graph(crate_graph);
diff --git a/crates/ide_completion/src/completions/attribute.rs b/crates/ide_completion/src/completions/attribute.rs
index 78fc30e1674..cc4f4b2af72 100644
--- a/crates/ide_completion/src/completions/attribute.rs
+++ b/crates/ide_completion/src/completions/attribute.rs
@@ -15,6 +15,7 @@ use crate::{
     Completions,
 };
 
+mod cfg;
 mod derive;
 mod lint;
 mod repr;
@@ -30,6 +31,9 @@ pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext)
                 lint::complete_lint(acc, ctx, token_tree.clone(), DEFAULT_LINTS);
                 lint::complete_lint(acc, ctx, token_tree, CLIPPY_LINTS);
             }
+            "cfg" => {
+                cfg::complete_cfg(acc, ctx);
+            }
             _ => (),
         },
         (None, Some(_)) => (),
@@ -852,4 +856,15 @@ mod tests {
             "#]],
         );
     }
+
+    #[test]
+    fn test_cfg() {
+        check(
+            r#"#[cfg(target_endian = $0"#,
+            expect![[r#"
+                at little
+                at big
+"#]],
+        );
+    }
 }
diff --git a/crates/ide_completion/src/completions/attribute/cfg.rs b/crates/ide_completion/src/completions/attribute/cfg.rs
new file mode 100644
index 00000000000..71e659563cb
--- /dev/null
+++ b/crates/ide_completion/src/completions/attribute/cfg.rs
@@ -0,0 +1,126 @@
+//! Completion for cfg
+
+use std::iter;
+
+use syntax::SyntaxKind;
+
+use crate::{
+    completions::Completions, context::CompletionContext, item::CompletionKind, CompletionItem,
+    CompletionItemKind,
+};
+
+pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext) {
+    let add_completion = |item: &&str| {
+        let mut completion =
+            CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), *item);
+        completion.insert_text(format!(r#""{}""#, item));
+        completion.kind(CompletionItemKind::Attribute);
+        acc.add(completion.build());
+    };
+
+    let previous = iter::successors(ctx.original_token.prev_token(), |t| {
+        (matches!(t.kind(), SyntaxKind::EQ) || t.kind().is_trivia())
+            .then(|| t.prev_token())
+            .flatten()
+    })
+    .find(|t| matches!(t.kind(), SyntaxKind::IDENT));
+
+    match previous.as_ref().map(|p| p.text()) {
+        Some("feature") => {
+            ctx.krate.map(|krate| {
+                krate.features(ctx.db).iter().for_each(|f| {
+                    let mut item = CompletionItem::new(
+                        CompletionKind::Attribute,
+                        ctx.source_range(),
+                        f.clone(),
+                    );
+                    item.insert_text(format!(r#""{}""#, f));
+
+                    acc.add(item.build())
+                })
+            });
+        }
+        Some("target_arch") => KNOWN_ARCH.iter().for_each(add_completion),
+        Some("target_env") => KNOWN_ENV.iter().for_each(add_completion),
+        Some("target_os") => KNOWN_OS.iter().for_each(add_completion),
+        Some("target_vendor") => KNOWN_VENDOR.iter().for_each(add_completion),
+        Some("target_endian") => ["little", "big"].iter().for_each(add_completion),
+        Some(name) => {
+            ctx.krate.map(|krate| {
+                krate.cfg(ctx.db).get_cfg_values(&name).iter().for_each(|s| {
+                    let mut item = CompletionItem::new(
+                        CompletionKind::Attribute,
+                        ctx.source_range(),
+                        s.as_str(),
+                    );
+                    item.insert_text(format!(r#""{}""#, s));
+
+                    acc.add(item.build());
+                })
+            });
+        }
+        None => {
+            ctx.krate.map(|krate| {
+                krate.cfg(ctx.db).get_cfg_keys().iter().for_each(|s| {
+                    let item = CompletionItem::new(
+                        CompletionKind::Attribute,
+                        ctx.source_range(),
+                        s.as_str(),
+                    );
+                    acc.add(item.build());
+                })
+            });
+        }
+    };
+}
+
+const KNOWN_ARCH: [&'static str; 19] = [
+    "aarch64",
+    "arm",
+    "avr",
+    "hexagon",
+    "mips",
+    "mips64",
+    "msp430",
+    "nvptx64",
+    "powerpc",
+    "powerpc64",
+    "riscv32",
+    "riscv64",
+    "s390x",
+    "sparc",
+    "sparc64",
+    "wasm32",
+    "wasm64",
+    "x86",
+    "x86_64",
+];
+
+const KNOWN_ENV: [&'static str; 7] =
+    ["eabihf", "gnu", "gnueabihf", "msvc", "relibc", "sgx", "uclibc"];
+
+const KNOWN_OS: [&'static str; 20] = [
+    "cuda",
+    "dragonfly",
+    "emscripten",
+    "freebsd",
+    "fuchsia",
+    "haiku",
+    "hermit",
+    "illumos",
+    "l4re",
+    "linux",
+    "netbsd",
+    "none",
+    "openbsd",
+    "psp",
+    "redox",
+    "solaris",
+    "uefi",
+    "unknown",
+    "vxworks",
+    "windows",
+];
+
+const KNOWN_VENDOR: [&'static str; 8] =
+    ["apple", "fortanix", "nvidia", "pc", "sony", "unknown", "wrs", "uwp"];
diff --git a/crates/project_model/src/workspace.rs b/crates/project_model/src/workspace.rs
index d8217f714eb..9ee3fc1a328 100644
--- a/crates/project_model/src/workspace.rs
+++ b/crates/project_model/src/workspace.rs
@@ -387,6 +387,7 @@ fn project_json_to_crate_graph(
                     cfg_options,
                     env,
                     proc_macro.unwrap_or_default(),
+                    Default::default(),
                 ),
             )
         })
@@ -582,6 +583,7 @@ fn detached_files_to_crate_graph(
             cfg_options.clone(),
             Env::default(),
             Vec::new(),
+            Default::default(),
         );
 
         for (name, krate) in public_deps.iter() {
@@ -726,6 +728,7 @@ fn add_target_crate_root(
         cfg_options,
         env,
         proc_macro,
+        pkg.features.clone(),
     );
 
     crate_id
@@ -755,6 +758,7 @@ fn sysroot_to_crate_graph(
                 cfg_options.clone(),
                 env,
                 proc_macro,
+                Default::default(),
             );
             Some((krate, crate_id))
         })