Repository: rust-analyzer/rust-analyzer
Pull request #6335 (2020-10-23 13:18:53)

Fix panic context
bors r+
🤖


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index 8bafc38952..e819297bd0 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -162,7 +162,6 @@ checksum = "ed67cbde08356238e75fc4656be4749481eeffb09e19f320a25237d5221c985d"
 name = "cfg"
 version = "0.0.0"
 dependencies = [
- "expect-test",
  "mbe",
  "rustc-hash",
  "syntax",
diff --git a/crates/cfg/Cargo.toml b/crates/cfg/Cargo.toml
index c68e391c1b..a6785ee8e1 100644
--- a/crates/cfg/Cargo.toml
+++ b/crates/cfg/Cargo.toml
@@ -17,4 +17,3 @@ tt = { path = "../tt", version = "0.0.0" }
 [dev-dependencies]
 mbe = { path = "../mbe" }
 syntax = { path = "../syntax" }
-expect-test = "1.0"
diff --git a/crates/cfg/src/cfg_expr.rs b/crates/cfg/src/cfg_expr.rs
index 42327f1e14..336fe25bc9 100644
--- a/crates/cfg/src/cfg_expr.rs
+++ b/crates/cfg/src/cfg_expr.rs
@@ -2,77 +2,30 @@
 //!
 //! See: https://doc.rust-lang.org/reference/conditional-compilation.html#conditional-compilation
 
-use std::{fmt, slice::Iter as SliceIter};
+use std::slice::Iter as SliceIter;
 
 use tt::SmolStr;
 
-/// A simple configuration value passed in from the outside.
-#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
-pub enum CfgAtom {
-    /// eg. `#[cfg(test)]`
-    Flag(SmolStr),
-    /// eg. `#[cfg(target_os = "linux")]`
-    ///
-    /// 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")]`.
-    KeyValue { key: SmolStr, value: SmolStr },
-}
-
-impl CfgAtom {
-    /// Returns `true` when the atom comes from the target specification.
-    ///
-    /// If this returns `true`, then changing this atom requires changing the compilation target. If
-    /// it returns `false`, the atom might come from a build script or the build system.
-    pub fn is_target_defined(&self) -> bool {
-        match self {
-            CfgAtom::Flag(flag) => matches!(&**flag, "unix" | "windows"),
-            CfgAtom::KeyValue { key, value: _ } => matches!(
-                &**key,
-                "target_arch"
-                    | "target_os"
-                    | "target_env"
-                    | "target_family"
-                    | "target_endian"
-                    | "target_pointer_width"
-                    | "target_vendor" // NOTE: `target_feature` is left out since it can be configured via `-Ctarget-feature`
-            ),
-        }
-    }
-}
-
-impl fmt::Display for CfgAtom {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            CfgAtom::Flag(name) => write!(f, "{}", name),
-            CfgAtom::KeyValue { key, value } => write!(f, "{} = {:?}", key, value),
-        }
-    }
-}
-
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub enum CfgExpr {
     Invalid,
-    Atom(CfgAtom),
+    Atom(SmolStr),
+    KeyValue { key: SmolStr, value: SmolStr },
     All(Vec<CfgExpr>),
     Any(Vec<CfgExpr>),
     Not(Box<CfgExpr>),
 }
 
