Skip to content

feat: Enable rust_analyzer for cfgs when code is being analyzed by rust-analyzer #15528

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Sep 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions crates/base-db/src/fixture.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,8 @@ impl ChangeFixture {
meta.edition,
Some(crate_name.clone().into()),
version,
meta.cfg,
Default::default(),
meta.cfg.clone(),
Some(meta.cfg),
meta.env,
false,
origin,
Expand All @@ -200,7 +200,7 @@ impl ChangeFixture {
} else if meta.path == "/main.rs" || meta.path == "/lib.rs" {
assert!(default_crate_root.is_none());
default_crate_root = Some(file_id);
default_cfg = meta.cfg;
default_cfg.extend(meta.cfg.into_iter());
default_env.extend(meta.env.iter().map(|(x, y)| (x.to_owned(), y.to_owned())));
default_target_data_layout = meta.target_data_layout;
}
Expand All @@ -220,8 +220,8 @@ impl ChangeFixture {
Edition::CURRENT,
Some(CrateName::new("test").unwrap().into()),
None,
default_cfg,
Default::default(),
default_cfg.clone(),
Some(default_cfg),
default_env,
false,
CrateOrigin::Local { repo: None, name: None },
Expand Down
26 changes: 26 additions & 0 deletions crates/cfg/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,32 @@ impl CfgOptions {
}
}

impl Extend<CfgAtom> for CfgOptions {
fn extend<T: IntoIterator<Item = CfgAtom>>(&mut self, iter: T) {
iter.into_iter().for_each(|cfg_flag| _ = self.enabled.insert(cfg_flag));
}
}

impl IntoIterator for CfgOptions {
type Item = <FxHashSet<CfgAtom> as IntoIterator>::Item;

type IntoIter = <FxHashSet<CfgAtom> as IntoIterator>::IntoIter;

fn into_iter(self) -> Self::IntoIter {
<FxHashSet<CfgAtom> as IntoIterator>::into_iter(self.enabled)
}
}

impl<'a> IntoIterator for &'a CfgOptions {
type Item = <&'a FxHashSet<CfgAtom> as IntoIterator>::Item;

type IntoIter = <&'a FxHashSet<CfgAtom> as IntoIterator>::IntoIter;

fn into_iter(self) -> Self::IntoIter {
<&FxHashSet<CfgAtom> as IntoIterator>::into_iter(&self.enabled)
}
}

#[derive(Default, Clone, Debug, PartialEq, Eq)]
pub struct CfgDiff {
// Invariants: No duplicates, no atom that's both in `enable` and `disable`.
Expand Down
57 changes: 34 additions & 23 deletions crates/ide-completion/src/completions/attribute/cfg.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
//! Completion for cfg

use std::iter;

use ide_db::SymbolKind;
use itertools::Itertools;
use syntax::SyntaxKind;
use syntax::{algo, ast::Ident, AstToken, Direction, NodeOrToken, SyntaxKind};

use crate::{completions::Completions, context::CompletionContext, CompletionItem};

Expand All @@ -15,31 +13,44 @@ pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_>) {
acc.add(completion.build(ctx.db));
};

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("target_arch") => KNOWN_ARCH.iter().copied().for_each(add_completion),
Some("target_env") => KNOWN_ENV.iter().copied().for_each(add_completion),
Some("target_os") => KNOWN_OS.iter().copied().for_each(add_completion),
Some("target_vendor") => KNOWN_VENDOR.iter().copied().for_each(add_completion),
Some("target_endian") => ["little", "big"].into_iter().for_each(add_completion),
Some(name) => ctx.krate.potential_cfg(ctx.db).get_cfg_values(name).cloned().for_each(|s| {
let insert_text = format!(r#""{s}""#);
let mut item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s);
item.insert_text(insert_text);
// FIXME: Move this into context/analysis.rs
let previous = ctx
.original_token
.prev_token()
.and_then(|it| {
if matches!(it.kind(), SyntaxKind::EQ) {
Some(it.into())
} else {
algo::non_trivia_sibling(it.into(), Direction::Prev)
}
})
.filter(|t| matches!(t.kind(), SyntaxKind::EQ))
.and_then(|it| algo::non_trivia_sibling(it.prev_sibling_or_token()?, Direction::Prev))
.map(|it| match it {
NodeOrToken::Node(_) => None,
NodeOrToken::Token(t) => Ident::cast(t),
});
match previous {
Some(None) => (),
Some(Some(p)) => match p.text() {
"target_arch" => KNOWN_ARCH.iter().copied().for_each(add_completion),
"target_env" => KNOWN_ENV.iter().copied().for_each(add_completion),
"target_os" => KNOWN_OS.iter().copied().for_each(add_completion),
"target_vendor" => KNOWN_VENDOR.iter().copied().for_each(add_completion),
"target_endian" => ["little", "big"].into_iter().for_each(add_completion),
name => ctx.krate.potential_cfg(ctx.db).get_cfg_values(name).cloned().for_each(|s| {
let insert_text = format!(r#""{s}""#);
let mut item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s);
item.insert_text(insert_text);

acc.add(item.build(ctx.db));
}),
acc.add(item.build(ctx.db));
}),
},
None => ctx.krate.potential_cfg(ctx.db).get_cfg_keys().cloned().unique().for_each(|s| {
let item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s);
acc.add(item.build(ctx.db));
}),
};
}
}

