Use symbol in cfg

This commit is contained in:
Lukas Wirth 2024-07-16 10:36:59 +02:00
parent 93024ad411
commit c30bdfcc84
22 changed files with 147 additions and 92 deletions

4
Cargo.lock generated
View file

@ -146,10 +146,10 @@ dependencies = [
"arbitrary", "arbitrary",
"derive_arbitrary", "derive_arbitrary",
"expect-test", "expect-test",
"intern",
"mbe", "mbe",
"oorandom", "oorandom",
"rustc-hash", "rustc-hash",
"smol_str",
"syntax", "syntax",
"tt", "tt",
] ]
@ -1416,6 +1416,7 @@ dependencies = [
"cargo_metadata", "cargo_metadata",
"cfg", "cfg",
"expect-test", "expect-test",
"intern",
"itertools", "itertools",
"la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"paths", "paths",
@ -1656,6 +1657,7 @@ dependencies = [
"ide", "ide",
"ide-db", "ide-db",
"ide-ssr", "ide-ssr",
"intern",
"itertools", "itertools",
"load-cargo", "load-cargo",
"lsp-server 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", "lsp-server 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",

View file

@ -50,7 +50,7 @@ debug = 2
[workspace.dependencies] [workspace.dependencies]
# local crates # local crates
base-db = { path = "./crates/base-db", version = "0.0.0" } base-db = { path = "./crates/base-db", version = "0.0.0" }
cfg = { path = "./crates/cfg", version = "0.0.0" } cfg = { path = "./crates/cfg", version = "0.0.0", features = ["tt"] }
flycheck = { path = "./crates/flycheck", version = "0.0.0" } flycheck = { path = "./crates/flycheck", version = "0.0.0" }
hir = { path = "./crates/hir", version = "0.0.0" } hir = { path = "./crates/hir", version = "0.0.0" }
hir-def = { path = "./crates/hir-def", version = "0.0.0" } hir-def = { path = "./crates/hir-def", version = "0.0.0" }

View file

@ -15,8 +15,8 @@ doctest = false
rustc-hash.workspace = true rustc-hash.workspace = true
# locals deps # locals deps
tt.workspace = true tt = { workspace = true, optional = true }
smol_str.workspace = true intern.workspace = true
[dev-dependencies] [dev-dependencies]
expect-test = "1.4.1" expect-test = "1.4.1"

View file

@ -2,20 +2,20 @@
//! //!
//! See: <https://doc.rust-lang.org/reference/conditional-compilation.html#conditional-compilation> //! See: <https://doc.rust-lang.org/reference/conditional-compilation.html#conditional-compilation>
use std::{fmt, slice::Iter as SliceIter}; use std::fmt;
use smol_str::SmolStr; use intern::Symbol;
/// A simple configuration value passed in from the outside. /// A simple configuration value passed in from the outside.
#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum CfgAtom { pub enum CfgAtom {
/// eg. `#[cfg(test)]` /// eg. `#[cfg(test)]`
Flag(SmolStr), Flag(Symbol),
/// eg. `#[cfg(target_os = "linux")]` /// eg. `#[cfg(target_os = "linux")]`
/// ///
/// Note that a key can have multiple values that are all considered "active" at the same time. /// Note that a key can have multiple values that are all considered "active" at the same time.
/// For example, `#[cfg(target_feature = "sse")]` and `#[cfg(target_feature = "sse2")]`. /// For example, `#[cfg(target_feature = "sse")]` and `#[cfg(target_feature = "sse2")]`.
KeyValue { key: SmolStr, value: SmolStr }, KeyValue { key: Symbol, value: Symbol },
} }
impl fmt::Display for CfgAtom { impl fmt::Display for CfgAtom {
@ -44,6 +44,7 @@ impl From<CfgAtom> for CfgExpr {
} }
impl CfgExpr { impl CfgExpr {
#[cfg(feature = "tt")]
pub fn parse<S>(tt: &tt::Subtree<S>) -> CfgExpr { pub fn parse<S>(tt: &tt::Subtree<S>) -> CfgExpr {
next_cfg_expr(&mut tt.token_trees.iter()).unwrap_or(CfgExpr::Invalid) next_cfg_expr(&mut tt.token_trees.iter()).unwrap_or(CfgExpr::Invalid)
} }
@ -63,7 +64,11 @@ impl CfgExpr {
} }
} }
} }
fn next_cfg_expr<S>(it: &mut SliceIter<'_, tt::TokenTree<S>>) -> Option<CfgExpr> {
#[cfg(feature = "tt")]
fn next_cfg_expr<S>(it: &mut std::slice::Iter<'_, tt::TokenTree<S>>) -> Option<CfgExpr> {
use intern::sym;
let name = match it.next() { let name = match it.next() {
None => return None, None => return None,
Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) => ident.sym.clone(), Some(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) => ident.sym.clone(),
@ -77,9 +82,7 @@ fn next_cfg_expr<S>(it: &mut SliceIter<'_, tt::TokenTree<S>>) -> Option<CfgExpr>
Some(tt::TokenTree::Leaf(tt::Leaf::Literal(literal))) => { Some(tt::TokenTree::Leaf(tt::Leaf::Literal(literal))) => {
it.next(); it.next();
it.next(); it.next();
// FIXME: escape? CfgAtom::KeyValue { key: name, value: literal.symbol.clone() }.into()
let value = literal.symbol.as_str().into();
CfgAtom::KeyValue { key: name.as_str().into(), value }.into()
} }
_ => return Some(CfgExpr::Invalid), _ => return Some(CfgExpr::Invalid),
} }
@ -88,14 +91,16 @@ fn next_cfg_expr<S>(it: &mut SliceIter<'_, tt::TokenTree<S>>) -> Option<CfgExpr>
it.next(); it.next();
let mut sub_it = subtree.token_trees.iter(); let mut sub_it = subtree.token_trees.iter();
let mut subs = std::iter::from_fn(|| next_cfg_expr(&mut sub_it)).collect(); let mut subs = std::iter::from_fn(|| next_cfg_expr(&mut sub_it)).collect();
match name.as_str() { match &name {
"all" => CfgExpr::All(subs), s if *s == sym::all => CfgExpr::All(subs),
"any" => CfgExpr::Any(subs), s if *s == sym::any => CfgExpr::Any(subs),
"not" => CfgExpr::Not(Box::new(subs.pop().unwrap_or(CfgExpr::Invalid))), s if *s == sym::not => {
CfgExpr::Not(Box::new(subs.pop().unwrap_or(CfgExpr::Invalid)))
}
_ => CfgExpr::Invalid, _ => CfgExpr::Invalid,
} }
} }
_ => CfgAtom::Flag(name.as_str().into()).into(), _ => CfgAtom::Flag(name).into(),
}; };
// Eat comma separator // Eat comma separator
@ -111,11 +116,11 @@ fn next_cfg_expr<S>(it: &mut SliceIter<'_, tt::TokenTree<S>>) -> Option<CfgExpr>
impl arbitrary::Arbitrary<'_> for CfgAtom { impl arbitrary::Arbitrary<'_> for CfgAtom {
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> { fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
if u.arbitrary()? { if u.arbitrary()? {
Ok(CfgAtom::Flag(String::arbitrary(u)?.into())) Ok(CfgAtom::Flag(Symbol::intern(<_>::arbitrary(u)?)))
} else { } else {
Ok(CfgAtom::KeyValue { Ok(CfgAtom::KeyValue {
key: String::arbitrary(u)?.into(), key: Symbol::intern(<_>::arbitrary(u)?),
value: String::arbitrary(u)?.into(), value: Symbol::intern(<_>::arbitrary(u)?),
}) })
} }
} }

View file

@ -66,9 +66,9 @@ impl DnfExpr {
} }
} }
res.enabled.sort_unstable(); res.enabled.sort_unstable_by(compare);
res.enabled.dedup(); res.enabled.dedup();
res.disabled.sort_unstable(); res.disabled.sort_unstable_by(compare);
res.disabled.dedup(); res.disabled.dedup();
Some(res) Some(res)
} }
@ -114,14 +114,25 @@ impl DnfExpr {
}; };
// Undo the FxHashMap randomization for consistent output. // Undo the FxHashMap randomization for consistent output.
diff.enable.sort_unstable(); diff.enable.sort_unstable_by(compare);
diff.disable.sort_unstable(); diff.disable.sort_unstable_by(compare);
Some(diff) Some(diff)
}) })
} }
} }
fn compare(a: &CfgAtom, b: &CfgAtom) -> std::cmp::Ordering {
match (a, b) {
(CfgAtom::Flag(a), CfgAtom::Flag(b)) => a.as_str().cmp(b.as_str()),
(CfgAtom::Flag(_), CfgAtom::KeyValue { .. }) => std::cmp::Ordering::Less,
(CfgAtom::KeyValue { .. }, CfgAtom::Flag(_)) => std::cmp::Ordering::Greater,
(CfgAtom::KeyValue { key, value }, CfgAtom::KeyValue { key: key2, value: value2 }) => {
key.as_str().cmp(key2.as_str()).then(value.as_str().cmp(value2.as_str()))
}
}
}
impl fmt::Display for DnfExpr { impl fmt::Display for DnfExpr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.conjunctions.len() != 1 { if self.conjunctions.len() != 1 {

View file

@ -9,9 +9,10 @@ use std::fmt;
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
use intern::Symbol;
pub use cfg_expr::{CfgAtom, CfgExpr}; pub use cfg_expr::{CfgAtom, CfgExpr};
pub use dnf::DnfExpr; pub use dnf::DnfExpr;
use smol_str::SmolStr;
/// Configuration options used for conditional compilation on items with `cfg` attributes. /// Configuration options used for conditional compilation on items with `cfg` attributes.
/// We have two kind of options in different namespaces: atomic options like `unix`, and /// We have two kind of options in different namespaces: atomic options like `unix`, and
@ -48,11 +49,11 @@ impl CfgOptions {
cfg.fold(&|atom| self.enabled.contains(atom)) cfg.fold(&|atom| self.enabled.contains(atom))
} }
pub fn insert_atom(&mut self, key: SmolStr) { pub fn insert_atom(&mut self, key: Symbol) {
self.enabled.insert(CfgAtom::Flag(key)); self.enabled.insert(CfgAtom::Flag(key));
} }
pub fn insert_key_value(&mut self, key: SmolStr, value: SmolStr) { pub fn insert_key_value(&mut self, key: Symbol, value: Symbol) {
self.enabled.insert(CfgAtom::KeyValue { key, value }); self.enabled.insert(CfgAtom::KeyValue { key, value });
} }
@ -66,19 +67,16 @@ impl CfgOptions {
} }
} }
pub fn get_cfg_keys(&self) -> impl Iterator<Item = &SmolStr> { pub fn get_cfg_keys(&self) -> impl Iterator<Item = &Symbol> {
self.enabled.iter().map(|it| match it { self.enabled.iter().map(|it| match it {
CfgAtom::Flag(key) => key, CfgAtom::Flag(key) => key,
CfgAtom::KeyValue { key, .. } => key, CfgAtom::KeyValue { key, .. } => key,
}) })
} }
pub fn get_cfg_values<'a>( pub fn get_cfg_values<'a>(&'a self, cfg_key: &'a str) -> impl Iterator<Item = &'a Symbol> + 'a {
&'a self,
cfg_key: &'a str,
) -> impl Iterator<Item = &'a SmolStr> + 'a {
self.enabled.iter().filter_map(move |it| match it { self.enabled.iter().filter_map(move |it| match it {
CfgAtom::KeyValue { key, value } if cfg_key == key => Some(value), CfgAtom::KeyValue { key, value } if cfg_key == key.as_str() => Some(value),
_ => None, _ => None,
}) })
} }