-impl From<CfgAtom> for CfgExpr {
-    fn from(atom: CfgAtom) -> Self {
-        CfgExpr::Atom(atom)
-    }
-}
-
 impl CfgExpr {
     pub fn parse(tt: &tt::Subtree) -> CfgExpr {
         next_cfg_expr(&mut tt.token_trees.iter()).unwrap_or(CfgExpr::Invalid)
     }
     /// Fold the cfg by querying all basic `Atom` and `KeyValue` predicates.
-    pub fn fold(&self, query: &dyn Fn(&CfgAtom) -> bool) -> Option<bool> {
+    pub fn fold(&self, query: &dyn Fn(&SmolStr, Option<&SmolStr>) -> bool) -> Option<bool> {
         match self {
             CfgExpr::Invalid => None,
-            CfgExpr::Atom(atom) => Some(query(atom)),
+            CfgExpr::Atom(name) => Some(query(name, None)),
+            CfgExpr::KeyValue { key, value } => Some(query(key, Some(value))),
             CfgExpr::All(preds) => {
                 preds.iter().try_fold(true, |s, pred| Some(s && pred.fold(query)?))
             }
@@ -101,7 +54,7 @@ fn next_cfg_expr(it: &mut SliceIter<tt::TokenTree>) -> Option<CfgExpr> {
                     // FIXME: escape? raw string?
                     let value =
                         SmolStr::new(literal.text.trim_start_matches('"').trim_end_matches('"'));
-                    CfgAtom::KeyValue { key: name, value }.into()
+                    CfgExpr::KeyValue { key: name, value }
                 }
                 _ => return Some(CfgExpr::Invalid),
             }
@@ -117,7 +70,7 @@ fn next_cfg_expr(it: &mut SliceIter<tt::TokenTree>) -> Option<CfgExpr> {
                 _ => CfgExpr::Invalid,
             }
         }
-        _ => CfgAtom::Flag(name).into(),
+        _ => CfgExpr::Atom(name),
     };
 
     // Eat comma separator
@@ -128,3 +81,53 @@ fn next_cfg_expr(it: &mut SliceIter<tt::TokenTree>) -> Option<CfgExpr> {
     }
     Some(ret)
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    use mbe::ast_to_token_tree;
+    use syntax::ast::{self, AstNode};
+
+    fn assert_parse_result(input: &str, expected: CfgExpr) {
+        let (tt, _) = {
+            let source_file = ast::SourceFile::parse(input).ok().unwrap();
+            let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
+            ast_to_token_tree(&tt).unwrap()
+        };
+        let cfg = CfgExpr::parse(&tt);
+        assert_eq!(cfg, expected);
+    }
+
+    #[test]
+    fn test_cfg_expr_parser() {
+        assert_parse_result("#![cfg(foo)]", CfgExpr::Atom("foo".into()));
+        assert_parse_result("#![cfg(foo,)]", CfgExpr::Atom("foo".into()));
+        assert_parse_result(
+            "#![cfg(not(foo))]",
+            CfgExpr::Not(Box::new(CfgExpr::Atom("foo".into()))),
+        );
+        assert_parse_result("#![cfg(foo(bar))]", CfgExpr::Invalid);
+
+        // Only take the first
+        assert_parse_result(r#"#![cfg(foo, bar = "baz")]"#, CfgExpr::Atom("foo".into()));
+
+        assert_parse_result(
+            r#"#![cfg(all(foo, bar = "baz"))]"#,
+            CfgExpr::All(vec![
+                CfgExpr::Atom("foo".into()),
+                CfgExpr::KeyValue { key: "bar".into(), value: "baz".into() },
+            ]),
+        );
+
+        assert_parse_result(
+            r#"#![cfg(any(not(), all(), , bar = "baz",))]"#,
+            CfgExpr::Any(vec![
+                CfgExpr::Not(Box::new(CfgExpr::Invalid)),
+                CfgExpr::All(vec![]),
+                CfgExpr::Invalid,
+                CfgExpr::KeyValue { key: "bar".into(), value: "baz".into() },
+            ]),
+        );
+    }
+}
diff --git a/crates/cfg/src/dnf.rs b/crates/cfg/src/dnf.rs
deleted file mode 100644
index 580c9a9a2c..0000000000
--- a/crates/cfg/src/dnf.rs
+++ /dev/null
@@ -1,320 +0,0 @@
-//! Disjunctive Normal Form construction.
-//!
-//! Algorithm from <https://www.cs.drexel.edu/~jjohnson/2015-16/fall/CS270/Lectures/3/dnf.pdf>,
-//! which would have been much easier to read if it used pattern matching. It's also missing the
-//! entire "distribute ANDs over ORs" part, which is not trivial. Oh well.
-//!
-//! This is currently both messy and inefficient. Feel free to improve, there are unit tests.
-
-use std::fmt;
-
-use rustc_hash::FxHashSet;
-
-use crate::{CfgAtom, CfgDiff, CfgExpr, CfgOptions, InactiveReason};
-
-/// A `#[cfg]` directive in Disjunctive Normal Form (DNF).
-pub struct DnfExpr {
-    conjunctions: Vec<Conjunction>,
-}
-
-struct Conjunction {
-    literals: Vec<Literal>,
-}
-
-struct Literal {
-    negate: bool,
-    var: Option<CfgAtom>, // None = Invalid
-}
-
-impl DnfExpr {
-    pub fn new(expr: CfgExpr) -> Self {
-        let builder = Builder { expr: DnfExpr { conjunctions: Vec::new() } };
-
-        builder.lower(expr.clone())
-    }
-
-    /// Computes a list of present or absent atoms in `opts` that cause this expression to evaluate
-    /// to `false`.
-    ///
-    /// Note that flipping a subset of these atoms might be sufficient to make the whole expression
-    /// evaluate to `true`. For that, see `compute_enable_hints`.
-    ///
-    /// Returns `None` when `self` is already true, or contains errors.
-    pub fn why_inactive(&self, opts: &CfgOptions) -> Option<InactiveReason> {
-        let mut res = InactiveReason { enabled: Vec::new(), disabled: Vec::new() };
-
-        for conj in &self.conjunctions {
-            let mut conj_is_true = true;
-            for lit in &conj.literals {
-                let atom = lit.var.as_ref()?;
-                let enabled = opts.enabled.contains(atom);
-                if lit.negate == enabled {
-                    // Literal is false, but needs to be true for this conjunction.
-                    conj_is_true = false;
-
-                    if enabled {
-                        res.enabled.push(atom.clone());
-                    } else {
-                        res.disabled.push(atom.clone());
-                    }
-                }
-            }
-
-            if conj_is_true {
-                // This expression is not actually inactive.
-                return None;
-            }
-        }
-
-        res.enabled.sort_unstable();
-        res.enabled.dedup();
-        res.disabled.sort_unstable();
-        res.disabled.dedup();
-        Some(res)
-    }
-
-    /// Returns `CfgDiff` objects that would enable this directive if applied to `opts`.
-    pub fn compute_enable_hints<'a>(
-        &'a self,
-        opts: &'a CfgOptions,
-    ) -> impl Iterator<Item = CfgDiff> + 'a {
-        // A cfg is enabled if any of `self.conjunctions` evaluate to `true`.
-
-        self.conjunctions.iter().filter_map(move |conj| {
-            let mut enable = FxHashSet::default();
-            let mut disable = FxHashSet::default();
-            for lit in &conj.literals {
-                let atom = lit.var.as_ref()?;
-                let enabled = opts.enabled.contains(atom);
-                if lit.negate && enabled {
-                    disable.insert(atom.clone());
-                }
-                if !lit.negate && !enabled {
-                    enable.insert(atom.clone());
-                }
-            }
-
-            // Check that this actually makes `conj` true.
-            for lit in &conj.literals {
-                let atom = lit.var.as_ref()?;
-                let enabled = enable.contains(atom)
-                    || (opts.enabled.contains(atom) && !disable.contains(atom));
-                if enabled == lit.negate {
-                    return None;
-                }
-            }
-
-            if enable.is_empty() && disable.is_empty() {
-                return None;
-            }
-
-            let mut diff = CfgDiff {
-                enable: enable.into_iter().collect(),
-                disable: disable.into_iter().collect(),
-            };
-
-            // Undo the FxHashMap randomization for consistent output.
-            diff.enable.sort_unstable();
-            diff.disable.sort_unstable();
-
-            Some(diff)
-        })
-    }
-}
-
-impl fmt::Display for DnfExpr {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        if self.conjunctions.len() != 1 {
-            write!(f, "any(")?;
-        }
-        for (i, conj) in self.conjunctions.iter().enumerate() {
-            if i != 0 {
-                f.write_str(", ")?;
-            }
-
-            write!(f, "{}", conj)?;
-        }
-        if self.conjunctions.len() != 1 {
-            write!(f, ")")?;
-        }
-
-        Ok(())
-    }
-}
-
-impl Conjunction {
-    fn new(parts: Vec<CfgExpr>) -> Self {
-        let mut literals = Vec::new();
-        for part in parts {
-            match part {
-                CfgExpr::Invalid | CfgExpr::Atom(_) | CfgExpr::Not(_) => {
-                    literals.push(Literal::new(part));
-                }
-                CfgExpr::All(conj) => {
-                    // Flatten.
-                    literals.extend(Conjunction::new(conj).literals);
-                }
-                CfgExpr::Any(_) => unreachable!("disjunction in conjunction"),
-            }
-        }
-
-        Self { literals }
-    }
-}
-
-impl fmt::Display for Conjunction {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        if self.literals.len() != 1 {
-            write!(f, "all(")?;
-        }
-        for (i, lit) in self.literals.iter().enumerate() {
-            if i != 0 {
-                f.write_str(", ")?;
-            }
-
-            write!(f, "{}", lit)?;
-        }
-        if self.literals.len() != 1 {
-            write!(f, ")")?;
-        }
-
-        Ok(())
-    }
-}
-
-impl Literal {
-    fn new(expr: CfgExpr) -> Self {
-        match expr {
-            CfgExpr::Invalid => Self { negate: false, var: None },
-            CfgExpr::Atom(atom) => Self { negate: false, var: Some(atom) },
-            CfgExpr::Not(expr) => match *expr {
-                CfgExpr::Invalid => Self { negate: true, var: None },
-                CfgExpr::Atom(atom) => Self { negate: true, var: Some(atom) },
-                _ => unreachable!("non-atom {:?}", expr),
-            },
-            CfgExpr::Any(_) | CfgExpr::All(_) => unreachable!("non-literal {:?}", expr),
-        }
-    }
-}
-
-impl fmt::Display for Literal {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        if self.negate {
-            write!(f, "not(")?;
-        }
-
-        match &self.var {
-            Some(var) => write!(f, "{}", var)?,
-            None => f.write_str("<invalid>")?,
-        }
-
-        if self.negate {
-            write!(f, ")")?;
-        }
-
-        Ok(())
-    }
-}
-
-struct Builder {
-    expr: DnfExpr,
-}
-
-impl Builder {
-    fn lower(mut self, expr: CfgExpr) -> DnfExpr {
-        let expr = make_nnf(expr);
-        let expr = make_dnf(expr);
-
-        match expr {
-            CfgExpr::Invalid | CfgExpr::Atom(_) | CfgExpr::Not(_) => {
-                self.expr.conjunctions.push(Conjunction::new(vec![expr]));
-            }
-            CfgExpr::All(conj) => {
-                self.expr.conjunctions.push(Conjunction::new(conj));
-            }
-            CfgExpr::Any(mut disj) => {
-                disj.reverse();
-                while let Some(conj) = disj.pop() {
-                    match conj {
-                        CfgExpr::Invalid | CfgExpr::Atom(_) | CfgExpr::All(_) | CfgExpr::Not(_) => {
-                            self.expr.conjunctions.push(Conjunction::new(vec![conj]));
-                        }
-                        CfgExpr::Any(inner_disj) => {
-                            // Flatten.
-                            disj.extend(inner_disj.into_iter().rev());
-                        }
-                    }
-                }
-            }
-        }
-
-        self.expr
-    }
-}
-
-fn make_dnf(expr: CfgExpr) -> CfgExpr {
-    match expr {
-        CfgExpr::Invalid | CfgExpr::Atom(_) | CfgExpr::Not(_) => expr,
-        CfgExpr::Any(e) => CfgExpr::Any(e.into_iter().map(|expr| make_dnf(expr)).collect()),
-        CfgExpr::All(e) => {
-            let e = e.into_iter().map(|expr| make_nnf(expr)).collect::<Vec<_>>();
-
-            CfgExpr::Any(distribute_conj(&e))
-        }
-    }
-}
-
-/// Turns a conjunction of expressions into a disjunction of expressions.
-fn distribute_conj(conj: &[CfgExpr]) -> Vec<CfgExpr> {
-    fn go(out: &mut Vec<CfgExpr>, with: &mut Vec<CfgExpr>, rest: &[CfgExpr]) {
-        match rest {
-            [head, tail @ ..] => match head {
-                CfgExpr::Any(disj) => {
-                    for part in disj {
-                        with.push(part.clone());
-                        go(out, with, tail);
-                        with.pop();
-                    }
-                }
-                _ => {
-                    with.push(head.clone());
-                    go(out, with, tail);
-                    with.pop();
-                }
-            },
-            _ => {
-                // Turn accumulated parts into a new conjunction.
-                out.push(CfgExpr::All(with.clone()));
-            }
-        }
-    }
-
-    let mut out = Vec::new();
-    let mut with = Vec::new();
-
-    go(&mut out, &mut with, conj);
-
-    out
-}
-
-fn make_nnf(expr: CfgExpr) -> CfgExpr {
-    match expr {
-        CfgExpr::Invalid | CfgExpr::Atom(_) => expr,
-        CfgExpr::Any(expr) => CfgExpr::Any(expr.into_iter().map(|expr| make_nnf(expr)).collect()),
-        CfgExpr::All(expr) => CfgExpr::All(expr.into_iter().map(|expr| make_nnf(expr)).collect()),
-        CfgExpr::Not(operand) => match *operand {
-            CfgExpr::Invalid | CfgExpr::Atom(_) => CfgExpr::Not(operand.clone()), // Original negated expr
-            CfgExpr::Not(expr) => {
-                // Remove double negation.
-                make_nnf(*expr)
-            }
-            // Convert negated conjunction/disjunction using DeMorgan's Law.
-            CfgExpr::Any(inner) => CfgExpr::All(
-                inner.into_iter().map(|expr| make_nnf(CfgExpr::Not(Box::new(expr)))).collect(),
-            ),
-            CfgExpr::All(inner) => CfgExpr::Any(
-                inner.into_iter().map(|expr| make_nnf(CfgExpr::Not(Box::new(expr)))).collect(),
-            ),
-        },
-    }
-}
diff --git a/crates/cfg/src/lib.rs b/crates/cfg/src/lib.rs
index d0e08cf5f7..a9d50e698a 100644
--- a/crates/cfg/src/lib.rs
+++ b/crates/cfg/src/lib.rs
@@ -1,17 +1,11 @@
 //! cfg defines conditional compiling options, `cfg` attibute parser and evaluator
 
 mod cfg_expr;