const KNOWN_ARCH: [&str; 20] = [
Expand Down
38 changes: 33 additions & 5 deletions crates/ide-completion/src/tests/attribute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,6 @@ struct Foo;
)
}

#[test]
fn inside_nested_attr() {
check(r#"#[cfg($0)]"#, expect![[]])
}

#[test]
fn with_existing_attr() {
check(
Expand Down Expand Up @@ -635,6 +630,32 @@ struct Foo;
mod cfg {
use super::*;

#[test]
fn inside_cfg() {
check(
r#"
//- /main.rs cfg:test,dbg=false,opt_level=2
#[cfg($0)]
"#,
expect![[r#"
ba dbg
ba opt_level
ba test
"#]],
);
check(
r#"
//- /main.rs cfg:test,dbg=false,opt_level=2
#[cfg(b$0)]
"#,
expect![[r#"
ba dbg
ba opt_level
ba test
"#]],
);
}

#[test]
fn cfg_target_endian() {
check(
Expand All @@ -644,6 +665,13 @@ mod cfg {
ba little
"#]],
);
check(
r#"#[cfg(target_endian = b$0"#,
expect![[r#"
ba big
ba little
"#]],
);
}
}

Expand Down
6 changes: 4 additions & 2 deletions crates/ide-db/src/search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,6 @@ impl Definition {
}

// def is crate root
// FIXME: We don't do searches for crates currently, as a crate does not actually have a single name
if let &Definition::Module(module) = self {
if module.is_crate_root() {
return SearchScope::reverse_dependencies(db, module.krate());
Expand Down Expand Up @@ -393,7 +392,10 @@ impl<'a> FindUsages<'a> {
let name = match self.def {
// special case crate modules as these do not have a proper name
Definition::Module(module) if module.is_crate_root() => {
// FIXME: This assumes the crate name is always equal to its display name when it really isn't
// FIXME: This assumes the crate name is always equal to its display name when it
// really isn't
// we should instead look at the dependency edge name and recursively search our way
// up the ancestors
module
.krate()
.display_name(self.sema.db)
Expand Down
36 changes: 23 additions & 13 deletions crates/project-model/src/workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//! metadata` or `rust-project.json`) into representation stored in the salsa
//! database -- `CrateGraph`.

use std::{collections::VecDeque, fmt, fs, process::Command, str::FromStr, sync};
use std::{collections::VecDeque, fmt, fs, iter, process::Command, str::FromStr, sync};

use anyhow::{format_err, Context};
use base_db::{
Expand Down Expand Up @@ -730,6 +730,7 @@ fn project_json_to_crate_graph(
)
});

let r_a_cfg_flag = CfgFlag::Atom("rust_analyzer".to_owned());
let mut cfg_cache: FxHashMap<&str, Vec<CfgFlag>> = FxHashMap::default();
let crates: FxHashMap<CrateId, CrateId> = project
.crates()
Expand Down Expand Up @@ -765,7 +766,12 @@ fn project_json_to_crate_graph(
*edition,
display_name.clone(),
version.clone(),
target_cfgs.iter().chain(cfg.iter()).cloned().collect(),
target_cfgs
.iter()
.chain(cfg.iter())
.chain(iter::once(&r_a_cfg_flag))
.cloned()
.collect(),
None,
env,
*is_proc_macro,
Expand Down Expand Up @@ -820,7 +826,7 @@ fn cargo_to_crate_graph(
sysroot: Option<&Sysroot>,
rustc_cfg: Vec<CfgFlag>,
override_cfg: &CfgOverrides,
// Don't compute cfg and use this if present
// Don't compute cfg and use this if present, only used for the sysroot experiment hack
forced_cfg: Option<CfgOptions>,
build_scripts: &WorkspaceBuildScripts,
target_layout: TargetLayoutLoadResult,
Expand All @@ -842,12 +848,7 @@ fn cargo_to_crate_graph(
None => (SysrootPublicDeps::default(), None),
};

let cfg_options = {
let mut cfg_options = CfgOptions::default();
cfg_options.extend(rustc_cfg);
cfg_options.insert_atom("debug_assertions".into());
cfg_options
};
let cfg_options = create_cfg_options(rustc_cfg);

// Mapping of a package to its library target
let mut pkg_to_lib_crate = FxHashMap::default();
Expand All @@ -866,6 +867,9 @@ fn cargo_to_crate_graph(
if cargo[pkg].is_local {
cfg_options.insert_atom("test".into());
}
if cargo[pkg].is_member {
cfg_options.insert_atom("rust_analyzer".into());
}

if !override_cfg.global.is_empty() {
cfg_options.apply_diff(override_cfg.global.clone());
Expand Down Expand Up @@ -1029,8 +1033,8 @@ fn detached_files_to_crate_graph(
None => (SysrootPublicDeps::default(), None),
};

let mut cfg_options = CfgOptions::default();
cfg_options.extend(rustc_cfg);
let mut cfg_options = create_cfg_options(rustc_cfg);
cfg_options.insert_atom("rust_analyzer".into());

for detached_file in detached_files {
let file_id = match load(detached_file) {
Expand Down Expand Up @@ -1295,8 +1299,7 @@ fn sysroot_to_crate_graph(
channel: Option<ReleaseChannel>,
) -> (SysrootPublicDeps, Option<CrateId>) {
let _p = profile::span("sysroot_to_crate_graph");
let mut cfg_options = CfgOptions::default();
cfg_options.extend(rustc_cfg.clone());
let cfg_options = create_cfg_options(rustc_cfg.clone());
let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = match &sysroot.hack_cargo_workspace {
Some(cargo) => handle_hack_cargo_workspace(
load,
Expand Down Expand Up @@ -1475,3 +1478,10 @@ fn inject_cargo_env(package: &PackageData, env: &mut Env) {

env.set("CARGO_PKG_LICENSE_FILE", String::new());
}

fn create_cfg_options(rustc_cfg: Vec<CfgFlag>) -> CfgOptions {
let mut cfg_options = CfgOptions::default();
cfg_options.extend(rustc_cfg);
cfg_options.insert_atom("debug_assertions".into());
cfg_options
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
cfg_options: CfgOptions(
[
"debug_assertions",
"rust_analyzer",
"test",
],
),
Expand Down Expand Up @@ -81,6 +82,7 @@
cfg_options: CfgOptions(
[
"debug_assertions",
"rust_analyzer",
"test",
],
),
Expand Down Expand Up @@ -151,6 +153,7 @@
cfg_options: CfgOptions(
[
"debug_assertions",
"rust_analyzer",
"test",
],
),
Expand Down Expand Up @@ -221,6 +224,7 @@
cfg_options: CfgOptions(
[
"debug_assertions",
"rust_analyzer",
"test",
],
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
cfg_options: CfgOptions(
[
"debug_assertions",
"rust_analyzer",
"test",
],
),
Expand Down Expand Up @@ -81,6 +82,7 @@
cfg_options: CfgOptions(
[
"debug_assertions",
"rust_analyzer",
"test",
],
),
Expand Down Expand Up @@ -151,6 +153,7 @@
cfg_options: CfgOptions(
[
"debug_assertions",
"rust_analyzer",
"test",
],
),
Expand Down Expand Up @@ -221,6 +224,7 @@
cfg_options: CfgOptions(
[
"debug_assertions",
"rust_analyzer",
"test",
],
),
Expand Down
Loading