View file

@ -1,5 +1,6 @@
use arbitrary::{Arbitrary, Unstructured}; use arbitrary::{Arbitrary, Unstructured};
use expect_test::{expect, Expect}; use expect_test::{expect, Expect};
use intern::Symbol;
use mbe::{syntax_node_to_token_tree, DocCommentDesugarMode, DummyTestSpanMap, DUMMY}; use mbe::{syntax_node_to_token_tree, DocCommentDesugarMode, DummyTestSpanMap, DUMMY};
use syntax::{ast, AstNode, Edition}; use syntax::{ast, AstNode, Edition};
@ -65,22 +66,25 @@ fn check_enable_hints(input: &str, opts: &CfgOptions, expected_hints: &[&str]) {
#[test] #[test]
fn test_cfg_expr_parser() { fn test_cfg_expr_parser() {
assert_parse_result("#![cfg(foo)]", CfgAtom::Flag("foo".into()).into()); assert_parse_result("#![cfg(foo)]", CfgAtom::Flag(Symbol::intern("foo")).into());
assert_parse_result("#![cfg(foo,)]", CfgAtom::Flag("foo".into()).into()); assert_parse_result("#![cfg(foo,)]", CfgAtom::Flag(Symbol::intern("foo")).into());
assert_parse_result( assert_parse_result(
"#![cfg(not(foo))]", "#![cfg(not(foo))]",
CfgExpr::Not(Box::new(CfgAtom::Flag("foo".into()).into())), CfgExpr::Not(Box::new(CfgAtom::Flag(Symbol::intern("foo")).into())),
); );
assert_parse_result("#![cfg(foo(bar))]", CfgExpr::Invalid); assert_parse_result("#![cfg(foo(bar))]", CfgExpr::Invalid);
// Only take the first // Only take the first
assert_parse_result(r#"#![cfg(foo, bar = "baz")]"#, CfgAtom::Flag("foo".into()).into()); assert_parse_result(
r#"#![cfg(foo, bar = "baz")]"#,
CfgAtom::Flag(Symbol::intern("foo")).into(),
);
assert_parse_result( assert_parse_result(
r#"#![cfg(all(foo, bar = "baz"))]"#, r#"#![cfg(all(foo, bar = "baz"))]"#,
CfgExpr::All(vec![ CfgExpr::All(vec![
CfgAtom::Flag("foo".into()).into(), CfgAtom::Flag(Symbol::intern("foo")).into(),
CfgAtom::KeyValue { key: "bar".into(), value: "baz".into() }.into(), CfgAtom::KeyValue { key: Symbol::intern("bar"), value: Symbol::intern("baz") }.into(),
]), ]),
); );
@ -90,7 +94,7 @@ fn test_cfg_expr_parser() {
CfgExpr::Not(Box::new(CfgExpr::Invalid)), CfgExpr::Not(Box::new(CfgExpr::Invalid)),
CfgExpr::All(vec![]), CfgExpr::All(vec![]),
CfgExpr::Invalid, CfgExpr::Invalid,
CfgAtom::KeyValue { key: "bar".into(), value: "baz".into() }.into(), CfgAtom::KeyValue { key: Symbol::intern("bar"), value: Symbol::intern("baz") }.into(),
]), ]),
); );
} }
@ -167,7 +171,7 @@ fn hints() {
check_enable_hints("#![cfg(all(a, b))]", &opts, &["enable a and b"]); check_enable_hints("#![cfg(all(a, b))]", &opts, &["enable a and b"]);
opts.insert_atom("test".into()); opts.insert_atom(Symbol::intern("test"));
check_enable_hints("#![cfg(test)]", &opts, &[]); check_enable_hints("#![cfg(test)]", &opts, &[]);
check_enable_hints("#![cfg(not(test))]", &opts, &["disable test"]); check_enable_hints("#![cfg(not(test))]", &opts, &["disable test"]);
@ -180,7 +184,7 @@ fn hints_impossible() {
check_enable_hints("#![cfg(all(test, not(test)))]", &opts, &[]); check_enable_hints("#![cfg(all(test, not(test)))]", &opts, &[]);
opts.insert_atom("test".into()); opts.insert_atom(Symbol::intern("test"));
check_enable_hints("#![cfg(all(test, not(test)))]", &opts, &[]); check_enable_hints("#![cfg(all(test, not(test)))]", &opts, &[]);
} }
@ -188,8 +192,8 @@ fn hints_impossible() {
#[test] #[test]
fn why_inactive() { fn why_inactive() {
let mut opts = CfgOptions::default(); let mut opts = CfgOptions::default();
opts.insert_atom("test".into()); opts.insert_atom(Symbol::intern("test"));
opts.insert_atom("test2".into()); opts.insert_atom(Symbol::intern("test2"));
check_why_inactive("#![cfg(a)]", &opts, expect![["a is disabled"]]); check_why_inactive("#![cfg(a)]", &opts, expect![["a is disabled"]]);
check_why_inactive("#![cfg(not(test))]", &opts, expect![["test is enabled"]]); check_why_inactive("#![cfg(not(test))]", &opts, expect![["test is enabled"]]);

View file

@ -563,7 +563,12 @@ fn concat_bytes_expand(
}; };
for (i, t) in tt.token_trees.iter().enumerate() { for (i, t) in tt.token_trees.iter().enumerate() {
match t { match t {
tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { symbol: text, span, kind, suffix: _ })) => { tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
symbol: text,
span,
kind,
suffix: _,
})) => {
record_span(*span); record_span(*span);
match kind { match kind {
tt::LitKind::Byte => { tt::LitKind::Byte => {

View file

@ -3,6 +3,7 @@ use std::iter::Peekable;
use base_db::CrateId; use base_db::CrateId;
use cfg::{CfgAtom, CfgExpr}; use cfg::{CfgAtom, CfgExpr};
use intern::{sym, Symbol};
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
use syntax::{ use syntax::{
ast::{self, Attr, HasAttrs, Meta, VariantList}, ast::{self, Attr, HasAttrs, Meta, VariantList},
@ -262,13 +263,13 @@ where
let name = match iter.next() { let name = match iter.next() {
None => return None, None => return None,
Some(NodeOrToken::Token(element)) => match element.kind() { Some(NodeOrToken::Token(element)) => match element.kind() {
syntax::T![ident] => element.text().to_owned(), syntax::T![ident] => Symbol::intern(element.text()),
_ => return Some(CfgExpr::Invalid), _ => return Some(CfgExpr::Invalid),
}, },
Some(_) => return Some(CfgExpr::Invalid), Some(_) => return Some(CfgExpr::Invalid),
}; };
let result = match name.as_str() { let result = match &name {
"all" | "any" | "not" => { s if [&sym::all, &sym::any, &sym::not].contains(&s) => {
let mut preds = Vec::new(); let mut preds = Vec::new();
let Some(NodeOrToken::Node(tree)) = iter.next() else { let Some(NodeOrToken::Node(tree)) = iter.next() else {
return Some(CfgExpr::Invalid); return Some(CfgExpr::Invalid);
@ -285,10 +286,12 @@ where
preds.push(pred); preds.push(pred);
} }
} }
let group = match name.as_str() { let group = match &name {
"all" => CfgExpr::All(preds), s if *s == sym::all => CfgExpr::All(preds),
"any" => CfgExpr::Any(preds), s if *s == sym::any => CfgExpr::Any(preds),
"not" => CfgExpr::Not(Box::new(preds.pop().unwrap_or(CfgExpr::Invalid))), s if *s == sym::not => {
CfgExpr::Not(Box::new(preds.pop().unwrap_or(CfgExpr::Invalid)))
}
_ => unreachable!(), _ => unreachable!(),
}; };
Some(group) Some(group)
@ -301,13 +304,15 @@ where
if (value_token.kind() == syntax::SyntaxKind::STRING) => if (value_token.kind() == syntax::SyntaxKind::STRING) =>
{ {
let value = value_token.text(); let value = value_token.text();
let value = value.trim_matches('"').into(); Some(CfgExpr::Atom(CfgAtom::KeyValue {
Some(CfgExpr::Atom(CfgAtom::KeyValue { key: name.into(), value })) key: name,
value: Symbol::intern(value.trim_matches('"')),
}))
} }
_ => None, _ => None,
} }
} }
_ => Some(CfgExpr::Atom(CfgAtom::Flag(name.into()))), _ => Some(CfgExpr::Atom(CfgAtom::Flag(name))),
}, },
}; };
if let Some(NodeOrToken::Token(element)) = iter.peek() { if let Some(NodeOrToken::Token(element)) = iter.peek() {

View file

@ -39,6 +39,7 @@ pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_>) {
"target_vendor" => KNOWN_VENDOR.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), "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| { name => ctx.krate.potential_cfg(ctx.db).get_cfg_values(name).cloned().for_each(|s| {
let s = s.as_str();
let insert_text = format!(r#""{s}""#); let insert_text = format!(r#""{s}""#);
let mut item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s); let mut item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s);
item.insert_text(insert_text); item.insert_text(insert_text);
@ -47,6 +48,7 @@ pub(crate) fn complete_cfg(acc: &mut Completions, ctx: &CompletionContext<'_>) {
}), }),
}, },
None => ctx.krate.potential_cfg(ctx.db).get_cfg_keys().cloned().unique().for_each(|s| { None => ctx.krate.potential_cfg(ctx.db).get_cfg_keys().cloned().unique().for_each(|s| {
let s = s.as_str();
let item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s); let item = CompletionItem::new(SymbolKind::BuiltinAttr, ctx.source_range(), s);
acc.add(item.build(ctx.db)); acc.add(item.build(ctx.db));
}), }),