-mod dnf;
-#[cfg(test)]
-mod tests;
-
-use std::fmt;
 
 use rustc_hash::FxHashSet;
 use tt::SmolStr;
 
-pub use cfg_expr::{CfgAtom, CfgExpr};
-pub use dnf::DnfExpr;
+pub use cfg_expr::CfgExpr;
 
 /// Configuration options used for conditional compilition on items with `cfg` attributes.
 /// We have two kind of options in different namespaces: atomic options like `unix`, and
@@ -25,131 +19,33 @@ pub use dnf::DnfExpr;
 /// See: https://doc.rust-lang.org/reference/conditional-compilation.html#set-configuration-options
 #[derive(Debug, Clone, PartialEq, Eq, Default)]
 pub struct CfgOptions {
-    enabled: FxHashSet<CfgAtom>,
+    atoms: FxHashSet<SmolStr>,
+    key_values: FxHashSet<(SmolStr, SmolStr)>,
 }
 
 impl CfgOptions {
     pub fn check(&self, cfg: &CfgExpr) -> Option<bool> {
-        cfg.fold(&|atom| self.enabled.contains(atom))
+        cfg.fold(&|key, value| match value {
+            None => self.atoms.contains(key),
+            Some(value) => self.key_values.contains(&(key.clone(), value.clone())),
+        })
     }
 
     pub fn insert_atom(&mut self, key: SmolStr) {
-        self.enabled.insert(CfgAtom::Flag(key));
+        self.atoms.insert(key);
     }
 
     pub fn insert_key_value(&mut self, key: SmolStr, value: SmolStr) {
-        self.enabled.insert(CfgAtom::KeyValue { key, value });
+        self.key_values.insert((key, value));
     }
 
     pub fn append(&mut self, other: &CfgOptions) {
-        for atom in &other.enabled {
-            self.enabled.insert(atom.clone());
-        }
-    }
-
-    pub fn apply_diff(&mut self, diff: CfgDiff) {
-        for atom in diff.enable {
-            self.enabled.insert(atom);
+        for atom in &other.atoms {
+            self.atoms.insert(atom.clone());
         }
 
-        for atom in diff.disable {
-            self.enabled.remove(&atom);
-        }
-    }
-}
-
-pub struct CfgDiff {
-    // Invariants: No duplicates, no atom that's both in `enable` and `disable`.
-    enable: Vec<CfgAtom>,
-    disable: Vec<CfgAtom>,
-}
-
-impl CfgDiff {
-    /// Returns the total number of atoms changed by this diff.
-    pub fn len(&self) -> usize {
-        self.enable.len() + self.disable.len()
-    }
-}
-
-impl fmt::Display for CfgDiff {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        if !self.enable.is_empty() {
-            f.write_str("enable ")?;
-            for (i, atom) in self.enable.iter().enumerate() {
-                let sep = match i {
-                    0 => "",
-                    _ if i == self.enable.len() - 1 => " and ",
-                    _ => ", ",
-                };
-                f.write_str(sep)?;
-
-                write!(f, "{}", atom)?;
-            }
-
-            if !self.disable.is_empty() {
-                f.write_str("; ")?;
-            }
+        for (key, value) in &other.key_values {
+            self.key_values.insert((key.clone(), value.clone()));
         }
-
-        if !self.disable.is_empty() {
-            f.write_str("disable ")?;
-            for (i, atom) in self.disable.iter().enumerate() {
-                let sep = match i {
-                    0 => "",
-                    _ if i == self.enable.len() - 1 => " and ",
-                    _ => ", ",
-                };
-                f.write_str(sep)?;
-
-                write!(f, "{}", atom)?;
-            }
-        }
-
-        Ok(())
-    }
-}
-
-pub struct InactiveReason {
-    enabled: Vec<CfgAtom>,
-    disabled: Vec<CfgAtom>,
-}
-
-impl fmt::Display for InactiveReason {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        if !self.enabled.is_empty() {
-            for (i, atom) in self.enabled.iter().enumerate() {
-                let sep = match i {
-                    0 => "",
-                    _ if i == self.enabled.len() - 1 => " and ",
-                    _ => ", ",
-                };
-                f.write_str(sep)?;
-
-                write!(f, "{}", atom)?;
-            }
-            let is_are = if self.enabled.len() == 1 { "is" } else { "are" };
-            write!(f, " {} enabled", is_are)?;
-
-            if !self.disabled.is_empty() {
-                f.write_str(" and ")?;
-            }
-        }
-
-        if !self.disabled.is_empty() {
-            for (i, atom) in self.disabled.iter().enumerate() {
-                let sep = match i {
-                    0 => "",
-                    _ if i == self.disabled.len() - 1 => " and ",
-                    _ => ", ",
-                };
-                f.write_str(sep)?;
-
-                write!(f, "{}", atom)?;
-            }
-            let is_are = if self.disabled.len() == 1 { "is" } else { "are" };
-            write!(f, " {} disabled", is_are)?;
-        }
-
-        Ok(())
     }
 }
diff --git a/crates/completion/src/completion_context.rs b/crates/completion/src/completion_context.rs
index e4f86d0e01..dc4e136c68 100644
--- a/crates/completion/src/completion_context.rs
+++ b/crates/completion/src/completion_context.rs
@@ -246,19 +246,6 @@ impl<'a> CompletionContext<'a> {
         }
     }
 
-    pub(crate) fn active_name_and_type(&self) -> Option<(String, Type)> {
-        if let Some(record_field) = &self.record_field_syntax {
-            mark::hit!(record_field_type_match);
-            let (struct_field, _local) = self.sema.resolve_record_field(record_field)?;
-            Some((struct_field.name(self.db).to_string(), struct_field.signature_ty(self.db)))
-        } else if let Some(active_parameter) = &self.active_parameter {
-            mark::hit!(active_param_type_match);
-            Some((active_parameter.name.clone(), active_parameter.ty.clone()))
-        } else {
-            None
-        }
-    }
-
     fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) {
         let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap();
         let syntax_element = NodeOrToken::Token(fake_ident_token);
diff --git a/crates/completion/src/completion_item.rs b/crates/completion/src/completion_item.rs
index 2e1ca0e595..f8be0ad2b4 100644
--- a/crates/completion/src/completion_item.rs
+++ b/crates/completion/src/completion_item.rs
@@ -2,7 +2,7 @@
 
 use std::fmt;
 
-use hir::{Documentation, Mutability};
+use hir::Documentation;
 use syntax::TextRange;
 use text_edit::TextEdit;
 
@@ -56,10 +56,6 @@ pub struct CompletionItem {
 
     /// Score is useful to pre select or display in better order completion items
     score: Option<CompletionScore>,
-
-    /// Indicates that a reference or mutable reference to this variable is a
-    /// possible match.
-    ref_match: Option<(Mutability, CompletionScore)>,
 }
 
 // We use custom debug for CompletionItem to make snapshot tests more readable.
@@ -198,7 +194,6 @@ impl CompletionItem {
             deprecated: None,
             trigger_call_info: None,
             score: None,
-            ref_match: None,
         }
     }
     /// What user sees in pop-up in the UI.
