Repository: rust-lang/rust-analyzer
Pull request #13380 (2022-10-10 07:47:24)

Honor cfg attributes on params when lowering their patterns
Closes https://github.com/rust-lang/rust-analyzer/issues/13375


Patch:
diff --git a/Cargo.lock b/Cargo.lock
index e45cde6838..93b7f746e0 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2082,18 +2082,18 @@ checksum = "06069a848f95fceae3e5e03c0ddc8cb78452b56654ee0c8e68f938cf790fb9e3"
 
 [[package]]
 name = "xflags"
-version = "0.3.0"
+version = "0.2.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cbf19f5031a1a812e96fede16f8161218883079946cea87619d3613db1efd268"
+checksum = "3f14fe1ed41a5a2b5ef3f565586c4a8a559ee55d3953faab360a771135bdee00"
 dependencies = [
  "xflags-macros",
 ]
 
 [[package]]
 name = "xflags-macros"
-version = "0.3.0"
+version = "0.2.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2afbd7f2039bb6cad2dd45f0c5dff49c0d4e26118398768b7a605524d4251809"
+checksum = "45d11d5fc2a97287eded8b170ca80533b3c42646dd7fa386a5eb045817921022"
 
 [[package]]
 name = "xshell"
diff --git a/crates/hir-def/src/body.rs b/crates/hir-def/src/body.rs
index 2dc7714bbb..759f3b8c04 100644
--- a/crates/hir-def/src/body.rs
+++ b/crates/hir-def/src/body.rs
@@ -311,7 +311,20 @@ impl Body {
             DefWithBodyId::FunctionId(f) => {
                 let f = f.lookup(db);
                 let src = f.source(db);
-                params = src.value.param_list();
+                params = src.value.param_list().map(|param_list| {
+                    let item_tree = f.id.item_tree(db);
+                    let func = &item_tree[f.id.value];
+                    let krate = f.container.module(db).krate;
+                    let crate_graph = db.crate_graph();
+                    (
+                        param_list,
+                        func.params.clone().map(move |param| {
+                            item_tree
+                                .attrs(db, krate, param.into())
+                                .is_cfg_enabled(&crate_graph[krate].cfg_options)
+                        }),
+                    )
+                });
                 (src.file_id, f.module(db), src.value.body().map(ast::Expr::from))
             }
             DefWithBodyId::ConstId(c) => {
@@ -334,6 +347,7 @@ impl Body {
         let expander = Expander::new(db, file_id, module);
         let (mut body, source_map) = Body::new(db, expander, params, body);
         body.shrink_to_fit();
+
         (Arc::new(body), Arc::new(source_map))
     }
 
@@ -370,7 +384,7 @@ impl Body {
     fn new(
         db: &dyn DefDatabase,
         expander: Expander,
-        params: Option<ast::ParamList>,
+        params: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
         body: Option<ast::Expr>,
     ) -> (Body, BodySourceMap) {
         lower::lower(db, expander, params, body)
diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs
index c4f91e49a6..ccc01c3efc 100644
--- a/crates/hir-def/src/body/lower.rs
+++ b/crates/hir-def/src/body/lower.rs
@@ -77,7 +77,7 @@ impl<'a> LowerCtx<'a> {
 pub(super) fn lower(
     db: &dyn DefDatabase,
     expander: Expander,
-    params: Option<ast::ParamList>,
+    params: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
     body: Option<ast::Expr>,
 ) -> (Body, BodySourceMap) {
     ExprCollector {
@@ -119,11 +119,13 @@ struct ExprCollector<'a> {
 impl ExprCollector<'_> {
     fn collect(
         mut self,
-        param_list: Option<ast::ParamList>,
+        param_list: Option<(ast::ParamList, impl Iterator<Item = bool>)>,
         body: Option<ast::Expr>,
     ) -> (Body, BodySourceMap) {
-        if let Some(param_list) = param_list {
-            if let Some(self_param) = param_list.self_param() {
+        if let Some((param_list, mut attr_enabled)) = param_list {
+            if let Some(self_param) =
+                param_list.self_param().filter(|_| attr_enabled.next().unwrap_or(false))
+            {
                 let ptr = AstPtr::new(&self_param);
                 let param_pat = self.alloc_pat(
                     Pat::Bind {
@@ -139,7 +141,11 @@ impl ExprCollector<'_> {
                 self.body.params.push(param_pat);
             }
 
-            for pat in param_list.params().filter_map(|param| param.pat()) {
+            for pat in param_list
+                .params()
+                .zip(attr_enabled)
+                .filter_map(|(param, enabled)| param.pat().filter(|_| enabled))
+            {
                 let param_pat = self.collect_pat(pat);
                 self.body.params.push(param_pat);
             }
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index f5324208c9..e08dd8dade 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -2807,10 +2807,6 @@ impl Type {
                     let subst = TyBuilder::subst_for_def(db, id, None).fill_with_unknown().build();
                     Some(subst)
                 }
-                ItemContainerId::ImplId(id) => {
-                    let subst = TyBuilder::subst_for_def(db, id, None).fill_with_unknown().build();
-                    Some(subst)
-                }
                 _ => None,
             },
             _ => None,
diff --git a/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs
index 970e948dfd..8d5cab283d 100644
--- a/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs
+++ b/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -9,7 +9,7 @@ use ide_db::{
     search::FileReference,
     FxHashSet, RootDatabase,
 };
-use itertools::Itertools;
+use itertools::{Itertools, Position};
 use syntax::{
     ast::{
         self, edit::IndentLevel, edit_in_place::Indent, make, AstNode, HasAttrs, HasGenericParams,
@@ -298,7 +298,37 @@ fn update_variant(variant: &ast::Variant, generics: Option<ast::GenericParamList
     let name = variant.name()?;
     let ty = generics
         .filter(|generics| generics.generic_params().count() > 0)
-        .map(|generics| make::ty(&format!("{}{}", &name.text(), generics.to_generic_args())))
+        .map(|generics| {
+            let mut generic_str = String::with_capacity(8);
+
+            for (p, more) in generics.generic_params().with_position().map(|p| match p {
+                Position::First(p) | Position::Middle(p) => (p, true),
+                Position::Last(p) | Position::Only(p) => (p, false),
+            }) {
+                match p {
+                    ast::GenericParam::ConstParam(konst) => {
+                        if let Some(name) = konst.name() {
+                            generic_str.push_str(name.text().as_str());
+                        }
+                    }
+                    ast::GenericParam::LifetimeParam(lt) => {
+                        if let Some(lt) = lt.lifetime() {
+                            generic_str.push_str(lt.text().as_str());
+                        }
+                    }
+                    ast::GenericParam::TypeParam(ty) => {
+                        if let Some(name) = ty.name() {
+                            generic_str.push_str(name.text().as_str());
+                        }
+                    }
+                }
+                if more {
+                    generic_str.push_str(", ");
+                }
+            }
+
+            make::ty(&format!("{}<{}>", &name.text(), &generic_str))
+        })
         .unwrap_or_else(|| make::ty(&name.text()));
 
     // change from a record to a tuple field list
diff --git a/crates/ide-assists/src/handlers/generate_impl.rs b/crates/ide-assists/src/handlers/generate_impl.rs
index 307cea3d0a..68287a20bf 100644
--- a/crates/ide-assists/src/handlers/generate_impl.rs
+++ b/crates/ide-assists/src/handlers/generate_impl.rs
@@ -52,7 +52,6 @@ mod tests {
 
     use super::*;
 
-    // FIXME: break up into separate test fns
     #[test]
     fn test_add_impl() {
         check_assist(
@@ -135,18 +134,6 @@ mod tests {
             }"#,
         );
 
-        check_assist(
-            generate_impl,
-            r#"
-            struct Defaulted<const N: i32 = 0> {}$0"#,
-            r#"
-            struct Defaulted<const N: i32 = 0> {}
-
-            impl<const N: i32> Defaulted<N> {
-                $0
-            }"#,
-        );
-
         check_assist(
             generate_impl,
             r#"pub trait Trait {}
diff --git a/crates/ide-assists/src/utils.rs b/crates/ide-assists/src/utils.rs
index 38396cd7d7..4ab6e2627f 100644
--- a/crates/ide-assists/src/utils.rs
+++ b/crates/ide-assists/src/utils.rs
@@ -2,6 +2,8 @@
 
 use std::ops;
 
+use itertools::Itertools;
+
 pub(crate) use gen_trait_fn_body::gen_trait_fn_body;
 use hir::{db::HirDatabase, HirDisplay, Semantics};
 use ide_db::{famous_defs::FamousDefs, path_transform::PathTransform, RootDatabase, SnippetCap};
@@ -13,7 +15,7 @@ use syntax::{
         edit_in_place::{AttrsOwnerEdit, Removable},
         make, HasArgList, HasAttrs, HasGenericParams, HasName, HasTypeBounds, Whitespace,
     },
-    ted, AstNode, AstToken, Direction, SourceFile,
+    ted, AstNode, AstToken, Direction, SmolStr, SourceFile,
     SyntaxKind::*,
     SyntaxNode, TextRange, TextSize, T,
 };
@@ -422,44 +424,34 @@ pub(crate) fn generate_trait_impl_text(adt: &ast::Adt, trait_text: &str, code: &
 }
 
 fn generate_impl_text_inner(adt: &ast::Adt, trait_text: Option<&str>, code: &str) -> String {
-    // Ensure lifetime params are before type & const params
-    let generic_params = adt.generic_param_list().map(|generic_params| {
-        let lifetime_params =
-            generic_params.lifetime_params().map(ast::GenericParam::LifetimeParam);
-        let ty_or_const_params = generic_params.type_or_const_params().filter_map(|param| {
-            // remove defaults since they can't be specified in impls
-            match param {
-                ast::TypeOrConstParam::Type(param) => {
-                    let param = param.clone_for_update();
-                    param.remove_default();
-                    Some(ast::GenericParam::TypeParam(param))
-                }
-                ast::TypeOrConstParam::Const(param) => {
-                    let param = param.clone_for_update();
-                    param.remove_default();
-                    Some(ast::GenericParam::ConstParam(param))
-                }
-            }
-        });
-
-        make::generic_param_list(itertools::chain(lifetime_params, ty_or_const_params))
-    });
-
-    // FIXME: use syntax::make & mutable AST apis instead
-    // `trait_text` and `code` can't be opaque blobs of text
+    let generic_params = adt.generic_param_list();
     let mut buf = String::with_capacity(code.len());
-
-    // Copy any cfg attrs from the original adt
     buf.push_str("\n\n");
-    let cfg_attrs = adt
-        .attrs()
-        .filter(|attr| attr.as_simple_call().map(|(name, _arg)| name == "cfg").unwrap_or(false));
-    cfg_attrs.for_each(|attr| buf.push_str(&format!("{attr}\n")));
-
-    // `impl{generic_params} {trait_text} for {name}{generic_params.to_generic_args()}`
+    adt.attrs()
+        .filter(|attr| attr.as_simple_call().map(|(name, _arg)| name == "cfg").unwrap_or(false))
+        .for_each(|attr| buf.push_str(format!("{}\n", attr).as_str()));
     buf.push_str("impl");
     if let Some(generic_params) = &generic_params {
-        format_to!(buf, "{generic_params}");
+        let lifetimes = generic_params.lifetime_params().map(|lt| format!("{}", lt.syntax()));
+        let toc_params = generic_params.type_or_const_params().map(|toc_param| {
+            let type_param = match toc_param {
+                ast::TypeOrConstParam::Type(x) => x,
+                ast::TypeOrConstParam::Const(x) => return x.syntax().to_string(),
+            };
+            let mut buf = String::new();
+            if let Some(it) = type_param.name() {
+                format_to!(buf, "{}", it.syntax());
+            }
+            if let Some(it) = type_param.colon_token() {
+                format_to!(buf, "{} ", it);
+            }
+            if let Some(it) = type_param.type_bound_list() {
+                format_to!(buf, "{}", it.syntax());
+            }
+            buf
+        });
+        let generics = lifetimes.chain(toc_params).format(", ");
+        format_to!(buf, "<{}>", generics);
     }
     buf.push(' ');
     if let Some(trait_text) = trait_text {
@@ -468,15 +460,23 @@ fn generate_impl_text_inner(adt: &ast::Adt, trait_text: Option<&str>, code: &str
     }
     buf.push_str(&adt.name().unwrap().text());
     if let Some(generic_params) = generic_params {
-        format_to!(buf, "{}", generic_params.to_generic_args());
+        let lifetime_params = generic_params
+            .lifetime_params()
+            .filter_map(|it| it.lifetime())
+            .map(|it| SmolStr::from(it.text()));
+        let toc_params = generic_params
+            .type_or_const_params()
+            .filter_map(|it| it.name())
+            .map(|it| SmolStr::from(it.text()));
+        format_to!(buf, "<{}>", lifetime_params.chain(toc_params).format(", "))
     }
 
     match adt.where_clause() {
         Some(where_clause) => {
-            format_to!(buf, "\n{where_clause}\n{{\n{code}\n}}");
+            format_to!(buf, "\n{}\n{{\n{}\n}}", where_clause, code);
         }
         None => {
-            format_to!(buf, " {{\n{code}\n}}");
+            format_to!(buf, " {{\n{}\n}}", code);
         }
     }
 
diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs
index a5e854b74d..f0563e290d 100644
--- a/crates/ide-completion/src/context.rs
+++ b/crates/ide-completion/src/context.rs
@@ -1,4 +1,4 @@
-//! See `CompletionContext` structure.
+//! See [`CompletionContext`] structure.
 
 mod analysis;
 #[cfg(test)]
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs
index d96827326c..92ce26b422 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -232,13 +232,8 @@ pub(crate) fn token_as_doc_comment(doc_token: &SyntaxToken) -> Option<DocComment
     (match_ast! {
         match doc_token {
             ast::Comment(comment) => TextSize::try_from(comment.prefix().len()).ok(),
-            ast::String(string) => {
-                doc_token.parent_ancestors().find_map(ast::Attr::cast).filter(|attr| attr.simple_name().as_deref() == Some("doc"))?;
-                if doc_token.parent_ancestors().find_map(ast::MacroCall::cast).filter(|mac| mac.path().and_then(|p| p.segment()?.name_ref()).as_ref().map(|n| n.text()).as_deref() == Some("include_str")).is_some() {
-                    return None;
-                }
-                string.open_quote_text_range().map(|it| it.len())
-            },
+            ast::String(string) => doc_token.parent_ancestors().find_map(ast::Attr::cast)
+                .filter(|attr| attr.simple_name().as_deref() == Some("doc")).and_then(|_| string.open_quote_text_range().map(|it| it.len())),
             _ => None,
         }
     }).map(|prefix_len| DocCommentToken { prefix_len, doc_token: doc_token.clone() })
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index d0be1b3f40..36a648fe4a 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -48,14 +48,10 @@ pub(crate) fn goto_definition(
             _ => 1,
         })?;
     if let Some(doc_comment) = token_as_doc_comment(&original_token) {
-        return doc_comment.get_definition_with_descend_at(
-            sema,
-            position.offset,
-            |def, _, link_range| {
-                let nav = def.try_to_nav(db)?;
-                Some(RangeInfo::new(link_range, vec![nav]))
-            },
-        );
+        return doc_comment.get_definition_with_descend_at(sema, position.offset, |def, _, _| {
+            let nav = def.try_to_nav(db)?;
+            Some(RangeInfo::new(original_token.text_range(), vec![nav]))
+        });
     }
     let navs = sema
         .descend_into_macros(original_token.clone())
@@ -99,14 +95,6 @@ fn try_lookup_include_path(
     if !matches!(&*name.text(), "include" | "include_str" | "include_bytes") {
         return None;
     }
-
-    // Ignore non-built-in macros to account for shadowing
-    if let Some(it) = sema.resolve_macro_call(&macro_call) {
-        if !matches!(it.kind(sema.db), hir::MacroKind::BuiltIn) {
-            return None;
-        }
-    }
-
     let file_id = sema.db.resolve_path(AnchoredPath { anchor: file_id, path: &path })?;
     let size = sema.db.file_text(file_id).len().try_into().ok()?;
     Some(NavigationTarget {
@@ -168,6 +156,9 @@ mod tests {
     fn check(ra_fixture: &str) {
         let (analysis, position, expected) = fixture::annotations(ra_fixture);
         let navs = analysis.goto_definition(position).unwrap().expect("no definition found").info;
+        if navs.is_empty() {
+            panic!("unresolved reference")
+        }
 
         let cmp = |&FileRange { file_id, range }: &_| (file_id, range.start());
         let navs = navs
@@ -1357,10 +1348,6 @@ fn f(e: Enum) {
         check(
             r#"
 //- /main.rs
-
-#[rustc_builtin_macro]
-macro_rules! include_str {}
-
 fn main() {
     let str = include_str!("foo.txt$0");
 }
@@ -1370,42 +1357,6 @@ fn main() {
 "#,
         );
     }
-
-    #[test]
-    fn goto_doc_include_str() {
-        check(
-            r#"
-//- /main.rs
-#[rustc_builtin_macro]
-macro_rules! include_str {}
-
-#[doc = include_str!("docs.md$0")]
-struct Item;
-
-//- /docs.md
-// docs
-//^file
-"#,
-        );
-    }
-
-    #[test]
-    fn goto_shadow_include() {
-        check(
-            r#"
-//- /main.rs
-macro_rules! include {
-    ("included.rs") => {}
-}
-
-include!("included.rs$0");
-
-//- /included.rs
-// empty
-"#,
-        );
-    }
-
     #[cfg(test)]
     mod goto_impl_of_trait_fn {
         use super::check;
diff --git a/crates/ide/src/highlight_related.rs b/crates/ide/src/highlight_related.rs
index 540a115832..1bdd626f1e 100644
--- a/crates/ide/src/highlight_related.rs
+++ b/crates/ide/src/highlight_related.rs
@@ -1373,22 +1373,6 @@ fn main() {
     ().func$0();
      //^^^^
 }
-"#,
-        );
-    }
-
-    #[test]
-    fn test_assoc_type_highlighting() {
-        check(
-            r#"
-trait Trait {
-    type Output;
-      // ^^^^^^
-}
-impl Trait for () {
-    type Output$0 = ();
-      // ^^^^^^
-}
 "#,
         );
     }
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index a4e6550984..71566d5de6 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -25,7 +25,7 @@ itertools = "0.10.3"
 scip = "0.1.1"
 lsp-types = { version = "0.93.1", features = ["proposed"] }
 parking_lot = "0.12.1"
-xflags = "0.3.0"
+xflags = "0.2.4"
 oorandom = "11.1.3"
 rustc-hash = "1.1.0"
 serde = { version = "1.0.137", features = ["derive"] }
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index eabfcf1944..f6a6802972 100644
--- a/crates/rust-analyzer/src/bin/main.rs
+++ b/crates/rust-analyzer/src/bin/main.rs
@@ -37,15 +37,16 @@ fn main() {
         process::exit(code);
     }
 
-    let flags = flags::RustAnalyzer::from_env_or_exit();
-    if let Err(err) = try_main(flags) {
+    if let Err(err) = try_main() {
         tracing::error!("Unexpected error: {}", err);
         eprintln!("{}", err);
         process::exit(101);
     }
 }
 
-fn try_main(flags: flags::RustAnalyzer) -> Result<()> {
+fn try_main() -> Result<()> {
+    let flags = flags::RustAnalyzer::from_env()?;
+
     #[cfg(debug_assertions)]
     if flags.wait_dbg || env::var("RA_WAIT_DBG").is_ok() {
         #[allow(unused_mut)]
@@ -75,6 +76,10 @@ fn try_main(flags: flags::RustAnalyzer) -> Result<()> {
                 println!("rust-analyzer {}", rust_analyzer::version());
                 return Ok(());
             }
+            if cmd.help {
+                println!("{}", flags::RustAnalyzer::HELP);
+                return Ok(());
+            }
             with_extra_thread("LspServer", run_server)?;
         }
         flags::RustAnalyzerCmd::ProcMacro(flags::ProcMacro) => {
diff --git a/crates/rust-analyzer/src/cli/flags.rs b/crates/rust-analyzer/src/cli/flags.rs
index 5bcc97e226..aa32654fbd 100644
--- a/crates/rust-analyzer/src/cli/flags.rs
+++ b/crates/rust-analyzer/src/cli/flags.rs
@@ -31,6 +31,8 @@ xflags::xflags! {
         default cmd lsp-server {
             /// Print version.
             optional --version
+            /// Print help.
+            optional -h, --help
 
             /// Dump a LSP config JSON schema.
             optional --print-config-schema
@@ -52,10 +54,10 @@ xflags::xflags! {
         }
 
         /// Batch typecheck project and print summary statistics
-        cmd analysis-stats {
+        cmd analysis-stats
             /// Directory with Cargo.toml.
             required path: PathBuf
-
+        {
             optional --output format: OutputFormat
 
             /// Randomize order in which crates, modules, and items are processed.
@@ -82,37 +84,38 @@ xflags::xflags! {
             optional --skip-inference
         }
 
-        cmd diagnostics {
+        cmd diagnostics
             /// Directory with Cargo.toml.
             required path: PathBuf
-
+        {
             /// Don't run build scripts or load `OUT_DIR` values by running `cargo check` before analysis.
             optional --disable-build-scripts
             /// Don't use expand proc macros.
             optional --disable-proc-macros
         }
 
-        cmd ssr {
+        cmd ssr
             /// A structured search replace rule (`$a.foo($b) ==> bar($a, $b)`)
             repeated rule: SsrRule
-        }
+        {}
 
-        cmd search {
+        cmd search
             /// A structured search replace pattern (`$a.foo($b)`)
             repeated pattern: SsrPattern
+        {
             /// Prints debug information for any nodes with source exactly equal to snippet.
             optional --debug snippet: String
         }
 
         cmd proc-macro {}
 
-        cmd lsif {
+        cmd lsif
             required path: PathBuf
-        }
+        {}
 
-        cmd scip {
+        cmd scip
             required path: PathBuf
-        }
+        {}
     }
 }
 
@@ -147,6 +150,7 @@ pub enum RustAnalyzerCmd {
 #[derive(Debug)]
 pub struct LspServer {
     pub version: bool,
+    pub help: bool,
     pub print_config_schema: bool,
 }
 
@@ -214,10 +218,7 @@ pub struct Scip {
 }
 
 impl RustAnalyzer {
-    #[allow(dead_code)]
-    pub fn from_env_or_exit() -> Self {
-        Self::from_env_or_exit_()
-    }
+    pub const HELP: &'static str = Self::HELP_;
 
     #[allow(dead_code)]
     pub fn from_env() -> xflags::Result<Self> {
diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs
index 229e7419b7..eadebbe8a2 100644
--- a/crates/syntax/src/ast/edit_in_place.rs
+++ b/crates/syntax/src/ast/edit_in_place.rs
@@ -235,24 +235,6 @@ impl ast::GenericParamList {
             }
         }
     }
-
-    /// Constructs a matching [`ast::GenericArgList`]
-    pub fn to_generic_args(&self) -> ast::GenericArgList {
-        let args = self.generic_params().filter_map(|param| match param {
-            ast::GenericParam::LifetimeParam(it) => {
-                Some(ast::GenericArg::LifetimeArg(make::lifetime_arg(it.lifetime()?)))
-            }
-            ast::GenericParam::TypeParam(it) => {
-                Some(ast::GenericArg::TypeArg(make::type_arg(make::ext::ty_name(it.name()?))))
-            }
-            ast::GenericParam::ConstParam(it) => {
-                // Name-only const params get parsed as `TypeArg`s
-                Some(ast::GenericArg::TypeArg(make::type_arg(make::ext::ty_name(it.name()?))))
-            }
-        });
-
-        make::generic_arg_list(args)
-    }
 }
 
 impl ast::WhereClause {
@@ -266,42 +248,6 @@ impl ast::WhereClause {
     }
 }
 
-impl ast::TypeParam {
-    pub fn remove_default(&self) {
-        if let Some((eq, last)) = self
-            .syntax()
-            .children_with_tokens()
-            .find(|it| it.kind() == T![=])
-            .zip(self.syntax().last_child_or_token())
-        {
-            ted::remove_all(eq..=last);
-
-            // remove any trailing ws
-            if let Some(last) = self.syntax().last_token().filter(|it| it.kind() == WHITESPACE) {
-                last.detach();
-            }
-        }
-    }
-}
-
-impl ast::ConstParam {
-    pub fn remove_default(&self) {
-        if let Some((eq, last)) = self
-            .syntax()
-            .children_with_tokens()
-            .find(|it| it.kind() == T![=])
-            .zip(self.syntax().last_child_or_token())
-        {
-            ted::remove_all(eq..=last);
-
-            // remove any trailing ws
-            if let Some(last) = self.syntax().last_token().filter(|it| it.kind() == WHITESPACE) {
-                last.detach();
-            }
-        }
-    }
-}
-
 pub trait Removable: AstNode {
     fn remove(&self);
 }
@@ -318,7 +264,7 @@ impl Removable for ast::TypeBoundList {
 impl ast::PathSegment {
     pub fn get_or_create_generic_arg_list(&self) -> ast::GenericArgList {
         if self.generic_arg_list().is_none() {
-            let arg_list = make::generic_arg_list(empty()).clone_for_update();
+            let arg_list = make::generic_arg_list().clone_for_update();
             ted::append_child(self.syntax(), arg_list.syntax());
         }
         self.generic_arg_list().unwrap()
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index c9a21e12c0..83f8bbac58 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -88,9 +88,6 @@ pub mod ext {
         block_expr(None, None)
     }
 
-    pub fn ty_name(name: ast::Name) -> ast::Type {
-        ty_path(ident_path(&format!("{name}")))
-    }
     pub fn ty_bool() -> ast::Type {
         ty_path(ident_path("bool"))
     }
@@ -163,7 +160,6 @@ pub fn assoc_item_list() -> ast::AssocItemList {
     ast_from_text("impl C for D {}")
 }
 
-// FIXME: `ty_params` should be `ast::GenericArgList`
 pub fn impl_(
     ty: ast::Path,
     params: Option<ast::GenericParamList>,
@@ -189,6 +185,10 @@ pub fn impl_trait(
     ast_from_text(&format!("impl{ty_params} {trait_} for {ty}{ty_params} {{}}"))
 }
 
+pub(crate) fn generic_arg_list() -> ast::GenericArgList {
+    ast_from_text("const S: T<> = ();")
+}
+
 pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment {
     ast_from_text(&format!("type __ = {name_ref};"))
 }
@@ -718,21 +718,6 @@ pub fn generic_param_list(
     ast_from_text(&format!("fn f<{args}>() {{ }}"))
 }
 
-pub fn type_arg(ty: ast::Type) -> ast::TypeArg {
-    ast_from_text(&format!("const S: T<{ty}> = ();"))
-}
-
-pub fn lifetime_arg(lifetime: ast::Lifetime) -> ast::LifetimeArg {
-    ast_from_text(&format!("const S: T<{lifetime}> = ();"))
-}
-
-pub(crate) fn generic_arg_list(
-    args: impl IntoIterator<Item = ast::GenericArg>,
-) -> ast::GenericArgList {
-    let args = args.into_iter().join(", ");
-    ast_from_text(&format!("const S: T<{args}> = ();"))
-}
-
 pub fn visibility_pub_crate() -> ast::Visibility {
     ast_from_text("pub(crate) struct S")
 }
diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml
index 14816912b7..95d44e9b9d 100644
--- a/xtask/Cargo.toml
+++ b/xtask/Cargo.toml
@@ -11,5 +11,5 @@ anyhow = "1.0.57"
 flate2 = "1.0.24"
 write-json = "0.1.2"
 xshell = "0.2.2"
-xflags = "0.3.0"
+xflags = "0.2.4"
 # Avoid adding more dependencies to this crate
diff --git a/xtask/src/flags.rs b/xtask/src/flags.rs
index 0fce488983..993c64ccea 100644
--- a/xtask/src/flags.rs
+++ b/xtask/src/flags.rs
@@ -7,6 +7,10 @@ xflags::xflags! {
 
     /// Run custom build command.
     cmd xtask {
+        default cmd help {
+            /// Print help information.
+            optional -h, --help
+        }
 
         /// Install rust-analyzer server or editor plugin.
         cmd install {
@@ -38,9 +42,9 @@ xflags::xflags! {
             optional --dry-run
         }
         /// Builds a benchmark version of rust-analyzer and puts it into `./target`.
-        cmd bb {
+        cmd bb
             required suffix: String
-        }
+        {}
     }
 }
 
@@ -54,6 +58,7 @@ pub struct Xtask {
 
 #[derive(Debug)]
 pub enum XtaskCmd {
+    Help(Help),
     Install(Install),
     FuzzTests(FuzzTests),
     Release(Release),
@@ -63,6 +68,11 @@ pub enum XtaskCmd {
     Bb(Bb),
 }
 
+#[derive(Debug)]
+pub struct Help {
+    pub help: bool,
+}
+
 #[derive(Debug)]
 pub struct Install {
     pub client: bool,
@@ -101,10 +111,7 @@ pub struct Bb {
 }
 
 impl Xtask {
-    #[allow(dead_code)]
-    pub fn from_env_or_exit() -> Self {
-        Self::from_env_or_exit_()
-    }
+    pub const HELP: &'static str = Self::HELP_;
 
     #[allow(dead_code)]
     pub fn from_env() -> xflags::Result<Self> {
diff --git a/xtask/src/main.rs b/xtask/src/main.rs
index a37f469adc..335ac324a5 100644
--- a/xtask/src/main.rs
+++ b/xtask/src/main.rs
@@ -25,12 +25,15 @@ use std::{
 use xshell::{cmd, Shell};
 
 fn main() -> anyhow::Result<()> {
-    let flags = flags::Xtask::from_env_or_exit();
-
     let sh = &Shell::new()?;
     sh.change_dir(project_root());
 
+    let flags = flags::Xtask::from_env()?;
     match flags.subcommand {
+        flags::XtaskCmd::Help(_) => {
+            println!("{}", flags::Xtask::HELP);
+            Ok(())
+        }
         flags::XtaskCmd::Install(cmd) => cmd.run(sh),
         flags::XtaskCmd::FuzzTests(_) => run_fuzzer(sh),
         flags::XtaskCmd::Release(cmd) => cmd.run(sh),


Test patch:
diff --git a/crates/hir-ty/src/tests/patterns.rs b/crates/hir-ty/src/tests/patterns.rs
index eb04bf8778..74de33117e 100644
--- a/crates/hir-ty/src/tests/patterns.rs
+++ b/crates/hir-ty/src/tests/patterns.rs
@@ -1070,3 +1070,13 @@ fn main() {
         "#,
     );
 }
+
+#[test]
+fn cfg_params() {
+    check_types(
+        r#"
+fn my_fn(#[cfg(feature = "feature")] u8: u8, u32: u32) {}
+                                           //^^^ u32
+"#,
+    );
+}