View file

@ -62,7 +62,7 @@ use std::panic::UnwindSafe;
use cfg::CfgOptions; use cfg::CfgOptions;
use fetch_crates::CrateInfo; use fetch_crates::CrateInfo;
use hir::ChangeWithProcMacros; use hir::{sym, ChangeWithProcMacros};
use ide_db::{ use ide_db::{
base_db::{ base_db::{
salsa::{self, ParallelDatabase}, salsa::{self, ParallelDatabase},
@ -248,7 +248,7 @@ impl Analysis {
// FIXME: cfg options // FIXME: cfg options
// Default to enable test for single file. // Default to enable test for single file.
let mut cfg_options = CfgOptions::default(); let mut cfg_options = CfgOptions::default();
cfg_options.insert_atom("test".into()); cfg_options.insert_atom(sym::test.clone());
crate_graph.add_crate_root( crate_graph.add_crate_root(
file_id, file_id,
Edition::CURRENT, Edition::CURRENT,

View file

@ -3,7 +3,7 @@ use std::fmt;
use ast::HasName; use ast::HasName;
use cfg::{CfgAtom, CfgExpr}; use cfg::{CfgAtom, CfgExpr};
use hir::{ use hir::{
db::HirDatabase, AsAssocItem, AttrsWithOwner, HasAttrs, HasSource, HirFileIdExt, Semantics, db::HirDatabase, sym, AsAssocItem, AttrsWithOwner, HasAttrs, HasSource, HirFileIdExt, Semantics,
}; };
use ide_assists::utils::{has_test_related_attribute, test_related_attribute_syn}; use ide_assists::utils::{has_test_related_attribute, test_related_attribute_syn};
use ide_db::{ use ide_db::{
@ -403,7 +403,7 @@ pub(crate) fn runnable_impl(
} }
fn has_cfg_test(attrs: AttrsWithOwner) -> bool { fn has_cfg_test(attrs: AttrsWithOwner) -> bool {
attrs.cfgs().any(|cfg| matches!(cfg, CfgExpr::Atom(CfgAtom::Flag(s)) if s == "test")) attrs.cfgs().any(|cfg| matches!(&cfg, CfgExpr::Atom(CfgAtom::Flag(s)) if *s == sym::test))
} }
/// Creates a test mod runnable for outline modules at the top of their definition. /// Creates a test mod runnable for outline modules at the top of their definition.

View file

@ -139,11 +139,17 @@ impl TaggedArcPtr {
} }
} }
#[derive(PartialEq, Eq, Hash, Debug)] #[derive(PartialEq, Eq, Hash)]
pub struct Symbol { pub struct Symbol {
repr: TaggedArcPtr, repr: TaggedArcPtr,
} }
impl fmt::Debug for Symbol {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.as_str().fmt(f)
}
}
const _: () = assert!(std::mem::size_of::<Symbol>() == std::mem::size_of::<NonNull<()>>()); const _: () = assert!(std::mem::size_of::<Symbol>() == std::mem::size_of::<NonNull<()>>());
const _: () = assert!(std::mem::align_of::<Symbol>() == std::mem::align_of::<NonNull<()>>()); const _: () = assert!(std::mem::align_of::<Symbol>() == std::mem::align_of::<NonNull<()>>());

View file

@ -93,20 +93,20 @@ define_symbols! {
__ra_fixup, __ra_fixup,
add_assign, add_assign,
add, add,
attributes,
align_offset, align_offset,
align,
all,
alloc_layout, alloc_layout,
alloc, alloc,
any,
as_str, as_str,
asm, asm,
assert, assert,
attributes,
begin_panic, begin_panic,
bench, bench,
bitand_assign, bitand_assign,
bitand, bitand,
notable_trait,
hidden,
local_inner_macros,
bitor_assign, bitor_assign,
bitor, bitor,
bitxor_assign, bitxor_assign,
@ -118,6 +118,7 @@ define_symbols! {
branch, branch,
Break, Break,
c_void, c_void,
C,
call_mut, call_mut,
call_once, call_once,
call, call,
@ -146,8 +147,10 @@ define_symbols! {
core, core,
coroutine_state, coroutine_state,
coroutine, coroutine,
count,
crate_type, crate_type,
CStr, CStr,
debug_assertions,
Debug, Debug,
default, default,
Default, Default,
@ -172,6 +175,7 @@ define_symbols! {
Eq, Eq,
Err, Err,
exchange_malloc, exchange_malloc,
exhaustive_patterns,
f128, f128,
f16, f16,
f32, f32,
@ -208,11 +212,13 @@ define_symbols! {
global_asm, global_asm,
gt, gt,
Hash, Hash,
hidden,
i128, i128,
i16, i16,
i32, i32,
i64, i64,
i8, i8,
ignore,
Implied, Implied,
include_bytes, include_bytes,
include_str, include_str,
@ -237,14 +243,15 @@ define_symbols! {
len, len,
line, line,
llvm_asm, llvm_asm,
local_inner_macros,
log_syntax, log_syntax,
lt, lt,
macro_rules, macro_rules,
ignore,
count,
manually_drop, manually_drop,
maybe_uninit, maybe_uninit,
metadata_type, metadata_type,
min_exhaustive_patterns,
miri,
missing, missing,
module_path, module_path,
mul_assign, mul_assign,
@ -271,6 +278,7 @@ define_symbols! {
None, None,
not, not,
Not, Not,
notable_trait,
Ok, Ok,
opaque, opaque,
ops, ops,
@ -280,6 +288,7 @@ define_symbols! {
Ord, Ord,
Output, Output,
owned_box, owned_box,
packed,
panic_2015, panic_2015,
panic_2021, panic_2021,
panic_bounds_check, panic_bounds_check,
@ -328,6 +337,7 @@ define_symbols! {
rust_2018, rust_2018,
rust_2021, rust_2021,
rust_2024, rust_2024,
rust_analyzer,
rustc_coherence_is_core, rustc_coherence_is_core,
rustc_macro_transparency, rustc_macro_transparency,
semitransparent, semitransparent,
@ -335,6 +345,7 @@ define_symbols! {
shl, shl,
shr_assign, shr_assign,
shr, shr,
simd,
sized, sized,
slice_len_fn, slice_len_fn,
Some, Some,
@ -367,10 +378,6 @@ define_symbols! {
u8, u8,
Unknown, Unknown,
unpin, unpin,
simd,
C,
align,
packed,
unreachable_2015, unreachable_2015,
unreachable_2021, unreachable_2021,
unreachable, unreachable,
@ -378,7 +385,5 @@ define_symbols! {
unsize, unsize,
usize, usize,
v1, v1,
exhaustive_patterns,
min_exhaustive_patterns,
va_list va_list
} }

View file

@ -25,6 +25,7 @@ itertools.workspace = true
# local deps # local deps
base-db.workspace = true base-db.workspace = true
intern.workspace = true
span.workspace = true span.workspace = true
cfg.workspace = true cfg.workspace = true
paths = { workspace = true, features = ["serde1"] } paths = { workspace = true, features = ["serde1"] }

View file

@ -4,6 +4,7 @@
use std::{fmt, str::FromStr}; use std::{fmt, str::FromStr};
use cfg::{CfgDiff, CfgOptions}; use cfg::{CfgDiff, CfgOptions};
use intern::Symbol;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use serde::Serialize; use serde::Serialize;
@ -44,8 +45,10 @@ impl Extend<CfgFlag> for CfgOptions {
fn extend<T: IntoIterator<Item = CfgFlag>>(&mut self, iter: T) { fn extend<T: IntoIterator<Item = CfgFlag>>(&mut self, iter: T) {
for cfg_flag in iter { for cfg_flag in iter {
match cfg_flag { match cfg_flag {
CfgFlag::Atom(it) => self.insert_atom(it.into()), CfgFlag::Atom(it) => self.insert_atom(Symbol::intern(&it)),
CfgFlag::KeyValue { key, value } => self.insert_key_value(key.into(), value.into()), CfgFlag::KeyValue { key, value } => {
self.insert_key_value(Symbol::intern(&key), Symbol::intern(&value))
}
} }
} }
} }

View file

@ -4,6 +4,7 @@ use base_db::{CrateGraph, FileId, ProcMacroPaths};
use cargo_metadata::Metadata; use cargo_metadata::Metadata;
use cfg::{CfgAtom, CfgDiff}; use cfg::{CfgAtom, CfgDiff};
use expect_test::{expect_file, ExpectFile}; use expect_test::{expect_file, ExpectFile};
use intern::sym;
use paths::{AbsPath, AbsPathBuf, Utf8Path, Utf8PathBuf}; use paths::{AbsPath, AbsPathBuf, Utf8Path, Utf8PathBuf};
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
@ -180,7 +181,7 @@ fn check_crate_graph(crate_graph: CrateGraph, expect: ExpectFile) {
#[test] #[test]
fn cargo_hello_world_project_model_with_wildcard_overrides() { fn cargo_hello_world_project_model_with_wildcard_overrides() {
let cfg_overrides = CfgOverrides { let cfg_overrides = CfgOverrides {
global: CfgDiff::new(Vec::new(), vec![CfgAtom::Flag("test".into())]).unwrap(), global: CfgDiff::new(Vec::new(), vec![CfgAtom::Flag(sym::test.clone())]).unwrap(),
selective: Default::default(), selective: Default::default(),
}; };
let (crate_graph, _proc_macros) = let (crate_graph, _proc_macros) =
@ -199,7 +200,7 @@ fn cargo_hello_world_project_model_with_selective_overrides() {
global: Default::default(), global: Default::default(),
selective: std::iter::once(( selective: std::iter::once((
"libc".to_owned(), "libc".to_owned(),
CfgDiff::new(Vec::new(), vec![CfgAtom::Flag("test".into())]).unwrap(), CfgDiff::new(Vec::new(), vec![CfgAtom::Flag(sym::test.clone())]).unwrap(),
)) ))
.collect(), .collect(),
}; };

View file

@ -10,6 +10,7 @@ use base_db::{
LangCrateOrigin, ProcMacroPaths, TargetLayoutLoadResult, LangCrateOrigin, ProcMacroPaths, TargetLayoutLoadResult,
}; };
use cfg::{CfgAtom, CfgDiff, CfgOptions}; use cfg::{CfgAtom, CfgDiff, CfgOptions};
use intern::{sym, Symbol};
use paths::{AbsPath, AbsPathBuf}; use paths::{AbsPath, AbsPathBuf};
use rustc_hash::{FxHashMap, FxHashSet}; use rustc_hash::{FxHashMap, FxHashSet};
use semver::Version; use semver::Version;
@ -977,8 +978,8 @@ fn cargo_to_crate_graph(
if cargo[pkg].is_local { if cargo[pkg].is_local {
// Add test cfg for local crates // Add test cfg for local crates
cfg_options.insert_atom("test".into()); cfg_options.insert_atom(sym::test.clone());
cfg_options.insert_atom("rust_analyzer".into()); cfg_options.insert_atom(sym::rust_analyzer.clone());
} }
override_cfg.apply(&mut cfg_options, &cargo[pkg].name); override_cfg.apply(&mut cfg_options, &cargo[pkg].name);
@ -1144,8 +1145,8 @@ fn detached_file_to_crate_graph(
sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load); sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load);
let mut cfg_options = CfgOptions::from_iter(rustc_cfg); let mut cfg_options = CfgOptions::from_iter(rustc_cfg);
cfg_options.insert_atom("test".into()); cfg_options.insert_atom(sym::test.clone());
cfg_options.insert_atom("rust_analyzer".into()); cfg_options.insert_atom(sym::rust_analyzer.clone());
override_cfg.apply(&mut cfg_options, ""); override_cfg.apply(&mut cfg_options, "");
let cfg_options = Arc::new(cfg_options); let cfg_options = Arc::new(cfg_options);
@ -1307,7 +1308,7 @@ fn add_target_crate_root(
let cfg_options = { let cfg_options = {
let mut opts = cfg_options; let mut opts = cfg_options;
for feature in pkg.active_features.iter() { for feature in pkg.active_features.iter() {
opts.insert_key_value("feature".into(), feature.into()); opts.insert_key_value(sym::feature.clone(), Symbol::intern(feature));
} }
if let Some(cfgs) = build_data.as_ref().map(|it| &it.cfgs) { if let Some(cfgs) = build_data.as_ref().map(|it| &it.cfgs) {
opts.extend(cfgs.iter().cloned()); opts.extend(cfgs.iter().cloned());
@ -1381,8 +1382,8 @@ fn sysroot_to_crate_graph(
&CfgOverrides { &CfgOverrides {
global: CfgDiff::new( global: CfgDiff::new(
vec![ vec![
CfgAtom::Flag("debug_assertions".into()), CfgAtom::Flag(sym::debug_assertions.clone()),
CfgAtom::Flag("miri".into()), CfgAtom::Flag(sym::miri.clone()),
], ],
vec![], vec![],
) )
@ -1394,7 +1395,7 @@ fn sysroot_to_crate_graph(
let mut pub_deps = vec![]; let mut pub_deps = vec![];
let mut libproc_macro = None; let mut libproc_macro = None;
let diff = CfgDiff::new(vec![], vec![CfgAtom::Flag("test".into())]).unwrap(); let diff = CfgDiff::new(vec![], vec![CfgAtom::Flag(sym::test.clone())]).unwrap();
for (cid, c) in cg.iter_mut() { for (cid, c) in cg.iter_mut() {
// uninject `test` flag so `core` keeps working. // uninject `test` flag so `core` keeps working.
Arc::make_mut(&mut c.cfg_options).apply_diff(diff.clone()); Arc::make_mut(&mut c.cfg_options).apply_diff(diff.clone());
@ -1449,8 +1450,8 @@ fn sysroot_to_crate_graph(
let cfg_options = Arc::new({ let cfg_options = Arc::new({
let mut cfg_options = CfgOptions::default(); let mut cfg_options = CfgOptions::default();
cfg_options.extend(rustc_cfg); cfg_options.extend(rustc_cfg);
cfg_options.insert_atom("debug_assertions".into()); cfg_options.insert_atom(sym::debug_assertions.clone());
cfg_options.insert_atom("miri".into()); cfg_options.insert_atom(sym::miri.clone());
cfg_options cfg_options
}); });
let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = stitched let sysroot_crates: FxHashMap<SysrootCrate, CrateId> = stitched

View file

@ -54,6 +54,7 @@ hir-def.workspace = true
hir-ty.workspace = true hir-ty.workspace = true
hir.workspace = true hir.workspace = true
ide-db.workspace = true ide-db.workspace = true
intern.workspace = true
# This should only be used in CLI # This should only be used in CLI
ide-ssr.workspace = true ide-ssr.workspace = true
ide.workspace = true ide.workspace = true

View file

@ -8,6 +8,7 @@ use std::{fmt, iter, ops::Not, sync::OnceLock};
use cfg::{CfgAtom, CfgDiff}; use cfg::{CfgAtom, CfgDiff};
use dirs::config_dir; use dirs::config_dir;
use flycheck::{CargoOptions, FlycheckConfig}; use flycheck::{CargoOptions, FlycheckConfig};
use hir::Symbol;
use ide::{ use ide::{
AssistConfig, CallableSnippets, CompletionConfig, DiagnosticsConfig, ExprFillDefaultMode, AssistConfig, CallableSnippets, CompletionConfig, DiagnosticsConfig, ExprFillDefaultMode,
GenericParameterHints, HighlightConfig, HighlightRelatedConfig, HoverConfig, HoverDocFormat, GenericParameterHints, HighlightConfig, HighlightRelatedConfig, HoverConfig, HoverDocFormat,
@ -1691,8 +1692,11 @@ impl Config {
self.cargo_cfgs() self.cargo_cfgs()
.iter() .iter()
.map(|(key, val)| match val { .map(|(key, val)| match val {
Some(val) => CfgAtom::KeyValue { key: key.into(), value: val.into() }, Some(val) => CfgAtom::KeyValue {
None => CfgAtom::Flag(key.into()), key: Symbol::intern(key),
value: Symbol::intern(val),
},
None => CfgAtom::Flag(Symbol::intern(key)),
}) })
.collect(), .collect(),
vec![], vec![],

View file

@ -3,6 +3,7 @@
use std::mem; use std::mem;
use cfg::{CfgAtom, CfgExpr}; use cfg::{CfgAtom, CfgExpr};
use hir::sym;
use ide::{Cancellable, CrateId, FileId, RunnableKind, TestId}; use ide::{Cancellable, CrateId, FileId, RunnableKind, TestId};
use project_model::project_json::Runnable; use project_model::project_json::Runnable;
use project_model::{CargoFeatures, ManifestPath, TargetKind}; use project_model::{CargoFeatures, ManifestPath, TargetKind};
@ -237,7 +238,7 @@ impl CargoTargetSpec {
/// Fill minimal features needed /// Fill minimal features needed
fn required_features(cfg_expr: &CfgExpr, features: &mut Vec<String>) { fn required_features(cfg_expr: &CfgExpr, features: &mut Vec<String>) {
match cfg_expr { match cfg_expr {
CfgExpr::Atom(CfgAtom::KeyValue { key, value }) if key == "feature" => { CfgExpr::Atom(CfgAtom::KeyValue { key, value }) if *key == sym::feature => {
features.push(value.to_string()) features.push(value.to_string())
} }
CfgExpr::All(preds) => { CfgExpr::All(preds) => {

View file

@ -481,9 +481,9 @@ impl FileMeta {
let mut cfg = CfgOptions::default(); let mut cfg = CfgOptions::default();
for (k, v) in f.cfgs { for (k, v) in f.cfgs {
if let Some(v) = v { if let Some(v) = v {
cfg.insert_key_value(k.into(), v.into()); cfg.insert_key_value(Symbol::intern(&k), Symbol::intern(&v));
} else { } else {
cfg.insert_atom(k.into()); cfg.insert_atom(Symbol::intern(&k));
} }
} }