@@ -245,15 +240,10 @@ impl CompletionItem {
     pub fn trigger_call_info(&self) -> bool {
         self.trigger_call_info
     }
-
-    pub fn ref_match(&self) -> Option<(Mutability, CompletionScore)> {
-        self.ref_match
-    }
 }
 
 /// A helper to make `CompletionItem`s.
 #[must_use]
-#[derive(Clone)]
 pub(crate) struct Builder {
     source_range: TextRange,
     completion_kind: CompletionKind,
@@ -268,7 +258,6 @@ pub(crate) struct Builder {
     deprecated: Option<bool>,
     trigger_call_info: Option<bool>,
     score: Option<CompletionScore>,
-    ref_match: Option<(Mutability, CompletionScore)>,
 }
 
 impl Builder {
@@ -299,7 +288,6 @@ impl Builder {
             deprecated: self.deprecated.unwrap_or(false),
             trigger_call_info: self.trigger_call_info.unwrap_or(false),
             score: self.score,
-            ref_match: self.ref_match,
         }
     }
     pub(crate) fn lookup_by(mut self, lookup: impl Into<String>) -> Builder {
@@ -362,13 +350,6 @@ impl Builder {
         self.trigger_call_info = Some(true);
         self
     }
-    pub(crate) fn set_ref_match(
-        mut self,
-        ref_match: Option<(Mutability, CompletionScore)>,
-    ) -> Builder {
-        self.ref_match = ref_match;
-        self
-    }
 }
 
 impl<'a> Into<CompletionItem> for Builder {
diff --git a/crates/completion/src/presentation.rs b/crates/completion/src/presentation.rs
index 2a19281cfe..0a0dc1ce50 100644
--- a/crates/completion/src/presentation.rs
+++ b/crates/completion/src/presentation.rs
@@ -1,7 +1,7 @@
 //! This modules takes care of rendering various definitions as completion items.
 //! It also handles scoring (sorting) completions.
 
-use hir::{HasAttrs, HasSource, HirDisplay, ModPath, Mutability, ScopeDef, StructKind, Type};
+use hir::{HasAttrs, HasSource, HirDisplay, ModPath, ScopeDef, StructKind, Type};
 use itertools::Itertools;
 use syntax::{ast::NameOwner, display::*};
 use test_utils::mark;
@@ -107,16 +107,9 @@ impl Completions {
             }
         };
 
-        let mut ref_match = None;
         if let ScopeDef::Local(local) = resolution {
-            if let Some((active_name, active_type)) = ctx.active_name_and_type() {
-                let ty = local.ty(ctx.db);
-                if let Some(score) =
-                    compute_score_from_active(&active_type, &active_name, &ty, &local_name)
-                {
-                    completion_item = completion_item.set_score(score);
-                }
-                ref_match = refed_type_matches(&active_type, &active_name, &ty, &local_name);
+            if let Some(score) = compute_score(ctx, &local.ty(ctx.db), &local_name) {
+                completion_item = completion_item.set_score(score);
             }
         }
 
@@ -138,7 +131,7 @@ impl Completions {
             }
         }
 
-        completion_item.kind(kind).set_documentation(docs).set_ref_match(ref_match).add_to(self)
+        completion_item.kind(kind).set_documentation(docs).add_to(self)
     }
 
     pub(crate) fn add_macro(
@@ -349,15 +342,25 @@ impl Completions {
     }
 }
 
-fn compute_score_from_active(
-    active_type: &Type,
-    active_name: &str,
+pub(crate) fn compute_score(
+    ctx: &CompletionContext,
     ty: &Type,
     name: &str,
 ) -> Option<CompletionScore> {
+    let (active_name, active_type) = if let Some(record_field) = &ctx.record_field_syntax {
+        mark::hit!(record_field_type_match);
+        let (struct_field, _local) = ctx.sema.resolve_record_field(record_field)?;
+        (struct_field.name(ctx.db).to_string(), struct_field.signature_ty(ctx.db))
+    } else if let Some(active_parameter) = &ctx.active_parameter {
+        mark::hit!(active_param_type_match);
+        (active_parameter.name.clone(), active_parameter.ty.clone())
+    } else {
+        return None;
+    };
+
     // Compute score
     // For the same type
-    if active_type != ty {
+    if &active_type != ty {
         return None;
     }
 
@@ -370,24 +373,6 @@ fn compute_score_from_active(
 
     Some(res)
 }
-fn refed_type_matches(
-    active_type: &Type,
-    active_name: &str,
-    ty: &Type,
-    name: &str,
-) -> Option<(Mutability, CompletionScore)> {
-    let derefed_active = active_type.remove_ref()?;
-    let score = compute_score_from_active(&derefed_active, &active_name, &ty, &name)?;
-    Some((
-        if active_type.is_mutable_reference() { Mutability::Mut } else { Mutability::Shared },
-        score,
-    ))
-}
-
-fn compute_score(ctx: &CompletionContext, ty: &Type, name: &str) -> Option<CompletionScore> {
-    let (active_name, active_type) = ctx.active_name_and_type()?;
-    compute_score_from_active(&active_type, &active_name, ty, name)
-}
 
 enum Params {
     Named(Vec<String>),
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs
index b2ce7ca3c3..dea552a605 100644
--- a/crates/hir_def/src/attr.rs
+++ b/crates/hir_def/src/attr.rs
@@ -125,20 +125,12 @@ impl Attrs {
         AttrQuery { attrs: self, key }
     }
 
-    pub fn cfg(&self) -> Option<CfgExpr> {
+    pub fn cfg(&self) -> impl Iterator<Item = CfgExpr> + '_ {
         // FIXME: handle cfg_attr :-)
-        let mut cfgs = self.by_key("cfg").tt_values().map(CfgExpr::parse).collect::<Vec<_>>();
-        match cfgs.len() {
-            0 => None,
-            1 => Some(cfgs.pop().unwrap()),
-            _ => Some(CfgExpr::All(cfgs)),
-        }
+        self.by_key("cfg").tt_values().map(CfgExpr::parse)
     }
     pub(crate) fn is_cfg_enabled(&self, cfg_options: &CfgOptions) -> bool {
-        match self.cfg() {
-            None => true,
-            Some(cfg) => cfg_options.check(&cfg) != Some(false),
-        }
+        self.cfg().all(|cfg| cfg_options.check(&cfg) != Some(false))
     }
 }
 
diff --git a/crates/hir_def/src/diagnostics.rs b/crates/hir_def/src/diagnostics.rs
index 532496b622..c9c08b01f8 100644
--- a/crates/hir_def/src/diagnostics.rs
+++ b/crates/hir_def/src/diagnostics.rs
@@ -1,13 +1,12 @@
 //! Diagnostics produced by `hir_def`.
 
 use std::any::Any;
-use stdx::format_to;
 
-use cfg::{CfgExpr, CfgOptions, DnfExpr};
 use hir_expand::diagnostics::{Diagnostic, DiagnosticCode};
-use hir_expand::{HirFileId, InFile};
 use syntax::{ast, AstPtr, SyntaxNodePtr};
 
+use hir_expand::{HirFileId, InFile};
+
 // Diagnostic: unresolved-module
 //
 // This diagnostic is triggered if rust-analyzer is unable to discover referred module.
@@ -95,8 +94,6 @@ impl Diagnostic for UnresolvedImport {
 pub struct InactiveCode {
     pub file: HirFileId,
     pub node: SyntaxNodePtr,
-    pub cfg: CfgExpr,
-    pub opts: CfgOptions,
 }
 
 impl Diagnostic for InactiveCode {
@@ -104,14 +101,8 @@ impl Diagnostic for InactiveCode {
         DiagnosticCode("inactive-code")
     }
     fn message(&self) -> String {
-        let inactive = DnfExpr::new(self.cfg.clone()).why_inactive(&self.opts);
-        let mut buf = "code is inactive due to #[cfg] directives".to_string();
-
-        if let Some(inactive) = inactive {
-            format_to!(buf, ": {}", inactive);
-        }
-
-        buf
+        // FIXME: say *why* it is configured out
+        "code is inactive due to #[cfg] directives".to_string()
     }
     fn display_source(&self) -> InFile<SyntaxNodePtr> {
         InFile::new(self.file, self.node.clone())
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs
index eb41d324e3..01a28aeeb4 100644
--- a/crates/hir_def/src/nameres.rs
+++ b/crates/hir_def/src/nameres.rs
@@ -283,7 +283,6 @@ pub enum ModuleSource {
 }
 
 mod diagnostics {
-    use cfg::{CfgExpr, CfgOptions};
     use hir_expand::diagnostics::DiagnosticSink;
     use hir_expand::hygiene::Hygiene;
     use hir_expand::InFile;
@@ -300,7 +299,7 @@ mod diagnostics {
 
         UnresolvedImport { ast: AstId<ast::Use>, index: usize },
 
-        UnconfiguredCode { ast: InFile<SyntaxNodePtr>, cfg: CfgExpr, opts: CfgOptions },
+        UnconfiguredCode { ast: InFile<SyntaxNodePtr> },
     }
 
     #[derive(Debug, PartialEq, Eq)]
@@ -342,10 +341,8 @@ mod diagnostics {
         pub(super) fn unconfigured_code(
             container: LocalModuleId,
             ast: InFile<SyntaxNodePtr>,
-            cfg: CfgExpr,
-            opts: CfgOptions,
         ) -> Self {
-            Self { in_module: container, kind: DiagnosticKind::UnconfiguredCode { ast, cfg, opts } }
+            Self { in_module: container, kind: DiagnosticKind::UnconfiguredCode { ast } }
         }
 
         pub(super) fn add_to(
@@ -398,13 +395,8 @@ mod diagnostics {
                     }
                 }
 
-                DiagnosticKind::UnconfiguredCode { ast, cfg, opts } => {
-                    sink.push(InactiveCode {
-                        file: ast.file_id,
-                        node: ast.value.clone(),
-                        cfg: cfg.clone(),
-                        opts: opts.clone(),
-                    });
+                DiagnosticKind::UnconfiguredCode { ast } => {
+                    sink.push(InactiveCode { file: ast.file_id, node: ast.value.clone() });
                 }
             }
         }
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs
index f30172d905..bff8edb62d 100644
--- a/crates/hir_def/src/nameres/collector.rs
+++ b/crates/hir_def/src/nameres/collector.rs
@@ -6,7 +6,7 @@
 use std::iter;
 
 use base_db::{CrateId, FileId, ProcMacroId};
-use cfg::{CfgExpr, CfgOptions};
+use cfg::CfgOptions;
 use hir_expand::InFile;
 use hir_expand::{
     ast_id_map::FileAstId,
@@ -900,8 +900,7 @@ impl ModCollector<'_, '_> {
         // `#[macro_use] extern crate` is hoisted to imports macros before collecting
         // any other items.
         for item in items {
-            let attrs = self.item_tree.attrs((*item).into());
-            if attrs.cfg().map_or(true, |cfg| self.is_cfg_enabled(&cfg)) {
+            if self.is_cfg_enabled(self.item_tree.attrs((*item).into())) {
                 if let ModItem::ExternCrate(id) = item {
                     let import = self.item_tree[*id].clone();
                     if import.is_macro_use {
@@ -913,11 +912,9 @@ impl ModCollector<'_, '_> {
 
         for &item in items {
             let attrs = self.item_tree.attrs(item.into());
-            if let Some(cfg) = attrs.cfg() {
-                if !self.is_cfg_enabled(&cfg) {
-                    self.emit_unconfigured_diagnostic(item, &cfg);
-                    continue;
-                }
+            if !self.is_cfg_enabled(attrs) {
+                self.emit_unconfigured_diagnostic(item);
+                continue;
             }
             let module =
                 ModuleId { krate: self.def_collector.def_map.krate, local_id: self.module_id };
@@ -1324,22 +1321,20 @@ impl ModCollector<'_, '_> {
         }
     }
 
-    fn is_cfg_enabled(&self, cfg: &CfgExpr) -> bool {
-        self.def_collector.cfg_options.check(cfg) != Some(false)
+    fn is_cfg_enabled(&self, attrs: &Attrs) -> bool {
+        attrs.is_cfg_enabled(self.def_collector.cfg_options)
     }
 
-    fn emit_unconfigured_diagnostic(&mut self, item: ModItem, cfg: &CfgExpr) {
+    fn emit_unconfigured_diagnostic(&mut self, item: ModItem) {
         let ast_id = item.ast_id(self.item_tree);
         let id_map = self.def_collector.db.ast_id_map(self.file_id);
         let syntax_ptr = id_map.get(ast_id).syntax_node_ptr();
 
         let ast_node = InFile::new(self.file_id, syntax_ptr);
-        self.def_collector.def_map.diagnostics.push(DefDiagnostic::unconfigured_code(
-            self.module_id,
-            ast_node,
-            cfg.clone(),
-            self.def_collector.cfg_options.clone(),
-        ));
+        self.def_collector
+            .def_map
+            .diagnostics
+            .push(DefDiagnostic::unconfigured_code(self.module_id, ast_node));
     }
 }
 
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 0332c7be04..6466422c5e 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -2128,7 +2128,7 @@ fn foo_<|>test() {}
                                     ignore: false,
                                 },
                             },
-                            cfg: None,
+                            cfg_exprs: [],
                         },
                     ),
                 ]
@@ -2166,7 +2166,7 @@ mod tests<|> {
                             kind: TestMod {
                                 path: "tests",
                             },
-                            cfg: None,
+                            cfg_exprs: [],
                         },
                     ),
                 ]
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index 49d8e4ae14..cccea129af 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -378,11 +378,7 @@ fn is_enum_name_similar_to_param_name(
 fn get_string_representation(expr: &ast::Expr) -> Option<String> {
     match expr {
         ast::Expr::MethodCallExpr(method_call_expr) => {
-            let name_ref = method_call_expr.name_ref()?;
-            match name_ref.text().as_str() {
-                "clone" => method_call_expr.receiver().map(|rec| rec.to_string()),
-                name_ref => Some(name_ref.to_owned()),
-            }
+            Some(method_call_expr.name_ref()?.to_string())
         }
         ast::Expr::RefExpr(ref_expr) => get_string_representation(&ref_expr.expr()?),
         _ => Some(expr.to_string()),
@@ -1209,29 +1205,6 @@ fn main() {
     let iter_of_iters = some_iter.take(2);
       //^^^^^^^^^^^^^ impl Iterator<Item = impl Iterator<Item = i32>>
 }
-"#,
-        );
-    }
-
-    #[test]
-    fn hide_param_hints_for_clones() {
-        check_with_config(
-            InlayHintsConfig {
-                parameter_hints: true,
-                type_hints: false,
-                chaining_hints: false,
-                max_length: None,
-            },
-            r#"
-fn foo(bar: i32, baz: String, qux: f32) {}
-
-fn main() {
-    let bar = 3;
-    let baz = &"baz";
-    let fez = 1.0;
-    foo(bar.clone(), baz.clone(), fez.clone());
-                                //^^^^^^^^^^^ qux
-}
 "#,
         );
     }
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index eb82456ad8..752ef2f219 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -15,7 +15,7 @@ use crate::{display::ToNav, FileId, NavigationTarget};
 pub struct Runnable {
     pub nav: NavigationTarget,
     pub kind: RunnableKind,
-    pub cfg: Option<CfgExpr>,
+    pub cfg_exprs: Vec<CfgExpr>,
 }
 
 #[derive(Debug, Clone)]
@@ -168,7 +168,7 @@ fn runnable_fn(
     };
 
     let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &fn_def));
-    let cfg = attrs.cfg();
+    let cfg_exprs = attrs.cfg().collect();
 
     let nav = if let RunnableKind::DocTest { .. } = kind {
         NavigationTarget::from_doc_commented(
@@ -179,7 +179,7 @@ fn runnable_fn(
     } else {
         NavigationTarget::from_named(sema.db, InFile::new(file_id.into(), &fn_def))
     };
-    Some(Runnable { nav, kind, cfg })
+    Some(Runnable { nav, kind, cfg_exprs })
 }
 
 #[derive(Debug, Copy, Clone)]
@@ -255,9 +255,9 @@ fn runnable_mod(
         .join("::");
 
     let attrs = Attrs::from_attrs_owner(sema.db, InFile::new(HirFileId::from(file_id), &module));
-    let cfg = attrs.cfg();
+    let cfg_exprs = attrs.cfg().collect();
     let nav = module_def.to_nav(sema.db);
-    Some(Runnable { nav, kind: RunnableKind::TestMod { path }, cfg })
+    Some(Runnable { nav, kind: RunnableKind::TestMod { path }, cfg_exprs })
 }
 
 // We could create runnables for modules with number_of_test_submodules > 0,
@@ -348,7 +348,7 @@ fn bench() {}
                             docs: None,
                         },
                         kind: Bin,
-                        cfg: None,
+                        cfg_exprs: [],
                     },
                     Runnable {
                         nav: NavigationTarget {
@@ -373,7 +373,7 @@ fn bench() {}
                                 ignore: false,
                             },
                         },
-                        cfg: None,
+                        cfg_exprs: [],
                     },
                     Runnable {
                         nav: NavigationTarget {
@@ -398,7 +398,7 @@ fn bench() {}
                                 ignore: true,
                             },
                         },
-                        cfg: None,
+                        cfg_exprs: [],
                     },
                     Runnable {
                         nav: NavigationTarget {
@@ -420,7 +420,7 @@ fn bench() {}
                                 "bench",
                             ),
                         },
-                        cfg: None,
+                        cfg_exprs: [],
                     },
                 ]
             "#]],
@@ -507,7 +507,7 @@ fn should_have_no_runnable_6() {}
                             docs: None,
                         },
                         kind: Bin,
-                        cfg: None,
+                        cfg_exprs: [],
                     },
                     Runnable {
                         nav: NavigationTarget {
@@ -527,7 +527,7 @@ fn should_have_no_runnable_6() {}
                                 "should_have_runnable",
                             ),
                         },
-                        cfg: None,
+                        cfg_exprs: [],
                     },
                     Runnable {
                         nav: NavigationTarget {
@@ -547,7 +547,7 @@ fn should_have_no_runnable_6() {}
                                 "should_have_runnable_1",
                             ),
                         },
-                        cfg: None,
+                        cfg_exprs: [],
                     },
                     Runnable {
                         nav: NavigationTarget {
@@ -567,7 +567,7 @@ fn should_have_no_runnable_6() {}
                                 "should_have_runnable_2",
                             ),
                         },
-                        cfg: None,
+                        cfg_exprs: [],
                     },
                 ]
             "#]],
@@ -609,7 +609,7 @@ impl Data {
                             docs: None,
                         },
                         kind: Bin,
-                        cfg: None,
+                        cfg_exprs: [],
                     },
                     Runnable {
                         nav: NavigationTarget {
@@ -629,7 +629,7 @@ impl Data {
                                 "Data::foo",
                             ),
                         },
-                        cfg: None,
+                        cfg_exprs: [],
                     },
                 ]
             "#]],
@@ -668,7 +668,7 @@ mod test_mod {
                         kind: TestMod {
                             path: "test_mod",
                         },
-                        cfg: None,
+                        cfg_exprs: [],
                     },
                     Runnable {
                         nav: NavigationTarget {
@@ -693,7 +693,7 @@ mod test_mod {
                                 ignore: false,
                             },
                         },
-                        cfg: None,
+                        cfg_exprs: [],
                     },
                 ]
             "#]],
@@ -748,7 +748,7 @@ mod root_tests {
                         kind: TestMod {
                             path: "root_tests::nested_tests_0",
                         },
-                        cfg: None,
+                        cfg_exprs: [],
                     },
                     Runnable {
                         nav: NavigationTarget {
@@ -768,7 +768,7 @@ mod root_tests {
                         kind: TestMod {
                             path: "root_tests::nested_tests_0::nested_tests_1",
                         },
-                        cfg: None,
+                        cfg_exprs: [],
                     },
                     Runnable {
                         nav: NavigationTarget {
@@ -793,7 +793,7 @@ mod root_tests {
                                 ignore: false,
                             },
                         },
-                        cfg: None,
+                        cfg_exprs: [],
                     },
                     Runnable {
                         nav: NavigationTarget {
@@ -818,7 +818,7 @@ mod root_tests {
                                 ignore: false,
                             },
                         },
-                        cfg: None,
+                        cfg_exprs: [],
                     },
                     Runnable {
                         nav: NavigationTarget {
@@ -838,7 +838,7 @@ mod root_tests {
                         kind: TestMod {
                             path: "root_tests::nested_tests_0::nested_tests_2",
                         },
-                        cfg: None,
+                        cfg_exprs: [],
                     },
                     Runnable {
                         nav: NavigationTarget {
@@ -863,7 +863,7 @@ mod root_tests {
                                 ignore: false,
                             },
                         },
-                        cfg: None,
+                        cfg_exprs: [],
                     },
                 ]
             "#]],
@@ -906,14 +906,12 @@ fn test_foo1() {}
                                 ignore: false,
                             },
                         },
-                        cfg: Some(
-                            Atom(
-                                KeyValue {
-                                    key: "feature",
-                                    value: "foo",
-                                },
-                            ),
-                        ),
+                        cfg_exprs: [
+                            KeyValue {
+                                key: "feature",
+                                value: "foo",
+                            },
+                        ],
                     },
                 ]
             "#]],
@@ -956,24 +954,20 @@ fn test_foo1() {}
                                 ignore: false,
                             },
                         },
-                        cfg: Some(
+                        cfg_exprs: [
                             All(
                                 [
-                                    Atom(
-                                        KeyValue {
-                                            key: "feature",
-                                            value: "foo",
-                                        },
-                                    ),
-                                    Atom(
-                                        KeyValue {
-                                            key: "feature",
-                                            value: "bar",
-                                        },
-                                    ),
+                                    KeyValue {
+                                        key: "feature",
+                                        value: "foo",
+                                    },
+                                    KeyValue {
+                                        key: "feature",
+                                        value: "bar",
+                                    },
                                 ],
                             ),
-                        ),
+                        ],
                     },
                 ]
             "#]],
diff --git a/crates/rust-analyzer/src/cargo_target_spec.rs b/crates/rust-analyzer/src/cargo_target_spec.rs
index 1ab72bd915..ddc028148c 100644
--- a/crates/rust-analyzer/src/cargo_target_spec.rs
+++ b/crates/rust-analyzer/src/cargo_target_spec.rs
@@ -1,6 +1,6 @@
 //! See `CargoTargetSpec`
 
-use cfg::{CfgAtom, CfgExpr};
+use cfg::CfgExpr;
 use ide::{FileId, RunnableKind, TestId};
 use project_model::{self, TargetKind};
 use vfs::AbsPathBuf;
@@ -24,7 +24,7 @@ impl CargoTargetSpec {
         snap: &GlobalStateSnapshot,
         spec: Option<CargoTargetSpec>,
         kind: &RunnableKind,
-        cfg: &Option<CfgExpr>,
+        cfgs: &[CfgExpr],
     ) -> Result<(Vec<String>, Vec<String>)> {
         let mut args = Vec::new();
         let mut extra_args = Vec::new();
@@ -87,7 +87,7 @@ impl CargoTargetSpec {
             args.push("--all-features".to_string());
         } else {
             let mut features = Vec::new();
-            if let Some(cfg) = cfg.as_ref() {
+            for cfg in cfgs {
                 required_features(cfg, &mut features);
             }
             for feature in &snap.config.cargo.features {
@@ -160,9 +160,7 @@ impl CargoTargetSpec {
 /// Fill minimal features needed
 fn required_features(cfg_expr: &CfgExpr, features: &mut Vec<String>) {
     match cfg_expr {
-        CfgExpr::Atom(CfgAtom::KeyValue { key, value }) if key == "feature" => {
-            features.push(value.to_string())
-        }
+        CfgExpr::KeyValue { key, value } if key == "feature" => features.push(value.to_string()),
         CfgExpr::All(preds) => {
             preds.iter().for_each(|cfg| required_features(cfg, features));
         }
diff --git a/crates/rust-analyzer/src/dispatch.rs b/crates/rust-analyzer/src/dispatch.rs
index 9c8815e29e..7a87515e9a 100644
--- a/crates/rust-analyzer/src/dispatch.rs
+++ b/crates/rust-analyzer/src/dispatch.rs
@@ -34,7 +34,7 @@ impl<'a> RequestDispatcher<'a> {
         };
         let world = panic::AssertUnwindSafe(&mut *self.global_state);
         let response = panic::catch_unwind(move || {
-            stdx::panic_context::enter(format!("request: {} {:#?}", R::METHOD, params));
+            let _pctx = stdx::panic_context::enter(format!("request: {} {:#?}", R::METHOD, params));
             let result = f(world.0, params);
             result_to_response::<R>(id, result)
         })
@@ -64,7 +64,7 @@ impl<'a> RequestDispatcher<'a> {
             let world = self.global_state.snapshot();
 
             move || {
-                let _ctx =
+                let _pctx =
                     stdx::panic_context::enter(format!("request: {} {:#?}", R::METHOD, params));
                 let result = f(world, params);
                 Task::Response(result_to_response::<R>(id, result))
@@ -160,7 +160,7 @@ impl<'a> NotificationDispatcher<'a> {
                 return Ok(self);
             }
         };
-        stdx::panic_context::enter(format!("notification: {}", N::METHOD));
+        let _pctx = stdx::panic_context::enter(format!("notification: {}", N::METHOD));
         f(self.global_state, params)?;
         Ok(self)
     }
diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs
index 2680e5f08b..f2d57f9867 100644
--- a/crates/rust-analyzer/src/handlers.rs
+++ b/crates/rust-analyzer/src/handlers.rs
@@ -570,7 +570,7 @@ pub(crate) fn handle_completion(
     let line_endings = snap.file_line_endings(position.file_id);
     let items: Vec<CompletionItem> = items
         .into_iter()
-        .flat_map(|item| to_proto::completion_item(&line_index, line_endings, item))
+        .map(|item| to_proto::completion_item(&line_index, line_endings, item))
         .collect();
 
     Ok(Some(items.into()))
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index d0fb92f89b..aeacde0f7a 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -160,13 +160,7 @@ pub(crate) fn completion_item(
     line_index: &LineIndex,
     line_endings: LineEndings,
     completion_item: CompletionItem,
-) -> Vec<lsp_types::CompletionItem> {
-    fn set_score(res: &mut lsp_types::CompletionItem, label: &str) {
-        res.preselect = Some(true);
-        // HACK: sort preselect items first
-        res.sort_text = Some(format!(" {}", label));
-    }
-
+) -> lsp_types::CompletionItem {
     let mut additional_text_edits = Vec::new();
     let mut text_edit = None;
     // LSP does not allow arbitrary edits in completion, so we have to do a
@@ -206,7 +200,9 @@ pub(crate) fn completion_item(
     };
 
     if completion_item.score().is_some() {
-        set_score(&mut res, completion_item.label());
+        res.preselect = Some(true);
+        // HACK: sort preselect items first
+        res.sort_text = Some(format!(" {}", completion_item.label()));
     }
 
     if completion_item.deprecated() {
@@ -221,22 +217,9 @@ pub(crate) fn completion_item(
         });
     }
 
-    let mut all_results = match completion_item.ref_match() {
-        Some(ref_match) => {
-            let mut refed = res.clone();
-            let (mutability, _score) = ref_match;
-            let label = format!("&{}{}", mutability.as_keyword_for_ref(), refed.label);
-            set_score(&mut refed, &label);
-            refed.label = label;
-            vec![res, refed]
-        }
-        None => vec![res],
-    };
+    res.insert_text_format = Some(insert_text_format(completion_item.insert_text_format()));
 
-    for mut r in all_results.iter_mut() {
-        r.insert_text_format = Some(insert_text_format(completion_item.insert_text_format()));
-    }
-    all_results
+    res
 }
 
 pub(crate) fn signature_help(
@@ -762,7 +745,7 @@ pub(crate) fn runnable(
     let workspace_root = spec.as_ref().map(|it| it.workspace_root.clone());
     let target = spec.as_ref().map(|s| s.target.clone());
     let (cargo_args, executable_args) =
-        CargoTargetSpec::runnable_args(snap, spec, &runnable.kind, &runnable.cfg)?;
+        CargoTargetSpec::runnable_args(snap, spec, &runnable.kind, &runnable.cfg_exprs)?;
     let label = runnable.label(target);
     let location = location_link(snap, None, runnable.nav)?;
 
@@ -792,48 +775,6 @@ mod tests {
 
     use super::*;
 
-    #[test]
-    fn test_completion_with_ref() {
-        let fixture = r#"
-        struct Foo;
-        fn foo(arg: &Foo) {}
-        fn main() {
-            let arg = Foo;
-            foo(<|>)
-        }"#;
-
-        let (offset, text) = test_utils::extract_offset(fixture);
-        let line_index = LineIndex::new(&text);
-        let (analysis, file_id) = Analysis::from_single_file(text);
-        let completions: Vec<(String, Option<String>)> = analysis
-            .completions(
-                &ide::CompletionConfig::default(),
-                base_db::FilePosition { file_id, offset },
-            )
-            .unwrap()
-            .unwrap()
-            .into_iter()
-            .filter(|c| c.label().ends_with("arg"))
-            .map(|c| completion_item(&line_index, LineEndings::Unix, c))
-            .flat_map(|comps| comps.into_iter().map(|c| (c.label, c.sort_text)))
-            .collect();
-        expect_test::expect![[r#"
-            [
-                (
-                    "arg",
-                    None,
-                ),
-                (
-                    "&arg",
-                    Some(
-                        " &arg",
-                    ),
-                ),
-            ]
-        "#]]
-        .assert_debug_eq(&completions);
-    }
-
     #[test]
     fn conv_fold_line_folding_only_fixup() {
         let text = r#"mod a;
diff --git a/crates/stdx/src/panic_context.rs b/crates/stdx/src/panic_context.rs
index fd232e0cce..8d51e20d39 100644
--- a/crates/stdx/src/panic_context.rs
+++ b/crates/stdx/src/panic_context.rs
@@ -4,7 +4,7 @@
 
 use std::{cell::RefCell, panic, sync::Once};
 
-pub fn enter(context: String) -> impl Drop {
+pub fn enter(context: String) -> PanicContext {
     static ONCE: Once = Once::new();
     ONCE.call_once(PanicContext::init);
 
@@ -13,7 +13,7 @@ pub fn enter(context: String) -> impl Drop {
 }
 
 #[must_use]
-struct PanicContext {
+pub struct PanicContext {
     _priv: (),
 }
 
diff --git a/docs/dev/debugging.md b/docs/dev/debugging.md
index 8c48fd5a1f..59a83f7d76 100644
--- a/docs/dev/debugging.md
+++ b/docs/dev/debugging.md
@@ -53,7 +53,7 @@ To apply changes to an already running debug process, press <kbd>Ctrl+Shift+P</k
 
 - A list of running processes should appear. Select the `rust-analyzer` from this repo.
 
-- Navigate to `crates/rust-analyzer/src/main_loop.rs` and add a breakpoint to the `on_request` function.
+- Navigate to `crates/rust-analyzer/src/main_loop.rs` and add a breakpoint to the `on_task` function.
 
 - Go back to the `[Extension Development Host]` instance and hover over a Rust variable and your breakpoint should hit.
 
diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc
index b9d907a4ae..7e8adcdb75 100644
--- a/docs/user/manual.adoc
+++ b/docs/user/manual.adoc
@@ -366,13 +366,6 @@ Cursor position or selection is signified by `┃` character.
 
 include::./generated_assists.adoc[]
 
-== Diagnostics
-
-While most errors and warnings provided by rust-analyzer come from the `cargo check` integration, there's a growing number of diagnostics implemented using rust-analyzer's own analysis.
-These diagnostics don't respect `#[allow]` or `#[deny]` attributes yet, but can be turned off using the `rust-analyzer.diagnostics.enable`, `rust-analyzer.diagnostics.enableExperimental` or `rust-analyzer.diagnostics.disabled` settings.
-
-include::./generated_diagnostic.adoc[]
-
 == Editor Features
 === VS Code
 


Test patch:
diff --git a/crates/cfg/src/tests.rs b/crates/cfg/src/tests.rs
deleted file mode 100644
index bd0f9ec483..0000000000
--- a/crates/cfg/src/tests.rs
+++ /dev/null
@@ -1,193 +0,0 @@
-use expect_test::{expect, Expect};
-use mbe::ast_to_token_tree;
-use syntax::{ast, AstNode};
-
-use crate::{CfgAtom, CfgExpr, CfgOptions, DnfExpr};
-
-fn assert_parse_result(input: &str, expected: CfgExpr) {
-    let (tt, _) = {
-        let source_file = ast::SourceFile::parse(input).ok().unwrap();
-        let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
-        ast_to_token_tree(&tt).unwrap()
-    };
-    let cfg = CfgExpr::parse(&tt);
-    assert_eq!(cfg, expected);
-}
-
-fn check_dnf(input: &str, expect: Expect) {
-    let (tt, _) = {
-        let source_file = ast::SourceFile::parse(input).ok().unwrap();
-        let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
-        ast_to_token_tree(&tt).unwrap()
-    };
-    let cfg = CfgExpr::parse(&tt);
-    let actual = format!("#![cfg({})]", DnfExpr::new(cfg));
-    expect.assert_eq(&actual);
-}
-
-fn check_why_inactive(input: &str, opts: &CfgOptions, expect: Expect) {
-    let (tt, _) = {
-        let source_file = ast::SourceFile::parse(input).ok().unwrap();
-        let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
-        ast_to_token_tree(&tt).unwrap()
-    };
-    let cfg = CfgExpr::parse(&tt);
-    let dnf = DnfExpr::new(cfg);
-    let why_inactive = dnf.why_inactive(opts).unwrap().to_string();
-    expect.assert_eq(&why_inactive);
-}
-
-#[track_caller]
-fn check_enable_hints(input: &str, opts: &CfgOptions, expected_hints: &[&str]) {
-    let (tt, _) = {
-        let source_file = ast::SourceFile::parse(input).ok().unwrap();
-        let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
-        ast_to_token_tree(&tt).unwrap()
-    };
-    let cfg = CfgExpr::parse(&tt);
-    let dnf = DnfExpr::new(cfg);
-    let hints = dnf.compute_enable_hints(opts).map(|diff| diff.to_string()).collect::<Vec<_>>();
-    assert_eq!(hints, expected_hints);
-}
-
-#[test]
-fn test_cfg_expr_parser() {
-    assert_parse_result("#![cfg(foo)]", CfgAtom::Flag("foo".into()).into());
-    assert_parse_result("#![cfg(foo,)]", CfgAtom::Flag("foo".into()).into());
-    assert_parse_result(
-        "#![cfg(not(foo))]",
-        CfgExpr::Not(Box::new(CfgAtom::Flag("foo".into()).into())),
-    );
-    assert_parse_result("#![cfg(foo(bar))]", CfgExpr::Invalid);
-
-    // Only take the first
-    assert_parse_result(r#"#![cfg(foo, bar = "baz")]"#, CfgAtom::Flag("foo".into()).into());
-
-    assert_parse_result(
-        r#"#![cfg(all(foo, bar = "baz"))]"#,
-        CfgExpr::All(vec![
-            CfgAtom::Flag("foo".into()).into(),
-            CfgAtom::KeyValue { key: "bar".into(), value: "baz".into() }.into(),
-        ]),
-    );
-
-    assert_parse_result(
-        r#"#![cfg(any(not(), all(), , bar = "baz",))]"#,
-        CfgExpr::Any(vec![
-            CfgExpr::Not(Box::new(CfgExpr::Invalid)),
-            CfgExpr::All(vec![]),
-            CfgExpr::Invalid,
-            CfgAtom::KeyValue { key: "bar".into(), value: "baz".into() }.into(),
-        ]),
-    );
-}
-
-#[test]
-fn smoke() {
-    check_dnf("#![cfg(test)]", expect![[r#"#![cfg(test)]"#]]);
-    check_dnf("#![cfg(not(test))]", expect![[r#"#![cfg(not(test))]"#]]);
-    check_dnf("#![cfg(not(not(test)))]", expect![[r#"#![cfg(test)]"#]]);
-
-    check_dnf("#![cfg(all(a, b))]", expect![[r#"#![cfg(all(a, b))]"#]]);
-    check_dnf("#![cfg(any(a, b))]", expect![[r#"#![cfg(any(a, b))]"#]]);
-
-    check_dnf("#![cfg(not(a))]", expect![[r#"#![cfg(not(a))]"#]]);
-}
-
-#[test]
-fn distribute() {
-    check_dnf("#![cfg(all(any(a, b), c))]", expect![[r#"#![cfg(any(all(a, c), all(b, c)))]"#]]);
-    check_dnf("#![cfg(all(c, any(a, b)))]", expect![[r#"#![cfg(any(all(c, a), all(c, b)))]"#]]);
-    check_dnf(
-        "#![cfg(all(any(a, b), any(c, d)))]",
-        expect![[r#"#![cfg(any(all(a, c), all(a, d), all(b, c), all(b, d)))]"#]],
-    );
-
-    check_dnf(
-        "#![cfg(all(any(a, b, c), any(d, e, f), g))]",
-        expect![[
-            r#"#![cfg(any(all(a, d, g), all(a, e, g), all(a, f, g), all(b, d, g), all(b, e, g), all(b, f, g), all(c, d, g), all(c, e, g), all(c, f, g)))]"#
-        ]],
-    );
-}
-
-#[test]
-fn demorgan() {
-    check_dnf("#![cfg(not(all(a, b)))]", expect![[r#"#![cfg(any(not(a), not(b)))]"#]]);
-    check_dnf("#![cfg(not(any(a, b)))]", expect![[r#"#![cfg(all(not(a), not(b)))]"#]]);
-
-    check_dnf("#![cfg(not(all(not(a), b)))]", expect![[r#"#![cfg(any(a, not(b)))]"#]]);
-    check_dnf("#![cfg(not(any(a, not(b))))]", expect![[r#"#![cfg(all(not(a), b))]"#]]);
-}
-
-#[test]
-fn nested() {
-    check_dnf("#![cfg(all(any(a), not(all(any(b)))))]", expect![[r#"#![cfg(all(a, not(b)))]"#]]);
-
-    check_dnf("#![cfg(any(any(a, b)))]", expect![[r#"#![cfg(any(a, b))]"#]]);
-    check_dnf("#![cfg(not(any(any(a, b))))]", expect![[r#"#![cfg(all(not(a), not(b)))]"#]]);
-    check_dnf("#![cfg(all(all(a, b)))]", expect![[r#"#![cfg(all(a, b))]"#]]);
-    check_dnf("#![cfg(not(all(all(a, b))))]", expect![[r#"#![cfg(any(not(a), not(b)))]"#]]);
-}
-
-#[test]
-fn hints() {
-    let mut opts = CfgOptions::default();
-
-    check_enable_hints("#![cfg(test)]", &opts, &["enable test"]);
-    check_enable_hints("#![cfg(not(test))]", &opts, &[]);
-
-    check_enable_hints("#![cfg(any(a, b))]", &opts, &["enable a", "enable b"]);
-    check_enable_hints("#![cfg(any(b, a))]", &opts, &["enable b", "enable a"]);
-
-    check_enable_hints("#![cfg(all(a, b))]", &opts, &["enable a and b"]);
-
-    opts.insert_atom("test".into());
-
-    check_enable_hints("#![cfg(test)]", &opts, &[]);
-    check_enable_hints("#![cfg(not(test))]", &opts, &["disable test"]);
-}
-
-/// Tests that we don't suggest hints for cfgs that express an inconsistent formula.
-#[test]
-fn hints_impossible() {
-    let mut opts = CfgOptions::default();
-
-    check_enable_hints("#![cfg(all(test, not(test)))]", &opts, &[]);
-
-    opts.insert_atom("test".into());
-
-    check_enable_hints("#![cfg(all(test, not(test)))]", &opts, &[]);
-}
-
-#[test]
-fn why_inactive() {
-    let mut opts = CfgOptions::default();
-    opts.insert_atom("test".into());
-    opts.insert_atom("test2".into());
-
-    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(all(not(test), not(test2)))]",
-        &opts,
-        expect![["test and test2 are enabled"]],
-    );
-    check_why_inactive("#![cfg(all(a, b))]", &opts, expect![["a and b are disabled"]]);
-    check_why_inactive(
-        "#![cfg(all(not(test), a))]",
-        &opts,
-        expect![["test is enabled and a is disabled"]],
-    );
-    check_why_inactive(
-        "#![cfg(all(not(test), test2, a))]",
-        &opts,
-        expect![["test is enabled and a is disabled"]],
-    );
-    check_why_inactive(
-        "#![cfg(all(not(test), not(test2), a))]",
-        &opts,
-        expect![["test and test2 are enabled and a is disabled"]],
-    );
-}
diff --git a/crates/hir_def/src/nameres/tests/diagnostics.rs b/crates/hir_def/src/nameres/tests/diagnostics.rs
index 5972248de7..576b813d2a 100644
--- a/crates/hir_def/src/nameres/tests/diagnostics.rs
+++ b/crates/hir_def/src/nameres/tests/diagnostics.rs
@@ -129,25 +129,3 @@ fn unresolved_module() {
         ",
     );
 }
-
-#[test]
-fn inactive_item() {
-    // Additional tests in `cfg` crate. This only tests disabled cfgs.
-
-    check_diagnostics(
-        r#"
-        //- /lib.rs
-          #[cfg(no)] pub fn f() {}
-        //^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no is disabled
-
-          #[cfg(no)] #[cfg(no2)] mod m;
-        //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: no and no2 are disabled
-
-          #[cfg(all(not(a), b))] enum E {}
-        //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: b is disabled
-
-          #[cfg(feature = "std")] use std;
-        //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ code is inactive due to #[cfg] directives: feature = "std" is disabled
-        "#,
-    );
-}