Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #2027 from rust-lang/rustc-pull
Rustc pull update
Sayantan Chakraborty 2 months ago
parent 92652dd · parent 9436677 · commit ee892d6
-rw-r--r--Cargo.lock30
-rw-r--r--Cargo.toml6
-rw-r--r--bench_data/glorious_old_parser18
-rw-r--r--crates/base-db/src/editioned_file_id.rs2
-rw-r--r--crates/edition/src/lib.rs2
-rw-r--r--crates/hir-def/src/expr_store/lower.rs82
-rw-r--r--crates/hir-def/src/item_scope.rs18
-rw-r--r--crates/hir-def/src/lang_item.rs1
-rw-r--r--crates/hir-def/src/nameres/collector.rs77
-rw-r--r--crates/hir-expand/src/builtin/fn_macro.rs19
-rw-r--r--crates/hir-ty/src/builtin_derive.rs2
-rw-r--r--crates/hir-ty/src/display.rs199
-rw-r--r--crates/hir-ty/src/lower.rs123
-rw-r--r--crates/hir-ty/src/tests/coercion.rs2
-rw-r--r--crates/hir-ty/src/tests/display_source_code.rs4
-rw-r--r--crates/hir-ty/src/tests/regression.rs43
-rw-r--r--crates/hir-ty/src/tests/simple.rs20
-rw-r--r--crates/hir-ty/src/tests/traits.rs10
-rw-r--r--crates/hir-ty/src/utils.rs25
-rw-r--r--crates/hir/src/display.rs2
-rw-r--r--crates/hir/src/lib.rs4
-rw-r--r--crates/hir/src/term_search/tactics.rs19
-rw-r--r--crates/ide-assists/src/handlers/auto_import.rs1
-rw-r--r--crates/ide-assists/src/handlers/convert_closure_to_fn.rs2
-rw-r--r--crates/ide-assists/src/handlers/extract_function.rs2
-rw-r--r--crates/ide-assists/src/handlers/generate_function.rs31
-rw-r--r--crates/ide-assists/src/handlers/generate_getter_or_setter.rs6
-rw-r--r--crates/ide-assists/src/handlers/move_guard.rs163
-rw-r--r--crates/ide-assists/src/utils.rs40
-rw-r--r--crates/ide-completion/src/completions/postfix.rs53
-rw-r--r--crates/ide-db/src/imports/insert_use.rs9
-rw-r--r--crates/ide-db/src/syntax_helpers/node_ext.rs6
-rw-r--r--crates/ide-db/src/syntax_helpers/suggest_name.rs50
-rw-r--r--crates/ide/src/goto_definition.rs6
-rw-r--r--crates/ide/src/highlight_related.rs2
-rw-r--r--crates/ide/src/hover/render.rs2
-rw-r--r--crates/ide/src/inlay_hints/bind_pat.rs17
-rw-r--r--crates/ide/src/lib.rs6
-rw-r--r--crates/ide/src/signature_help.rs4
-rw-r--r--crates/ide/src/syntax_highlighting.rs19
-rw-r--r--crates/ide/src/syntax_highlighting/highlight.rs40
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html12
-rw-r--r--crates/ide/src/typing.rs18
-rw-r--r--crates/intern/src/symbol/symbols.rs2
-rw-r--r--crates/parser/src/grammar/expressions/atom.rs6
-rw-r--r--crates/parser/src/syntax_kind/generated.rs8
-rw-r--r--crates/parser/test_data/parser/err/0042_weird_blocks.rast3
-rw-r--r--crates/parser/test_data/parser/inline/ok/try_block_expr.rast37
-rw-r--r--crates/parser/test_data/parser/inline/ok/try_block_expr.rs1
-rw-r--r--crates/proc-macro-srv/src/dylib/proc_macros.rs6
-rw-r--r--crates/project-model/src/sysroot.rs6
-rw-r--r--crates/project-model/src/workspace.rs2
-rw-r--r--crates/rust-analyzer/src/flycheck.rs288
-rw-r--r--crates/rust-analyzer/src/global_state.rs8
-rw-r--r--crates/rust-analyzer/src/handlers/dispatch.rs17
-rw-r--r--crates/span/src/hygiene.rs51
-rw-r--r--crates/syntax/rust.ungram5
-rw-r--r--crates/syntax/src/ast/expr_ext.rs14
-rw-r--r--crates/syntax/src/ast/generated/nodes.rs54
-rw-r--r--crates/syntax/src/syntax_error.rs10
-rw-r--r--crates/test-utils/src/minicore.rs32
-rw-r--r--crates/toolchain/src/lib.rs3
-rw-r--r--docs/book/README.md2
-rw-r--r--docs/book/src/contributing/README.md11
-rw-r--r--docs/book/src/contributing/debugging.md2
-rw-r--r--docs/book/src/installation.md4
-rw-r--r--docs/book/src/rust_analyzer_binary.md26
-rw-r--r--docs/book/src/troubleshooting.md10
-rw-r--r--docs/book/src/vs_code.md15
-rw-r--r--xtask/src/codegen/grammar/ast_src.rs2
70 files changed, 1240 insertions, 582 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 2cf3e37a43..755ae55eea 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1614,9 +1614,9 @@ dependencies = [
[[package]]
name = "num-conv"
-version = "0.1.0"
+version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
+checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050"
[[package]]
name = "num-traits"
@@ -2453,9 +2453,9 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "salsa"
-version = "0.25.2"
+version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2e2aa2fca57727371eeafc975acc8e6f4c52f8166a78035543f6ee1c74c2dcc"
+checksum = "f77debccd43ba198e9cee23efd7f10330ff445e46a98a2b107fed9094a1ee676"
dependencies = [
"boxcar",
"crossbeam-queue",
@@ -2478,15 +2478,15 @@ dependencies = [
[[package]]
name = "salsa-macro-rules"
-version = "0.25.2"
+version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1bfc2a1e7bf06964105515451d728f2422dedc3a112383324a00b191a5c397a3"
+checksum = "ea07adbf42d91cc076b7daf3b38bc8168c19eb362c665964118a89bc55ef19a5"
[[package]]
name = "salsa-macros"
-version = "0.25.2"
+version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d844c1aa34946da46af683b5c27ec1088a3d9d84a2b837a108223fd830220e1"
+checksum = "d16d4d8b66451b9c75ddf740b7fc8399bc7b8ba33e854a5d7526d18708f67b05"
dependencies = [
"proc-macro2",
"quote",
@@ -2914,9 +2914,9 @@ dependencies = [
[[package]]
name = "time"
-version = "0.3.44"
+version = "0.3.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d"
+checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c"
dependencies = [
"deranged",
"itoa",
@@ -2924,22 +2924,22 @@ dependencies = [
"num-conv",
"num_threads",
"powerfmt",
- "serde",
+ "serde_core",
"time-core",
"time-macros",
]
[[package]]
name = "time-core"
-version = "0.1.6"
+version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b"
+checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
[[package]]
name = "time-macros"
-version = "0.2.24"
+version = "0.2.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3"
+checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215"
dependencies = [
"num-conv",
"time-core",
diff --git a/Cargo.toml b/Cargo.toml
index 2288933a96..04559f15ed 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -135,13 +135,13 @@ rayon = "1.10.0"
rowan = "=0.15.17"
# Ideally we'd not enable the macros feature but unfortunately the `tracked` attribute does not work
# on impls without it
-salsa = { version = "0.25.2", default-features = false, features = [
+salsa = { version = "0.26", default-features = false, features = [
"rayon",
"salsa_unstable",
"macros",
"inventory",
] }
-salsa-macros = "0.25.2"
+salsa-macros = "0.26"
semver = "1.0.26"
serde = { version = "1.0.219" }
serde_derive = { version = "1.0.219" }
@@ -192,8 +192,6 @@ unused_lifetimes = "warn"
unreachable_pub = "warn"
[workspace.lints.clippy]
-# FIXME Remove the tidy test once the lint table is stable
-
## lint groups
complexity = { level = "warn", priority = -1 }
correctness = { level = "deny", priority = -1 }
diff --git a/bench_data/glorious_old_parser b/bench_data/glorious_old_parser
index f593f2b295..8136daa832 100644
--- a/bench_data/glorious_old_parser
+++ b/bench_data/glorious_old_parser
@@ -724,7 +724,7 @@ impl<'a> Parser<'a> {
// {foo(bar {}}
// - ^
// | |
- // | help: `)` may belong here (FIXME: #58270)
+ // | help: `)` may belong here
// |
// unclosed delimiter
if let Some(sp) = unmatched.unclosed_span {
@@ -3217,7 +3217,6 @@ impl<'a> Parser<'a> {
}
_ => {
- // FIXME Could factor this out into non_fatal_unexpected or something.
let actual = self.this_token_to_string();
self.span_err(self.span, &format!("unexpected token: `{}`", actual));
}
@@ -5250,7 +5249,6 @@ impl<'a> Parser<'a> {
}
}
} else {
- // FIXME: Bad copy of attrs
let old_directory_ownership =
mem::replace(&mut self.directory.ownership, DirectoryOwnership::UnownedViaBlock);
let item = self.parse_item_(attrs.clone(), false, true)?;
@@ -5953,23 +5951,14 @@ impl<'a> Parser<'a> {
});
assoc_ty_bindings.push(span);
} else if self.check_const_arg() {
- // FIXME(const_generics): to distinguish between idents for types and consts,
- // we should introduce a GenericArg::Ident in the AST and distinguish when
- // lowering to the HIR. For now, idents for const args are not permitted.
-
// Parse const argument.
let expr = if let token::OpenDelim(token::Brace) = self.token {
self.parse_block_expr(None, self.span, BlockCheckMode::Default, ThinVec::new())?
} else if self.token.is_ident() {
- // FIXME(const_generics): to distinguish between idents for types and consts,
- // we should introduce a GenericArg::Ident in the AST and distinguish when
- // lowering to the HIR. For now, idents for const args are not permitted.
return Err(
self.fatal("identifiers may currently not be used for const generics")
);
} else {
- // FIXME(const_generics): this currently conflicts with emplacement syntax
- // with negative integer literals.
self.parse_literal_maybe_minus()?
};
let value = AnonConst {
@@ -5991,9 +5980,6 @@ impl<'a> Parser<'a> {
}
}
- // FIXME: we would like to report this in ast_validation instead, but we currently do not
- // preserve ordering of generic parameters with respect to associated type binding, so we
- // lose that information after parsing.
if misplaced_assoc_ty_bindings.len() > 0 {
let mut err = self.struct_span_err(
args_lo.to(self.prev_span),
@@ -6079,8 +6065,6 @@ impl<'a> Parser<'a> {
bounds,
}
));
- // FIXME: Decide what should be used here, `=` or `==`.
- // FIXME: We are just dropping the binders in lifetime_defs on the floor here.
} else if self.eat(&token::Eq) || self.eat(&token::EqEq) {
let rhs_ty = self.parse_ty()?;
where_clause.predicates.push(ast::WherePredicate::EqPredicate(
diff --git a/crates/base-db/src/editioned_file_id.rs b/crates/base-db/src/editioned_file_id.rs
index 13fb05d565..dd419f48fc 100644
--- a/crates/base-db/src/editioned_file_id.rs
+++ b/crates/base-db/src/editioned_file_id.rs
@@ -60,7 +60,7 @@ const _: () = {
}
}
- impl zalsa_struct_::HashEqLike<WithoutCrate> for EditionedFileIdData {
+ impl zalsa_::HashEqLike<WithoutCrate> for EditionedFileIdData {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
Hash::hash(self, state);
diff --git a/crates/edition/src/lib.rs b/crates/edition/src/lib.rs
index f1a1fe5964..eb4cec39dc 100644
--- a/crates/edition/src/lib.rs
+++ b/crates/edition/src/lib.rs
@@ -16,8 +16,6 @@ impl Edition {
pub const DEFAULT: Edition = Edition::Edition2015;
pub const LATEST: Edition = Edition::Edition2024;
pub const CURRENT: Edition = Edition::Edition2024;
- /// The current latest stable edition, note this is usually not the right choice in code.
- pub const CURRENT_FIXME: Edition = Edition::Edition2024;
pub fn from_u32(u32: u32) -> Edition {
match u32 {
diff --git a/crates/hir-def/src/expr_store/lower.rs b/crates/hir-def/src/expr_store/lower.rs
index 4fbf6d9517..701586c258 100644
--- a/crates/hir-def/src/expr_store/lower.rs
+++ b/crates/hir-def/src/expr_store/lower.rs
@@ -426,7 +426,7 @@ pub struct ExprCollector<'db> {
/// and we need to find the current definition. So we track the number of definitions we saw.
current_block_legacy_macro_defs_count: FxHashMap<Name, usize>,
- current_try_block_label: Option<LabelId>,
+ current_try_block: Option<TryBlock>,
label_ribs: Vec<LabelRib>,
unowned_bindings: Vec<BindingId>,
@@ -472,6 +472,13 @@ enum Awaitable {
No(&'static str),
}
+enum TryBlock {
+ // `try { ... }`
+ Homogeneous { label: LabelId },
+ // `try bikeshed Ty { ... }`
+ Heterogeneous { label: LabelId },
+}
+
#[derive(Debug, Default)]
struct BindingList {
map: FxHashMap<(Name, HygieneId), BindingId>,
@@ -532,7 +539,7 @@ impl<'db> ExprCollector<'db> {
lang_items: OnceCell::new(),
store: ExpressionStoreBuilder::default(),
expander,
- current_try_block_label: None,
+ current_try_block: None,
is_lowering_coroutine: false,
label_ribs: Vec::new(),
unowned_bindings: Vec::new(),
@@ -1069,7 +1076,9 @@ impl<'db> ExprCollector<'db> {
self.alloc_expr(Expr::Let { pat, expr }, syntax_ptr)
}
ast::Expr::BlockExpr(e) => match e.modifier() {
- Some(ast::BlockModifier::Try(_)) => self.desugar_try_block(e),
+ Some(ast::BlockModifier::Try { try_token: _, bikeshed_token: _, result_type }) => {
+ self.desugar_try_block(e, result_type)
+ }
Some(ast::BlockModifier::Unsafe(_)) => {
self.collect_block_(e, |id, statements, tail| Expr::Unsafe {
id,
@@ -1344,7 +1353,7 @@ impl<'db> ExprCollector<'db> {
.map(|it| this.lower_type_ref_disallow_impl_trait(it));
let prev_is_lowering_coroutine = mem::take(&mut this.is_lowering_coroutine);
- let prev_try_block_label = this.current_try_block_label.take();
+ let prev_try_block = this.current_try_block.take();
let awaitable = if e.async_token().is_some() {
Awaitable::Yes
@@ -1369,7 +1378,7 @@ impl<'db> ExprCollector<'db> {
let capture_by =
if e.move_token().is_some() { CaptureBy::Value } else { CaptureBy::Ref };
this.is_lowering_coroutine = prev_is_lowering_coroutine;
- this.current_try_block_label = prev_try_block_label;
+ this.current_try_block = prev_try_block;
this.alloc_expr(
Expr::Closure {
args: args.into(),
@@ -1686,11 +1695,15 @@ impl<'db> ExprCollector<'db> {
/// Desugar `try { <stmts>; <expr> }` into `'<new_label>: { <stmts>; ::std::ops::Try::from_output(<expr>) }`,
/// `try { <stmts>; }` into `'<new_label>: { <stmts>; ::std::ops::Try::from_output(()) }`
/// and save the `<new_label>` to use it as a break target for desugaring of the `?` operator.
- fn desugar_try_block(&mut self, e: BlockExpr) -> ExprId {
+ fn desugar_try_block(&mut self, e: BlockExpr, result_type: Option<ast::Type>) -> ExprId {
let try_from_output = self.lang_path(self.lang_items().TryTraitFromOutput);
let label = self.generate_new_name();
let label = self.alloc_label_desugared(Label { name: label }, AstPtr::new(&e).wrap_right());
- let old_label = self.current_try_block_label.replace(label);
+ let try_block_info = match result_type {
+ Some(_) => TryBlock::Heterogeneous { label },
+ None => TryBlock::Homogeneous { label },
+ };
+ let old_try_block = self.current_try_block.replace(try_block_info);
let ptr = AstPtr::new(&e).upcast();
let (btail, expr_id) = self.with_labeled_rib(label, HygieneId::ROOT, |this| {
@@ -1720,8 +1733,38 @@ impl<'db> ExprCollector<'db> {
unreachable!("block was lowered to non-block");
};
*tail = Some(next_tail);
- self.current_try_block_label = old_label;
- expr_id
+ self.current_try_block = old_try_block;
+ match result_type {
+ Some(ty) => {
+ // `{ let <name>: <ty> = <expr>; <name> }`
+ let name = self.generate_new_name();
+ let type_ref = self.lower_type_ref_disallow_impl_trait(ty);
+ let binding = self.alloc_binding(
+ name.clone(),
+ BindingAnnotation::Unannotated,
+ HygieneId::ROOT,
+ );
+ let pat = self.alloc_pat_desugared(Pat::Bind { id: binding, subpat: None });
+ self.add_definition_to_binding(binding, pat);
+ let tail_expr =
+ self.alloc_expr_desugared_with_ptr(Expr::Path(Path::from(name)), ptr);
+ self.alloc_expr_desugared_with_ptr(
+ Expr::Block {
+ id: None,
+ statements: Box::new([Statement::Let {
+ pat,
+ type_ref: Some(type_ref),
+ initializer: Some(expr_id),
+ else_branch: None,
+ }]),
+ tail: Some(tail_expr),
+ label: None,
+ },
+ ptr,
+ )
+ }
+ None => expr_id,
+ }
}
/// Desugar `ast::WhileExpr` from: `[opt_ident]: while <cond> <body>` into:
@@ -1863,6 +1906,8 @@ impl<'db> ExprCollector<'db> {
/// ControlFlow::Continue(val) => val,
/// ControlFlow::Break(residual) =>
/// // If there is an enclosing `try {...}`:
+ /// break 'catch_target Residual::into_try_type(residual),
+ /// // If there is an enclosing `try bikeshed Ty {...}`:
/// break 'catch_target Try::from_residual(residual),
/// // Otherwise:
/// return Try::from_residual(residual),
@@ -1873,7 +1918,6 @@ impl<'db> ExprCollector<'db> {
let try_branch = self.lang_path(lang_items.TryTraitBranch);
let cf_continue = self.lang_path(lang_items.ControlFlowContinue);
let cf_break = self.lang_path(lang_items.ControlFlowBreak);
- let try_from_residual = self.lang_path(lang_items.TryTraitFromResidual);
let operand = self.collect_expr_opt(e.expr());
let try_branch = self.alloc_expr(try_branch.map_or(Expr::Missing, Expr::Path), syntax_ptr);
let expr = self
@@ -1910,13 +1954,23 @@ impl<'db> ExprCollector<'db> {
guard: None,
expr: {
let it = self.alloc_expr(Expr::Path(Path::from(break_name)), syntax_ptr);
- let callee = self
- .alloc_expr(try_from_residual.map_or(Expr::Missing, Expr::Path), syntax_ptr);
+ let convert_fn = match self.current_try_block {
+ Some(TryBlock::Homogeneous { .. }) => {
+ self.lang_path(lang_items.ResidualIntoTryType)
+ }
+ Some(TryBlock::Heterogeneous { .. }) | None => {
+ self.lang_path(lang_items.TryTraitFromResidual)
+ }
+ };
+ let callee =
+ self.alloc_expr(convert_fn.map_or(Expr::Missing, Expr::Path), syntax_ptr);
let result =
self.alloc_expr(Expr::Call { callee, args: Box::new([it]) }, syntax_ptr);
self.alloc_expr(
- match self.current_try_block_label {
- Some(label) => Expr::Break { expr: Some(result), label: Some(label) },
+ match self.current_try_block {
+ Some(
+ TryBlock::Heterogeneous { label } | TryBlock::Homogeneous { label },
+ ) => Expr::Break { expr: Some(result), label: Some(label) },
None => Expr::Return { expr: Some(result) },
},
syntax_ptr,
diff --git a/crates/hir-def/src/item_scope.rs b/crates/hir-def/src/item_scope.rs
index 9e1efb9777..1303773b59 100644
--- a/crates/hir-def/src/item_scope.rs
+++ b/crates/hir-def/src/item_scope.rs
@@ -893,6 +893,24 @@ impl ItemScope {
self.macros.get_mut(name).expect("tried to update visibility of non-existent macro");
res.vis = vis;
}
+
+ pub(crate) fn update_def_types(&mut self, name: &Name, def: ModuleDefId, vis: Visibility) {
+ let res = self.types.get_mut(name).expect("tried to update def of non-existent type");
+ res.def = def;
+ res.vis = vis;
+ }
+
+ pub(crate) fn update_def_values(&mut self, name: &Name, def: ModuleDefId, vis: Visibility) {
+ let res = self.values.get_mut(name).expect("tried to update def of non-existent value");
+ res.def = def;
+ res.vis = vis;
+ }
+
+ pub(crate) fn update_def_macros(&mut self, name: &Name, def: MacroId, vis: Visibility) {
+ let res = self.macros.get_mut(name).expect("tried to update def of non-existent macro");
+ res.def = def;
+ res.vis = vis;
+ }
}
impl PerNs {
diff --git a/crates/hir-def/src/lang_item.rs b/crates/hir-def/src/lang_item.rs
index 51dd55301f..fef92c89b1 100644
--- a/crates/hir-def/src/lang_item.rs
+++ b/crates/hir-def/src/lang_item.rs
@@ -456,6 +456,7 @@ language_item_table! { LangItems =>
TryTraitFromOutput, sym::from_output, FunctionId;
TryTraitBranch, sym::branch, FunctionId;
TryTraitFromYeet, sym::from_yeet, FunctionId;
+ ResidualIntoTryType, sym::into_try_type, FunctionId;
PointerLike, sym::pointer_like, TraitId;
diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs
index 323060f61d..f51524c1b5 100644
--- a/crates/hir-def/src/nameres/collector.rs
+++ b/crates/hir-def/src/nameres/collector.rs
@@ -1209,42 +1209,69 @@ impl<'db> DefCollector<'db> {
// `ItemScope::push_res_with_import()`.
if let Some(def) = defs.types
&& let Some(prev_def) = prev_defs.types
- && def.def == prev_def.def
- && self.from_glob_import.contains_type(module_id, name.clone())
- && def.vis != prev_def.vis
- && def.vis.max(self.db, prev_def.vis, &self.def_map) == Some(def.vis)
{
- changed = true;
- // This import is being handled here, don't pass it down to
- // `ItemScope::push_res_with_import()`.
- defs.types = None;
- self.def_map.modules[module_id].scope.update_visibility_types(name, def.vis);
+ if def.def == prev_def.def
+ && self.from_glob_import.contains_type(module_id, name.clone())
+ && def.vis != prev_def.vis
+ && def.vis.max(self.db, prev_def.vis, &self.def_map) == Some(def.vis)
+ {
+ changed = true;
+ // This import is being handled here, don't pass it down to
+ // `ItemScope::push_res_with_import()`.
+ defs.types = None;
+ self.def_map.modules[module_id].scope.update_visibility_types(name, def.vis);
+ }
+ // When the source module's definition changed (e.g., due to an explicit import
+ // shadowing a glob), propagate the new definition to modules that glob-import from it.
+ // We check that the previous definition came from the same glob import to avoid
+ // incorrectly overwriting definitions from different glob sources.
+ //
+ // Note this is not a perfect fix, but it makes
+ // https://github.com/rust-lang/rust-analyzer/issues/19224 work for now until we
+ // implement a proper glob graph
+ else if def.def != prev_def.def && prev_def.import == def_import_type {
+ changed = true;
+ defs.types = None;
+ self.def_map.modules[module_id].scope.update_def_types(name, def.def, def.vis);
+ }
}
if let Some(def) = defs.values
&& let Some(prev_def) = prev_defs.values
- && def.def == prev_def.def
- && self.from_glob_import.contains_value(module_id, name.clone())
- && def.vis != prev_def.vis
- && def.vis.max(self.db, prev_def.vis, &self.def_map) == Some(def.vis)
{
- changed = true;
- // See comment above.
- defs.values = None;
- self.def_map.modules[module_id].scope.update_visibility_values(name, def.vis);
+ if def.def == prev_def.def
+ && self.from_glob_import.contains_value(module_id, name.clone())
+ && def.vis != prev_def.vis
+ && def.vis.max(self.db, prev_def.vis, &self.def_map) == Some(def.vis)
+ {
+ changed = true;
+ defs.values = None;
+ self.def_map.modules[module_id].scope.update_visibility_values(name, def.vis);
+ } else if def.def != prev_def.def
+ && prev_def.import.map(ImportOrExternCrate::from) == def_import_type
+ {
+ changed = true;
+ defs.values = None;
+ self.def_map.modules[module_id].scope.update_def_values(name, def.def, def.vis);
+ }
}
if let Some(def) = defs.macros
&& let Some(prev_def) = prev_defs.macros
- && def.def == prev_def.def
- && self.from_glob_import.contains_macro(module_id, name.clone())
- && def.vis != prev_def.vis
- && def.vis.max(self.db, prev_def.vis, &self.def_map) == Some(def.vis)
{
- changed = true;
- // See comment above.
- defs.macros = None;
- self.def_map.modules[module_id].scope.update_visibility_macros(name, def.vis);
+ if def.def == prev_def.def
+ && self.from_glob_import.contains_macro(module_id, name.clone())
+ && def.vis != prev_def.vis
+ && def.vis.max(self.db, prev_def.vis, &self.def_map) == Some(def.vis)
+ {
+ changed = true;
+ defs.macros = None;
+ self.def_map.modules[module_id].scope.update_visibility_macros(name, def.vis);
+ } else if def.def != prev_def.def && prev_def.import == def_import_type {
+ changed = true;
+ defs.macros = None;
+ self.def_map.modules[module_id].scope.update_def_macros(name, def.def, def.vis);
+ }
}
}
diff --git a/crates/hir-expand/src/builtin/fn_macro.rs b/crates/hir-expand/src/builtin/fn_macro.rs
index 6e4b96b050..3029bca1d5 100644
--- a/crates/hir-expand/src/builtin/fn_macro.rs
+++ b/crates/hir-expand/src/builtin/fn_macro.rs
@@ -830,15 +830,18 @@ fn include_bytes_expand(
span: Span,
) -> ExpandResult<tt::TopSubtree> {
// FIXME: actually read the file here if the user asked for macro expansion
- let res = tt::TopSubtree::invisible_from_leaves(
+ let underscore = sym::underscore;
+ let zero = tt::Literal {
+ text_and_suffix: sym::_0_u8,
span,
- [tt::Leaf::Literal(tt::Literal {
- text_and_suffix: Symbol::empty(),
- span,
- kind: tt::LitKind::ByteStrRaw(1),
- suffix_len: 0,
- })],
- );
+ kind: tt::LitKind::Integer,
+ suffix_len: 3,
+ };
+ // We don't use a real length since we can't know the file length, so we use an underscore
+ // to infer it.
+ let res = quote! {span =>
+ &[#zero; #underscore]
+ };
ExpandResult::ok(res)
}
diff --git a/crates/hir-ty/src/builtin_derive.rs b/crates/hir-ty/src/builtin_derive.rs
index f3e67d01e5..0c3c513668 100644
--- a/crates/hir-ty/src/builtin_derive.rs
+++ b/crates/hir-ty/src/builtin_derive.rs
@@ -133,7 +133,7 @@ pub fn impl_trait<'db>(
}
}
-#[salsa::tracked(returns(ref), unsafe(non_update_types))]
+#[salsa::tracked(returns(ref))]
pub fn predicates<'db>(db: &'db dyn HirDatabase, impl_: BuiltinDeriveImplId) -> GenericPredicates {
let loc = impl_.loc(db);
let generic_params = GenericParams::new(db, loc.adt.into());
diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs
index 43b428c3fa..4e77e8be36 100644
--- a/crates/hir-ty/src/display.rs
+++ b/crates/hir-ty/src/display.rs
@@ -12,7 +12,6 @@ use either::Either;
use hir_def::{
FindPathConfig, GenericDefId, GenericParamId, HasModule, LocalFieldId, Lookup, ModuleDefId,
ModuleId, TraitId,
- db::DefDatabase,
expr_store::{ExpressionStore, path::Path},
find_path::{self, PrefixKind},
hir::generics::{TypeOrConstParamData, TypeParamProvenance, WherePredicate},
@@ -100,6 +99,9 @@ pub struct HirFormatter<'a, 'db> {
display_kind: DisplayKind,
display_target: DisplayTarget,
bounds_formatting_ctx: BoundsFormattingCtx<'db>,
+ /// Whether formatting `impl Trait1 + Trait2` or `dyn Trait1 + Trait2` needs parentheses around it,
+ /// for example when formatting `&(impl Trait1 + Trait2)`.
+ trait_bounds_need_parens: bool,
}
// FIXME: To consider, ref and dyn trait lifetimes can be omitted if they are `'_`, path args should
@@ -331,6 +333,7 @@ pub trait HirDisplay<'db> {
show_container_bounds: false,
display_lifetimes: DisplayLifetime::OnlyNamedOrStatic,
bounds_formatting_ctx: Default::default(),
+ trait_bounds_need_parens: false,
}) {
Ok(()) => {}
Err(HirDisplayError::FmtError) => panic!("Writing to String can't fail!"),
@@ -566,6 +569,7 @@ impl<'db, T: HirDisplay<'db>> HirDisplayWrapper<'_, 'db, T> {
show_container_bounds: self.show_container_bounds,
display_lifetimes: self.display_lifetimes,
bounds_formatting_ctx: Default::default(),
+ trait_bounds_need_parens: false,
})
}
@@ -612,7 +616,11 @@ impl<'db, T: HirDisplay<'db> + Internable> HirDisplay<'db> for Interned<T> {
}
}
-fn write_projection<'db>(f: &mut HirFormatter<'_, 'db>, alias: &AliasTy<'db>) -> Result {
+fn write_projection<'db>(
+ f: &mut HirFormatter<'_, 'db>,
+ alias: &AliasTy<'db>,
+ needs_parens_if_multi: bool,
+) -> Result {
if f.should_truncate() {
return write!(f, "{TYPE_HINT_TRUNCATION}");
}
@@ -650,6 +658,7 @@ fn write_projection<'db>(f: &mut HirFormatter<'_, 'db>, alias: &AliasTy<'db>) ->
Either::Left(Ty::new_alias(f.interner, AliasTyKind::Projection, *alias)),
&bounds,
SizedByDefault::NotSized,
+ needs_parens_if_multi,
)
});
}
@@ -1056,7 +1065,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
return write!(f, "{TYPE_HINT_TRUNCATION}");
}
- use TyKind;
+ let trait_bounds_need_parens = mem::replace(&mut f.trait_bounds_need_parens, false);
match self.kind() {
TyKind::Never => write!(f, "!")?,
TyKind::Str => write!(f, "str")?,
@@ -1077,103 +1086,34 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
c.hir_fmt(f)?;
write!(f, "]")?;
}
- kind @ (TyKind::RawPtr(t, m) | TyKind::Ref(_, t, m)) => {
- if let TyKind::Ref(l, _, _) = kind {
- f.write_char('&')?;
- if f.render_region(l) {
- l.hir_fmt(f)?;
- f.write_char(' ')?;
- }
- match m {
- rustc_ast_ir::Mutability::Not => (),
- rustc_ast_ir::Mutability::Mut => f.write_str("mut ")?,
- }
- } else {
- write!(
- f,
- "*{}",
- match m {
- rustc_ast_ir::Mutability::Not => "const ",
- rustc_ast_ir::Mutability::Mut => "mut ",
- }
- )?;
+ TyKind::Ref(l, t, m) => {
+ f.write_char('&')?;
+ if f.render_region(l) {
+ l.hir_fmt(f)?;
+ f.write_char(' ')?;
+ }
+ match m {
+ rustc_ast_ir::Mutability::Not => (),
+ rustc_ast_ir::Mutability::Mut => f.write_str("mut ")?,
}
- // FIXME: all this just to decide whether to use parentheses...
- let (preds_to_print, has_impl_fn_pred) = match t.kind() {
- TyKind::Dynamic(bounds, region) => {
- let contains_impl_fn =
- bounds.iter().any(|bound| match bound.skip_binder() {
- ExistentialPredicate::Trait(trait_ref) => {
- let trait_ = trait_ref.def_id.0;
- fn_traits(f.lang_items()).any(|it| it == trait_)
- }
- _ => false,
- });
- let render_lifetime = f.render_region(region);
- (bounds.len() + render_lifetime as usize, contains_impl_fn)
- }
- TyKind::Alias(AliasTyKind::Opaque, ty) => {
- let opaque_ty_id = match ty.def_id {
- SolverDefId::InternedOpaqueTyId(id) => id,
- _ => unreachable!(),
- };
- let impl_trait_id = db.lookup_intern_impl_trait_id(opaque_ty_id);
- if let ImplTraitId::ReturnTypeImplTrait(func, _) = impl_trait_id {
- let data = impl_trait_id.predicates(db);
- let bounds =
- || data.iter_instantiated_copied(f.interner, ty.args.as_slice());
- let mut len = bounds().count();
-
- // Don't count Sized but count when it absent
- // (i.e. when explicit ?Sized bound is set).
- let default_sized = SizedByDefault::Sized { anchor: func.krate(db) };
- let sized_bounds = bounds()
- .filter(|b| {
- matches!(
- b.kind().skip_binder(),
- ClauseKind::Trait(trait_ref)
- if default_sized.is_sized_trait(
- trait_ref.def_id().0,
- db,
- ),
- )
- })
- .count();
- match sized_bounds {
- 0 => len += 1,
- _ => {
- len = len.saturating_sub(sized_bounds);
- }
- }
-
- let contains_impl_fn = bounds().any(|bound| {
- if let ClauseKind::Trait(trait_ref) = bound.kind().skip_binder() {
- let trait_ = trait_ref.def_id().0;
- fn_traits(f.lang_items()).any(|it| it == trait_)
- } else {
- false
- }
- });
- (len, contains_impl_fn)
- } else {
- (0, false)
- }
+ f.trait_bounds_need_parens = true;
+ t.hir_fmt(f)?;
+ f.trait_bounds_need_parens = false;
+ }
+ TyKind::RawPtr(t, m) => {
+ write!(
+ f,
+ "*{}",
+ match m {
+ rustc_ast_ir::Mutability::Not => "const ",
+ rustc_ast_ir::Mutability::Mut => "mut ",
}
- _ => (0, false),
- };
-
- if has_impl_fn_pred && preds_to_print <= 2 {
- return t.hir_fmt(f);
- }
+ )?;
- if preds_to_print > 1 {
- write!(f, "(")?;
- t.hir_fmt(f)?;
- write!(f, ")")?;
- } else {
- t.hir_fmt(f)?;
- }
+ f.trait_bounds_need_parens = true;
+ t.hir_fmt(f)?;
+ f.trait_bounds_need_parens = false;
}
TyKind::Tuple(tys) => {
if tys.len() == 1 {
@@ -1328,7 +1268,9 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
hir_fmt_generics(f, parameters.as_slice(), Some(def.def_id().0.into()), None)?;
}
- TyKind::Alias(AliasTyKind::Projection, alias_ty) => write_projection(f, &alias_ty)?,
+ TyKind::Alias(AliasTyKind::Projection, alias_ty) => {
+ write_projection(f, &alias_ty, trait_bounds_need_parens)?
+ }
TyKind::Foreign(alias) => {
let type_alias = db.type_alias_signature(alias.0);
f.start_location_link(alias.0.into());
@@ -1363,6 +1305,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
Either::Left(*self),
&bounds,
SizedByDefault::Sized { anchor: krate },
+ trait_bounds_need_parens,
)?;
}
TyKind::Closure(id, substs) => {
@@ -1525,6 +1468,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
Either::Left(*self),
&bounds,
SizedByDefault::Sized { anchor: krate },
+ trait_bounds_need_parens,
)?;
}
},
@@ -1567,6 +1511,7 @@ impl<'db> HirDisplay<'db> for Ty<'db> {
Either::Left(*self),
&bounds_to_display,
SizedByDefault::NotSized,
+ trait_bounds_need_parens,
)?;
}
TyKind::Error(_) => {
@@ -1806,11 +1751,11 @@ pub enum SizedByDefault {
}
impl SizedByDefault {
- fn is_sized_trait(self, trait_: TraitId, db: &dyn DefDatabase) -> bool {
+ fn is_sized_trait(self, trait_: TraitId, interner: DbInterner<'_>) -> bool {
match self {
Self::NotSized => false,
- Self::Sized { anchor } => {
- let sized_trait = hir_def::lang_item::lang_items(db, anchor).Sized;
+ Self::Sized { .. } => {
+ let sized_trait = interner.lang_items().Sized;
Some(trait_) == sized_trait
}
}
@@ -1823,16 +1768,62 @@ pub fn write_bounds_like_dyn_trait_with_prefix<'db>(
this: Either<Ty<'db>, Region<'db>>,
predicates: &[Clause<'db>],
default_sized: SizedByDefault,
+ needs_parens_if_multi: bool,
) -> Result {
+ let needs_parens =
+ needs_parens_if_multi && trait_bounds_need_parens(f, this, predicates, default_sized);
+ if needs_parens {
+ write!(f, "(")?;
+ }
write!(f, "{prefix}")?;
if !predicates.is_empty()
|| predicates.is_empty() && matches!(default_sized, SizedByDefault::Sized { .. })
{
write!(f, " ")?;
- write_bounds_like_dyn_trait(f, this, predicates, default_sized)
- } else {
- Ok(())
+ write_bounds_like_dyn_trait(f, this, predicates, default_sized)?;
+ }
+ if needs_parens {
+ write!(f, ")")?;
+ }
+ Ok(())
+}
+
+fn trait_bounds_need_parens<'db>(
+ f: &mut HirFormatter<'_, 'db>,
+ this: Either<Ty<'db>, Region<'db>>,
+ predicates: &[Clause<'db>],
+ default_sized: SizedByDefault,
+) -> bool {
+ // This needs to be kept in sync with `write_bounds_like_dyn_trait()`.
+ let mut distinct_bounds = 0usize;
+ let mut is_sized = false;
+ for p in predicates {
+ match p.kind().skip_binder() {
+ ClauseKind::Trait(trait_ref) => {
+ let trait_ = trait_ref.def_id().0;
+ if default_sized.is_sized_trait(trait_, f.interner) {
+ is_sized = true;
+ if matches!(default_sized, SizedByDefault::Sized { .. }) {
+ // Don't print +Sized, but rather +?Sized if absent.
+ continue;
+ }
+ }
+
+ distinct_bounds += 1;
+ }
+ ClauseKind::TypeOutlives(to) if Either::Left(to.0) == this => distinct_bounds += 1,
+ ClauseKind::RegionOutlives(lo) if Either::Right(lo.0) == this => distinct_bounds += 1,
+ _ => {}
+ }
}
+
+ if let SizedByDefault::Sized { .. } = default_sized
+ && !is_sized
+ {
+ distinct_bounds += 1;
+ }
+
+ distinct_bounds > 1
}
fn write_bounds_like_dyn_trait<'db>(
@@ -1855,7 +1846,7 @@ fn write_bounds_like_dyn_trait<'db>(
match p.kind().skip_binder() {
ClauseKind::Trait(trait_ref) => {
let trait_ = trait_ref.def_id().0;
- if default_sized.is_sized_trait(trait_, f.db) {
+ if default_sized.is_sized_trait(trait_, f.interner) {
is_sized = true;
if matches!(default_sized, SizedByDefault::Sized { .. }) {
// Don't print +Sized, but rather +?Sized if absent.
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs
index 5789bf02a4..386556b156 100644
--- a/crates/hir-ty/src/lower.rs
+++ b/crates/hir-ty/src/lower.rs
@@ -831,6 +831,8 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> {
let mut ordered_associated_types = vec![];
if let Some(principal_trait) = principal {
+ // Generally we should not elaborate in lowering as this can lead to cycles, but
+ // here rustc cycles as well.
for clause in elaborate::elaborate(
interner,
[Clause::upcast_from(
@@ -1897,7 +1899,7 @@ impl<'db> GenericPredicates {
/// Resolve the where clause(s) of an item with generics.
///
/// Diagnostics are computed only for this item's predicates, not for parents.
- #[salsa::tracked(returns(ref))]
+ #[salsa::tracked(returns(ref), cycle_result=generic_predicates_cycle_result)]
pub fn query_with_diagnostics(
db: &'db dyn HirDatabase,
def: GenericDefId,
@@ -1906,6 +1908,20 @@ impl<'db> GenericPredicates {
}
}
+/// A cycle can occur from malformed code.
+fn generic_predicates_cycle_result(
+ _db: &dyn HirDatabase,
+ _: salsa::Id,
+ _def: GenericDefId,
+) -> (GenericPredicates, Diagnostics) {
+ (
+ GenericPredicates::from_explicit_own_predicates(StoredEarlyBinder::bind(
+ Clauses::default().store(),
+ )),
+ None,
+ )
+}
+
impl GenericPredicates {
#[inline]
pub(crate) fn from_explicit_own_predicates(
@@ -2590,11 +2606,13 @@ pub(crate) fn associated_type_by_name_including_super_traits<'db>(
) -> Option<(TraitRef<'db>, TypeAliasId)> {
let module = trait_ref.def_id.0.module(db);
let interner = DbInterner::new_with(db, module.krate(db));
- rustc_type_ir::elaborate::supertraits(interner, Binder::dummy(trait_ref)).find_map(|t| {
- let trait_id = t.as_ref().skip_binder().def_id.0;
- let assoc_type = trait_id.trait_items(db).associated_type_by_name(name)?;
- Some((t.skip_binder(), assoc_type))
- })
+ all_supertraits_trait_refs(db, trait_ref.def_id.0)
+ .map(|t| t.instantiate(interner, trait_ref.args))
+ .find_map(|t| {
+ let trait_id = t.def_id.0;
+ let assoc_type = trait_id.trait_items(db).associated_type_by_name(name)?;
+ Some((t, assoc_type))
+ })
}
pub fn associated_type_shorthand_candidates(
@@ -2723,3 +2741,96 @@ fn named_associated_type_shorthand_candidates<'db, R>(
_ => None,
}
}
+
+/// During lowering, elaborating supertraits can cause cycles. To avoid that, we have a separate query
+/// to only collect supertraits.
+///
+/// Technically, it is possible to avoid even more cycles by only collecting the `TraitId` of supertraits
+/// without their args. However rustc doesn't do that, so we don't either.
+pub(crate) fn all_supertraits_trait_refs(
+ db: &dyn HirDatabase,
+ trait_: TraitId,
+) -> impl ExactSizeIterator<Item = EarlyBinder<'_, TraitRef<'_>>> {
+ let interner = DbInterner::new_no_crate(db);
+ return all_supertraits_trait_refs_query(db, trait_).iter().map(move |trait_ref| {
+ trait_ref.get_with(|(trait_, args)| {
+ TraitRef::new_from_args(interner, (*trait_).into(), args.as_ref())
+ })
+ });
+
+ #[salsa_macros::tracked(returns(deref), cycle_result = all_supertraits_trait_refs_cycle_result)]
+ pub(crate) fn all_supertraits_trait_refs_query(
+ db: &dyn HirDatabase,
+ trait_: TraitId,
+ ) -> Box<[StoredEarlyBinder<(TraitId, StoredGenericArgs)>]> {
+ let resolver = trait_.resolver(db);
+ let signature = db.trait_signature(trait_);
+ let mut ctx = TyLoweringContext::new(
+ db,
+ &resolver,
+ &signature.store,
+ trait_.into(),
+ LifetimeElisionKind::AnonymousReportError,
+ );
+ let interner = ctx.interner;
+
+ let self_param_ty = Ty::new_param(
+ interner,
+ TypeParamId::from_unchecked(TypeOrConstParamId {
+ parent: trait_.into(),
+ local_id: Idx::from_raw(la_arena::RawIdx::from_u32(0)),
+ }),
+ 0,
+ );
+
+ let mut supertraits = FxHashSet::default();
+ supertraits.insert(StoredEarlyBinder::bind((
+ trait_,
+ GenericArgs::identity_for_item(interner, trait_.into()).store(),
+ )));
+
+ for pred in signature.generic_params.where_predicates() {
+ let WherePredicate::TypeBound { target, bound } = pred else {
+ continue;
+ };
+ let target = &signature.store[*target];
+ if let TypeRef::TypeParam(param_id) = target
+ && param_id.local_id().into_raw().into_u32() == 0
+ {
+ // This is `Self`.
+ } else if let TypeRef::Path(path) = target
+ && path.is_self_type()
+ {
+ // Also `Self`.
+ } else {
+ // Not `Self`!
+ continue;
+ }
+
+ ctx.lower_type_bound(bound, self_param_ty, true).for_each(|(clause, _)| {
+ if let ClauseKind::Trait(trait_ref) = clause.kind().skip_binder() {
+ supertraits.extend(
+ all_supertraits_trait_refs(db, trait_ref.trait_ref.def_id.0).map(|t| {
+ let trait_ref = t.instantiate(interner, trait_ref.trait_ref.args);
+ StoredEarlyBinder::bind((trait_ref.def_id.0, trait_ref.args.store()))
+ }),
+ );
+ }
+ });
+ }
+
+ Box::from_iter(supertraits)
+ }
+
+ pub(crate) fn all_supertraits_trait_refs_cycle_result(
+ db: &dyn HirDatabase,
+ _: salsa::Id,
+ trait_: TraitId,
+ ) -> Box<[StoredEarlyBinder<(TraitId, StoredGenericArgs)>]> {
+ let interner = DbInterner::new_no_crate(db);
+ Box::new([StoredEarlyBinder::bind((
+ trait_,
+ GenericArgs::identity_for_item(interner, trait_.into()).store(),
+ ))])
+ }
+}
diff --git a/crates/hir-ty/src/tests/coercion.rs b/crates/hir-ty/src/tests/coercion.rs
index 36630ab587..438699b409 100644
--- a/crates/hir-ty/src/tests/coercion.rs
+++ b/crates/hir-ty/src/tests/coercion.rs
@@ -608,7 +608,7 @@ trait Foo {}
fn test(f: impl Foo, g: &(impl Foo + ?Sized)) {
let _: &dyn Foo = &f;
let _: &dyn Foo = g;
- //^ expected &'? (dyn Foo + 'static), got &'? impl Foo + ?Sized
+ //^ expected &'? (dyn Foo + 'static), got &'? (impl Foo + ?Sized)
}
"#,
);
diff --git a/crates/hir-ty/src/tests/display_source_code.rs b/crates/hir-ty/src/tests/display_source_code.rs
index dc3869930d..37da7fc875 100644
--- a/crates/hir-ty/src/tests/display_source_code.rs
+++ b/crates/hir-ty/src/tests/display_source_code.rs
@@ -111,7 +111,7 @@ fn test(
b;
//^ impl Foo
c;
- //^ &impl Foo + ?Sized
+ //^ &(impl Foo + ?Sized)
d;
//^ S<impl Foo>
ref_any;
@@ -192,7 +192,7 @@ fn test(
b;
//^ fn(impl Foo) -> impl Foo
c;
-} //^ fn(&impl Foo + ?Sized) -> &impl Foo + ?Sized
+} //^ fn(&(impl Foo + ?Sized)) -> &(impl Foo + ?Sized)
"#,
);
}
diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs
index 4f1480c393..3b5b4e4fa5 100644
--- a/crates/hir-ty/src/tests/regression.rs
+++ b/crates/hir-ty/src/tests/regression.rs
@@ -2363,6 +2363,7 @@ fn test() {
}
"#,
expect![[r#"
+ 46..49 'Foo': Foo<N>
93..97 'self': Foo<N>
108..125 '{ ... }': usize
118..119 'N': usize
@@ -2645,3 +2646,45 @@ where
"#,
);
}
+
+#[test]
+fn issue_21560() {
+ check_no_mismatches(
+ r#"
+mod bindings {
+ use super::*;
+ pub type HRESULT = i32;
+}
+use bindings::*;
+
+
+mod error {
+ use super::*;
+ pub fn nonzero_hresult(hr: HRESULT) -> crate::HRESULT {
+ hr
+ }
+}
+pub use error::*;
+
+mod hresult {
+ use super::*;
+ pub struct HRESULT(pub i32);
+}
+pub use hresult::HRESULT;
+
+ "#,
+ );
+}
+
+#[test]
+fn regression_21577() {
+ check_no_mismatches(
+ r#"
+pub trait FilterT<F: FilterT<F, V = Self::V> = Self> {
+ type V;
+
+ fn foo() {}
+}
+ "#,
+ );
+}
diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs
index 98503452d3..7d4f04268a 100644
--- a/crates/hir-ty/src/tests/simple.rs
+++ b/crates/hir-ty/src/tests/simple.rs
@@ -2152,10 +2152,11 @@ async fn main() {
let z: core::ops::ControlFlow<(), _> = try { () };
let w = const { 92 };
let t = 'a: { 92 };
+ let u = try bikeshed core::ops::ControlFlow<(), _> { () };
}
"#,
expect![[r#"
- 16..193 '{ ...2 }; }': ()
+ 16..256 '{ ...) }; }': ()
26..27 'x': i32
30..43 'unsafe { 92 }': i32
39..41 '92': i32
@@ -2176,6 +2177,13 @@ async fn main() {
176..177 't': i32
180..190 ''a: { 92 }': i32
186..188 '92': i32
+ 200..201 'u': ControlFlow<(), ()>
+ 204..253 'try bi...{ () }': ControlFlow<(), ()>
+ 204..253 'try bi...{ () }': fn from_output<ControlFlow<(), ()>>(<ControlFlow<(), ()> as Try>::Output) -> ControlFlow<(), ()>
+ 204..253 'try bi...{ () }': ControlFlow<(), ()>
+ 204..253 'try bi...{ () }': ControlFlow<(), ()>
+ 204..253 'try bi...{ () }': ControlFlow<(), ()>
+ 249..251 '()': ()
"#]],
)
}
@@ -4056,3 +4064,13 @@ fn foo() {
"#]],
);
}
+
+#[test]
+fn include_bytes_len_mismatch() {
+ check_no_mismatches(
+ r#"
+//- minicore: include_bytes
+static S: &[u8; 158] = include_bytes!("/foo/bar/baz.txt");
+ "#,
+ );
+}
diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs
index 390553c0d7..cdf7b40003 100644
--- a/crates/hir-ty/src/tests/traits.rs
+++ b/crates/hir-ty/src/tests/traits.rs
@@ -219,14 +219,16 @@ fn test() {
#[test]
fn infer_try_block() {
- // FIXME: We should test more cases, but it currently doesn't work, since
- // our labeled block type inference is broken.
check_types(
r#"
-//- minicore: try, option
+//- minicore: try, option, result, from
fn test() {
let x: Option<_> = try { Some(2)?; };
//^ Option<()>
+ let homogeneous = try { Ok::<(), u32>(())?; "hi" };
+ //^^^^^^^^^^^ Result<&'? str, u32>
+ let heterogeneous = try bikeshed Result<_, u64> { 1 };
+ //^^^^^^^^^^^^^ Result<i32, u64>
}
"#,
);
@@ -4819,7 +4821,7 @@ fn allowed3(baz: impl Baz<Assoc = Qux<impl Foo>>) {}
431..433 '{}': ()
447..450 'baz': impl Baz<Assoc = impl Foo>
480..482 '{}': ()
- 500..503 'baz': impl Baz<Assoc = &'a impl Foo + 'a>
+ 500..503 'baz': impl Baz<Assoc = &'a (impl Foo + 'a)>
544..546 '{}': ()
560..563 'baz': impl Baz<Assoc = Qux<impl Foo>>
598..600 '{}': ()
diff --git a/crates/hir-ty/src/utils.rs b/crates/hir-ty/src/utils.rs
index 7dd73f1e7a..148300deb8 100644
--- a/crates/hir-ty/src/utils.rs
+++ b/crates/hir-ty/src/utils.rs
@@ -22,6 +22,7 @@ use crate::{
TargetFeatures,
db::HirDatabase,
layout::{Layout, TagEncoding},
+ lower::all_supertraits_trait_refs,
mir::pad16,
};
@@ -62,23 +63,13 @@ pub fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> SmallVec<[T
/// Returns an iterator over the whole super trait hierarchy (including the
/// trait itself).
-pub fn all_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> SmallVec<[TraitId; 4]> {
- // we need to take care a bit here to avoid infinite loops in case of cycles
- // (i.e. if we have `trait A: B; trait B: A;`)
-
- let mut result = smallvec![trait_];
- let mut i = 0;
- while let Some(&t) = result.get(i) {
- // yeah this is quadratic, but trait hierarchies should be flat
- // enough that this doesn't matter
- direct_super_traits_cb(db, t, |tt| {
- if !result.contains(&tt) {
- result.push(tt);
- }
- });
- i += 1;
- }
- result
+pub fn all_super_traits(db: &dyn HirDatabase, trait_: TraitId) -> SmallVec<[TraitId; 4]> {
+ let mut supertraits = all_supertraits_trait_refs(db, trait_)
+ .map(|trait_ref| trait_ref.skip_binder().def_id.0)
+ .collect::<SmallVec<[_; _]>>();
+ supertraits.sort_unstable();
+ supertraits.dedup();
+ supertraits
}
fn direct_super_traits_cb(db: &dyn DefDatabase, trait_: TraitId, cb: impl FnMut(TraitId)) {
diff --git a/crates/hir/src/display.rs b/crates/hir/src/display.rs
index 1f9af564c3..b4440dfa18 100644
--- a/crates/hir/src/display.rs
+++ b/crates/hir/src/display.rs
@@ -587,6 +587,7 @@ impl<'db> HirDisplay<'db> for TypeParam {
Either::Left(ty),
&predicates,
SizedByDefault::Sized { anchor: krate },
+ false,
);
}
},
@@ -614,6 +615,7 @@ impl<'db> HirDisplay<'db> for TypeParam {
Either::Left(ty),
&predicates,
default_sized,
+ false,
)?;
}
Ok(())
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 252d71fb80..4b61566516 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -4233,6 +4233,10 @@ impl Local {
self.parent(db).module(db)
}
+ pub fn as_id(self) -> u32 {
+ self.binding_id.into_raw().into_u32()
+ }
+
pub fn ty(self, db: &dyn HirDatabase) -> Type<'_> {
let def = self.parent;
let infer = InferenceResult::for_body(db, def);
diff --git a/crates/hir/src/term_search/tactics.rs b/crates/hir/src/term_search/tactics.rs
index 05a89e7652..8622aa1378 100644
--- a/crates/hir/src/term_search/tactics.rs
+++ b/crates/hir/src/term_search/tactics.rs
@@ -18,7 +18,6 @@ use hir_ty::{
use itertools::Itertools;
use rustc_hash::FxHashSet;
use rustc_type_ir::inherent::Ty as _;
-use span::Edition;
use crate::{
Adt, AssocItem, GenericDef, GenericParam, HasAttrs, HasVisibility, Impl, ModuleDef, ScopeDef,
@@ -367,7 +366,11 @@ pub(super) fn free_function<'a, 'lt, 'db, DB: HirDatabase>(
let ret_ty = it.ret_type_with_args(db, generics.iter().cloned());
// Filter out private and unsafe functions
if !it.is_visible_from(db, module)
- || it.is_unsafe_to_call(db, None, Edition::CURRENT_FIXME)
+ || it.is_unsafe_to_call(
+ db,
+ None,
+ crate::Crate::from(ctx.scope.resolver().krate()).edition(db),
+ )
|| it.is_unstable(db)
|| ctx.config.enable_borrowcheck && ret_ty.contains_reference(db)
|| ret_ty.is_raw_ptr()
@@ -473,7 +476,11 @@ pub(super) fn impl_method<'a, 'lt, 'db, DB: HirDatabase>(
// Filter out private and unsafe functions
if !it.is_visible_from(db, module)
- || it.is_unsafe_to_call(db, None, Edition::CURRENT_FIXME)
+ || it.is_unsafe_to_call(
+ db,
+ None,
+ crate::Crate::from(ctx.scope.resolver().krate()).edition(db),
+ )
|| it.is_unstable(db)
{
return None;
@@ -667,7 +674,11 @@ pub(super) fn impl_static_method<'a, 'lt, 'db, DB: HirDatabase>(
// Filter out private and unsafe functions
if !it.is_visible_from(db, module)
- || it.is_unsafe_to_call(db, None, Edition::CURRENT_FIXME)
+ || it.is_unsafe_to_call(
+ db,
+ None,
+ crate::Crate::from(ctx.scope.resolver().krate()).edition(db),
+ )
|| it.is_unstable(db)
{
return None;
diff --git a/crates/ide-assists/src/handlers/auto_import.rs b/crates/ide-assists/src/handlers/auto_import.rs
index cc2bf81749..2694910aa6 100644
--- a/crates/ide-assists/src/handlers/auto_import.rs
+++ b/crates/ide-assists/src/handlers/auto_import.rs
@@ -155,6 +155,7 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<
&scope,
mod_path_to_ast(&import_path, edition),
&ctx.config.insert_use,
+ edition,
);
},
);
diff --git a/crates/ide-assists/src/handlers/convert_closure_to_fn.rs b/crates/ide-assists/src/handlers/convert_closure_to_fn.rs
index ca142332d9..5a223e1130 100644
--- a/crates/ide-assists/src/handlers/convert_closure_to_fn.rs
+++ b/crates/ide-assists/src/handlers/convert_closure_to_fn.rs
@@ -132,7 +132,7 @@ pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_>)
);
}
- if block.try_token().is_none()
+ if block.try_block_modifier().is_none()
&& block.unsafe_token().is_none()
&& block.label().is_none()
&& block.const_token().is_none()
diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs
index f2363c6f7b..124ef509fb 100644
--- a/crates/ide-assists/src/handlers/extract_function.rs
+++ b/crates/ide-assists/src/handlers/extract_function.rs
@@ -859,7 +859,7 @@ impl FunctionBody {
ast::BlockExpr(block_expr) => {
let (constness, block) = match block_expr.modifier() {
Some(ast::BlockModifier::Const(_)) => (true, block_expr),
- Some(ast::BlockModifier::Try(_)) => (false, block_expr),
+ Some(ast::BlockModifier::Try { .. }) => (false, block_expr),
Some(ast::BlockModifier::Label(label)) if label.lifetime().is_some() => (false, block_expr),
_ => continue,
};
diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs
index bd66c02b41..ded3b0f5ac 100644
--- a/crates/ide-assists/src/handlers/generate_function.rs
+++ b/crates/ide-assists/src/handlers/generate_function.rs
@@ -1147,14 +1147,7 @@ fn fn_arg_type(
if ty.is_reference() || ty.is_mutable_reference() {
let famous_defs = &FamousDefs(&ctx.sema, ctx.sema.scope(fn_arg.syntax())?.krate());
convert_reference_type(ty.strip_references(), ctx.db(), famous_defs)
- .map(|conversion| {
- conversion
- .convert_type(
- ctx.db(),
- target_module.krate(ctx.db()).to_display_target(ctx.db()),
- )
- .to_string()
- })
+ .map(|conversion| conversion.convert_type(ctx.db(), target_module).to_string())
.or_else(|| ty.display_source_code(ctx.db(), target_module.into(), true).ok())
} else {
ty.display_source_code(ctx.db(), target_module.into(), true).ok()
@@ -3191,4 +3184,26 @@ fn main() {
"#,
);
}
+
+ #[test]
+ fn regression_21288() {
+ check_assist(
+ generate_function,
+ r#"
+//- minicore: copy
+fn foo() {
+ $0bar(&|x| true)
+}
+ "#,
+ r#"
+fn foo() {
+ bar(&|x| true)
+}
+
+fn bar(arg: impl Fn(_) -> bool) {
+ ${0:todo!()}
+}
+ "#,
+ );
+ }
}
diff --git a/crates/ide-assists/src/handlers/generate_getter_or_setter.rs b/crates/ide-assists/src/handlers/generate_getter_or_setter.rs
index e42d0ed1b0..73e93a3fbf 100644
--- a/crates/ide-assists/src/handlers/generate_getter_or_setter.rs
+++ b/crates/ide-assists/src/handlers/generate_getter_or_setter.rs
@@ -226,15 +226,15 @@ fn generate_getter_from_info(
)
} else {
(|| {
- let krate = ctx.sema.scope(record_field_info.field_ty.syntax())?.krate();
- let famous_defs = &FamousDefs(&ctx.sema, krate);
+ let module = ctx.sema.scope(record_field_info.field_ty.syntax())?.module();
+ let famous_defs = &FamousDefs(&ctx.sema, module.krate(ctx.db()));
ctx.sema
.resolve_type(&record_field_info.field_ty)
.and_then(|ty| convert_reference_type(ty, ctx.db(), famous_defs))
.map(|conversion| {
cov_mark::hit!(convert_reference_type);
(
- conversion.convert_type(ctx.db(), krate.to_display_target(ctx.db())),
+ conversion.convert_type(ctx.db(), module),
conversion.getter(record_field_info.field_name.to_string()),
)
})
diff --git a/crates/ide-assists/src/handlers/move_guard.rs b/crates/ide-assists/src/handlers/move_guard.rs
index 84f02bdfdb..fdc5a0fbda 100644
--- a/crates/ide-assists/src/handlers/move_guard.rs
+++ b/crates/ide-assists/src/handlers/move_guard.rs
@@ -1,6 +1,7 @@
-use itertools::Itertools;
+use itertools::{Itertools, chain};
use syntax::{
SyntaxKind::WHITESPACE,
+ TextRange,
ast::{
AstNode, BlockExpr, ElseBranch, Expr, IfExpr, MatchArm, Pat, edit::AstNodeEdit, make,
prec::ExprPrecedence, syntax_factory::SyntaxFactory,
@@ -44,13 +45,26 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext<'_>)
cov_mark::hit!(move_guard_inapplicable_in_arm_body);
return None;
}
- let space_before_guard = guard.syntax().prev_sibling_or_token();
+ let rest_arms = rest_arms(&match_arm, ctx.selection_trimmed())?;
+ let space_before_delete = chain(
+ guard.syntax().prev_sibling_or_token(),
+ rest_arms.iter().filter_map(|it| it.syntax().prev_sibling_or_token()),
+ );
let space_after_arrow = match_arm.fat_arrow_token()?.next_sibling_or_token();
- let guard_condition = guard.condition()?.reset_indent();
let arm_expr = match_arm.expr()?;
- let then_branch = crate::utils::wrap_block(&arm_expr);
- let if_expr = make::expr_if(guard_condition, then_branch, None).indent(arm_expr.indent_level());
+ let if_branch = chain([&match_arm], &rest_arms)
+ .rfold(None, |else_branch, arm| {
+ if let Some(guard) = arm.guard() {
+ let then_branch = crate::utils::wrap_block(&arm.expr()?);
+ let guard_condition = guard.condition()?.reset_indent();
+ Some(make::expr_if(guard_condition, then_branch, else_branch).into())
+ } else {
+ arm.expr().map(|it| crate::utils::wrap_block(&it).into())
+ }
+ })?
+ .indent(arm_expr.indent_level());
+ let ElseBranch::IfExpr(if_expr) = if_branch else { return None };
let target = guard.syntax().text_range();
acc.add(
@@ -59,10 +73,13 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext<'_>)
target,
|builder| {
let mut edit = builder.make_editor(match_arm.syntax());
- if let Some(element) = space_before_guard
- && element.kind() == WHITESPACE
- {
- edit.delete(element);
+ for element in space_before_delete {
+ if element.kind() == WHITESPACE {
+ edit.delete(element);
+ }
+ }
+ for rest_arm in &rest_arms {
+ edit.delete(rest_arm.syntax());
}
if let Some(element) = space_after_arrow
&& element.kind() == WHITESPACE
@@ -221,6 +238,25 @@ pub(crate) fn move_arm_cond_to_match_guard(
)
}
+fn rest_arms(match_arm: &MatchArm, selection: TextRange) -> Option<Vec<MatchArm>> {
+ match_arm
+ .parent_match()
+ .match_arm_list()?
+ .arms()
+ .skip_while(|it| it != match_arm)
+ .skip(1)
+ .take_while(move |it| {
+ selection.is_empty() || crate::utils::is_selected(it, selection, false)
+ })
+ .take_while(move |it| {
+ it.pat()
+ .zip(match_arm.pat())
+ .is_some_and(|(a, b)| a.syntax().text() == b.syntax().text())
+ })
+ .collect::<Vec<_>>()
+ .into()
+}
+
// Parses an if-else-if chain to get the conditions and the then branches until we encounter an else
// branch or the end.
fn parse_if_chain(if_expr: IfExpr) -> Option<(Vec<(Expr, BlockExpr)>, Option<BlockExpr>)> {
@@ -345,6 +381,115 @@ fn main() {
}
#[test]
+ fn move_multiple_guard_to_arm_body_works() {
+ check_assist(
+ move_guard_to_arm_body,
+ r#"
+fn main() {
+ match 92 {
+ x @ 0..30 $0if x % 3 == 0 => false,
+ x @ 0..30 if x % 2 == 0 => true,
+ _ => false
+ }
+}
+"#,
+ r#"
+fn main() {
+ match 92 {
+ x @ 0..30 => if x % 3 == 0 {
+ false
+ } else if x % 2 == 0 {
+ true
+ },
+ _ => false
+ }
+}
+"#,
+ );
+
+ check_assist(
+ move_guard_to_arm_body,
+ r#"
+fn main() {
+ match 92 {
+ x @ 0..30 $0if x % 3 == 0 => false,
+ x @ 0..30 if x % 2 == 0 => true,
+ x @ 0..30 => false,
+ _ => true
+ }
+}
+"#,
+ r#"
+fn main() {
+ match 92 {
+ x @ 0..30 => if x % 3 == 0 {
+ false
+ } else if x % 2 == 0 {
+ true
+ } else {
+ false
+ },
+ _ => true
+ }
+}
+"#,
+ );
+
+ check_assist(
+ move_guard_to_arm_body,
+ r#"
+fn main() {
+ match 92 {
+ x @ 0..30 if x % 3 == 0 => false,
+ x @ 0..30 $0if x % 2 == 0$0 => true,
+ x @ 0..30 => false,
+ _ => true
+ }
+}
+"#,
+ r#"
+fn main() {
+ match 92 {
+ x @ 0..30 if x % 3 == 0 => false,
+ x @ 0..30 => if x % 2 == 0 {
+ true
+ },
+ x @ 0..30 => false,
+ _ => true
+ }
+}
+"#,
+ );
+
+ check_assist(
+ move_guard_to_arm_body,
+ r#"
+fn main() {
+ match 92 {
+ x @ 0..30 $0if x % 3 == 0 => false,
+ x @ 0..30 $0if x % 2 == 0 => true,
+ x @ 0..30 => false,
+ _ => true
+ }
+}
+"#,
+ r#"
+fn main() {
+ match 92 {
+ x @ 0..30 => if x % 3 == 0 {
+ false
+ } else if x % 2 == 0 {
+ true
+ },
+ x @ 0..30 => false,
+ _ => true
+ }
+}
+"#,
+ );
+ }
+
+ #[test]
fn move_guard_to_block_arm_body_works() {
check_assist(
move_guard_to_arm_body,
diff --git a/crates/ide-assists/src/utils.rs b/crates/ide-assists/src/utils.rs
index 4b8c193057..5e08cba8e2 100644
--- a/crates/ide-assists/src/utils.rs
+++ b/crates/ide-assists/src/utils.rs
@@ -4,8 +4,7 @@ use std::slice;
pub(crate) use gen_trait_fn_body::gen_trait_fn_body;
use hir::{
- DisplayTarget, HasAttrs as HirHasAttrs, HirDisplay, InFile, ModuleDef, PathResolution,
- Semantics,
+ HasAttrs as HirHasAttrs, HirDisplay, InFile, ModuleDef, PathResolution, Semantics,
db::{ExpandDatabase, HirDatabase},
};
use ide_db::{
@@ -836,13 +835,12 @@ enum ReferenceConversionType {
}
impl<'db> ReferenceConversion<'db> {
- pub(crate) fn convert_type(
- &self,
- db: &'db dyn HirDatabase,
- display_target: DisplayTarget,
- ) -> ast::Type {
+ pub(crate) fn convert_type(&self, db: &'db dyn HirDatabase, module: hir::Module) -> ast::Type {
let ty = match self.conversion {
- ReferenceConversionType::Copy => self.ty.display(db, display_target).to_string(),
+ ReferenceConversionType::Copy => self
+ .ty
+ .display_source_code(db, module.into(), true)
+ .unwrap_or_else(|_| "_".to_owned()),
ReferenceConversionType::AsRefStr => "&str".to_owned(),
ReferenceConversionType::AsRefSlice => {
let type_argument_name = self
@@ -850,8 +848,8 @@ impl<'db> ReferenceConversion<'db> {
.type_arguments()
.next()
.unwrap()
- .display(db, display_target)
- .to_string();
+ .display_source_code(db, module.into(), true)
+ .unwrap_or_else(|_| "_".to_owned());
format!("&[{type_argument_name}]")
}
ReferenceConversionType::Dereferenced => {
@@ -860,8 +858,8 @@ impl<'db> ReferenceConversion<'db> {
.type_arguments()
.next()
.unwrap()
- .display(db, display_target)
- .to_string();
+ .display_source_code(db, module.into(), true)
+ .unwrap_or_else(|_| "_".to_owned());
format!("&{type_argument_name}")
}
ReferenceConversionType::Option => {
@@ -870,16 +868,22 @@ impl<'db> ReferenceConversion<'db> {
.type_arguments()
.next()
.unwrap()
- .display(db, display_target)
- .to_string();
+ .display_source_code(db, module.into(), true)
+ .unwrap_or_else(|_| "_".to_owned());
format!("Option<&{type_argument_name}>")
}
ReferenceConversionType::Result => {
let mut type_arguments = self.ty.type_arguments();
- let first_type_argument_name =
- type_arguments.next().unwrap().display(db, display_target).to_string();
- let second_type_argument_name =
- type_arguments.next().unwrap().display(db, display_target).to_string();
+ let first_type_argument_name = type_arguments
+ .next()
+ .unwrap()
+ .display_source_code(db, module.into(), true)
+ .unwrap_or_else(|_| "_".to_owned());
+ let second_type_argument_name = type_arguments
+ .next()
+ .unwrap()
+ .display_source_code(db, module.into(), true)
+ .unwrap_or_else(|_| "_".to_owned());
format!("Result<&{first_type_argument_name}, &{second_type_argument_name}>")
}
};
diff --git a/crates/ide-completion/src/completions/postfix.rs b/crates/ide-completion/src/completions/postfix.rs
index 7f67ef848e..cffc44f8af 100644
--- a/crates/ide-completion/src/completions/postfix.rs
+++ b/crates/ide-completion/src/completions/postfix.rs
@@ -151,6 +151,10 @@ pub(crate) fn complete_postfix(
.add_to(acc, ctx.db);
}
},
+ _ if is_in_cond => {
+ postfix_snippet("let", "let", &format!("let $1 = {receiver_text}"))
+ .add_to(acc, ctx.db);
+ }
_ if matches!(second_ancestor.kind(), STMT_LIST | EXPR_STMT) => {
postfix_snippet("let", "let", &format!("let $0 = {receiver_text};"))
.add_to(acc, ctx.db);
@@ -253,7 +257,6 @@ pub(crate) fn complete_postfix(
&format!("while {receiver_text} {{\n $0\n}}"),
)
.add_to(acc, ctx.db);
- postfix_snippet("not", "!expr", &format!("!{receiver_text}")).add_to(acc, ctx.db);
} else if let Some(trait_) = ctx.famous_defs().core_iter_IntoIterator()
&& receiver_ty.impls_trait(ctx.db, trait_, &[])
{
@@ -266,6 +269,10 @@ pub(crate) fn complete_postfix(
}
}
+ if receiver_ty.is_bool() || receiver_ty.is_unknown() {
+ postfix_snippet("not", "!expr", &format!("!{receiver_text}")).add_to(acc, ctx.db);
+ }
+
let block_should_be_wrapped = if let ast::Expr::BlockExpr(block) = dot_receiver {
block.modifier().is_some() || !block.is_standalone()
} else {
@@ -586,6 +593,31 @@ fn main() {
}
#[test]
+ fn postfix_completion_works_in_if_condition() {
+ check(
+ r#"
+fn foo(cond: bool) {
+ if cond.$0
+}
+"#,
+ expect![[r#"
+ sn box Box::new(expr)
+ sn call function(expr)
+ sn const const {}
+ sn dbg dbg!(expr)
+ sn dbgr dbg!(&expr)
+ sn deref *expr
+ sn let let
+ sn not !expr
+ sn ref &expr
+ sn refm &mut expr
+ sn return return expr
+ sn unsafe unsafe {}
+ "#]],
+ );
+ }
+
+ #[test]
fn postfix_type_filtering() {
check(
r#"
@@ -745,6 +777,25 @@ fn main() {
}
#[test]
+ fn iflet_fallback_cond() {
+ check_edit(
+ "let",
+ r#"
+fn main() {
+ let bar = 2;
+ if bar.$0
+}
+"#,
+ r#"
+fn main() {
+ let bar = 2;
+ if let $1 = bar
+}
+"#,
+ );
+ }
+
+ #[test]
fn option_letelse() {
check_edit(
"lete",
diff --git a/crates/ide-db/src/imports/insert_use.rs b/crates/ide-db/src/imports/insert_use.rs
index 4444ef5d81..db1d599d55 100644
--- a/crates/ide-db/src/imports/insert_use.rs
+++ b/crates/ide-db/src/imports/insert_use.rs
@@ -146,9 +146,14 @@ pub fn insert_use(scope: &ImportScope, path: ast::Path, cfg: &InsertUseConfig) {
insert_use_with_alias_option(scope, path, cfg, None);
}
-pub fn insert_use_as_alias(scope: &ImportScope, path: ast::Path, cfg: &InsertUseConfig) {
+pub fn insert_use_as_alias(
+ scope: &ImportScope,
+ path: ast::Path,
+ cfg: &InsertUseConfig,
+ edition: span::Edition,
+) {
let text: &str = "use foo as _";
- let parse = syntax::SourceFile::parse(text, span::Edition::CURRENT_FIXME);
+ let parse = syntax::SourceFile::parse(text, edition);
let node = parse
.tree()
.syntax()
diff --git a/crates/ide-db/src/syntax_helpers/node_ext.rs b/crates/ide-db/src/syntax_helpers/node_ext.rs
index 94ecf6a02d..e30b21c139 100644
--- a/crates/ide-db/src/syntax_helpers/node_ext.rs
+++ b/crates/ide-db/src/syntax_helpers/node_ext.rs
@@ -49,7 +49,7 @@ pub fn is_closure_or_blk_with_modif(expr: &ast::Expr) -> bool {
block_expr.modifier(),
Some(
ast::BlockModifier::Async(_)
- | ast::BlockModifier::Try(_)
+ | ast::BlockModifier::Try { .. }
| ast::BlockModifier::Const(_)
)
)
@@ -148,7 +148,7 @@ pub fn walk_patterns_in_expr(start: &ast::Expr, cb: &mut dyn FnMut(ast::Pat)) {
block_expr.modifier(),
Some(
ast::BlockModifier::Async(_)
- | ast::BlockModifier::Try(_)
+ | ast::BlockModifier::Try { .. }
| ast::BlockModifier::Const(_)
)
)
@@ -291,7 +291,7 @@ pub fn for_each_tail_expr(expr: &ast::Expr, cb: &mut dyn FnMut(&ast::Expr)) {
match b.modifier() {
Some(
ast::BlockModifier::Async(_)
- | ast::BlockModifier::Try(_)
+ | ast::BlockModifier::Try { .. }
| ast::BlockModifier::Const(_),
) => return cb(expr),
diff --git a/crates/ide-db/src/syntax_helpers/suggest_name.rs b/crates/ide-db/src/syntax_helpers/suggest_name.rs
index b8b9a7a768..5d1e876ea2 100644
--- a/crates/ide-db/src/syntax_helpers/suggest_name.rs
+++ b/crates/ide-db/src/syntax_helpers/suggest_name.rs
@@ -206,17 +206,18 @@ impl NameGenerator {
expr: &ast::Expr,
sema: &Semantics<'_, RootDatabase>,
) -> Option<SmolStr> {
+ let edition = sema.scope(expr.syntax())?.krate().edition(sema.db);
// `from_param` does not benefit from stripping it need the largest
// context possible so we check firstmost
- if let Some(name) = from_param(expr, sema) {
+ if let Some(name) = from_param(expr, sema, edition) {
return Some(self.suggest_name(&name));
}
let mut next_expr = Some(expr.clone());
while let Some(expr) = next_expr {
- let name = from_call(&expr)
- .or_else(|| from_type(&expr, sema))
- .or_else(|| from_field_name(&expr));
+ let name = from_call(&expr, edition)
+ .or_else(|| from_type(&expr, sema, edition))
+ .or_else(|| from_field_name(&expr, edition));
if let Some(name) = name {
return Some(self.suggest_name(&name));
}
@@ -270,7 +271,7 @@ impl NameGenerator {
}
}
-fn normalize(name: &str) -> Option<SmolStr> {
+fn normalize(name: &str, edition: syntax::Edition) -> Option<SmolStr> {
let name = to_lower_snake_case(name).to_smolstr();
if USELESS_NAMES.contains(&name.as_str()) {
@@ -281,16 +282,16 @@ fn normalize(name: &str) -> Option<SmolStr> {
return None;
}
- if !is_valid_name(&name) {
+ if !is_valid_name(&name, edition) {
return None;
}
Some(name)
}
-fn is_valid_name(name: &str) -> bool {
+fn is_valid_name(name: &str, edition: syntax::Edition) -> bool {
matches!(
- super::LexedStr::single_token(syntax::Edition::CURRENT_FIXME, name),
+ super::LexedStr::single_token(edition, name),
Some((syntax::SyntaxKind::IDENT, _error))
)
}
@@ -304,11 +305,11 @@ fn is_useless_method(method: &ast::MethodCallExpr) -> bool {
}
}
-fn from_call(expr: &ast::Expr) -> Option<SmolStr> {
- from_func_call(expr).or_else(|| from_method_call(expr))
+fn from_call(expr: &ast::Expr, edition: syntax::Edition) -> Option<SmolStr> {
+ from_func_call(expr, edition).or_else(|| from_method_call(expr, edition))
}
-fn from_func_call(expr: &ast::Expr) -> Option<SmolStr> {
+fn from_func_call(expr: &ast::Expr, edition: syntax::Edition) -> Option<SmolStr> {
let call = match expr {
ast::Expr::CallExpr(call) => call,
_ => return None,
@@ -318,10 +319,10 @@ fn from_func_call(expr: &ast::Expr) -> Option<SmolStr> {
_ => return None,
};
let ident = func.path()?.segment()?.name_ref()?.ident_token()?;
- normalize(ident.text())
+ normalize(ident.text(), edition)
}
-fn from_method_call(expr: &ast::Expr) -> Option<SmolStr> {
+fn from_method_call(expr: &ast::Expr, edition: syntax::Edition) -> Option<SmolStr> {
let method = match expr {
ast::Expr::MethodCallExpr(call) => call,
_ => return None,
@@ -340,10 +341,14 @@ fn from_method_call(expr: &ast::Expr) -> Option<SmolStr> {
}
}
- normalize(name)
+ normalize(name, edition)
}
-fn from_param(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<SmolStr> {
+fn from_param(
+ expr: &ast::Expr,
+ sema: &Semantics<'_, RootDatabase>,
+ edition: Edition,
+) -> Option<SmolStr> {
let arg_list = expr.syntax().parent().and_then(ast::ArgList::cast)?;
let args_parent = arg_list.syntax().parent()?;
let func = match_ast! {
@@ -362,7 +367,7 @@ fn from_param(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<Sm
let param = func.params().into_iter().nth(idx)?;
let pat = sema.source(param)?.value.right()?.pat()?;
let name = var_name_from_pat(&pat)?;
- normalize(&name.to_smolstr())
+ normalize(&name.to_smolstr(), edition)
}
fn var_name_from_pat(pat: &ast::Pat) -> Option<ast::Name> {
@@ -374,10 +379,13 @@ fn var_name_from_pat(pat: &ast::Pat) -> Option<ast::Name> {
}
}
-fn from_type(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> Option<SmolStr> {
+fn from_type(
+ expr: &ast::Expr,
+ sema: &Semantics<'_, RootDatabase>,
+ edition: Edition,
+) -> Option<SmolStr> {
let ty = sema.type_of_expr(expr)?.adjusted();
let ty = ty.remove_ref().unwrap_or(ty);
- let edition = sema.scope(expr.syntax())?.krate().edition(sema.db);
name_of_type(&ty, sema.db, edition)
}
@@ -417,7 +425,7 @@ fn name_of_type<'db>(
} else {
return None;
};
- normalize(&name)
+ normalize(&name, edition)
}
fn sequence_name<'db>(
@@ -450,13 +458,13 @@ fn trait_name(trait_: &hir::Trait, db: &RootDatabase, edition: Edition) -> Optio
Some(name)
}
-fn from_field_name(expr: &ast::Expr) -> Option<SmolStr> {
+fn from_field_name(expr: &ast::Expr, edition: syntax::Edition) -> Option<SmolStr> {
let field = match expr {
ast::Expr::FieldExpr(field) => field,
_ => return None,
};
let ident = field.name_ref()?.ident_token()?;
- normalize(ident.text())
+ normalize(ident.text(), edition)
}
#[cfg(test)]
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index c0a7438081..3c3ac9d3bb 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -332,7 +332,7 @@ pub(crate) fn find_fn_or_blocks(
ast::BlockExpr(blk) => {
match blk.modifier() {
Some(ast::BlockModifier::Async(_)) => blk.syntax().clone(),
- Some(ast::BlockModifier::Try(_)) if token_kind != T![return] => blk.syntax().clone(),
+ Some(ast::BlockModifier::Try { .. }) if token_kind != T![return] => blk.syntax().clone(),
_ => continue,
}
},
@@ -404,8 +404,8 @@ fn nav_for_exit_points(
let blk_in_file = InFile::new(file_id, blk.into());
Some(expr_to_nav(db, blk_in_file, Some(async_tok)))
},
- Some(ast::BlockModifier::Try(_)) if token_kind != T![return] => {
- let try_tok = blk.try_token()?.text_range();
+ Some(ast::BlockModifier::Try { .. }) if token_kind != T![return] => {
+ let try_tok = blk.try_block_modifier()?.try_token()?.text_range();
let blk_in_file = InFile::new(file_id, blk.into());
Some(expr_to_nav(db, blk_in_file, Some(try_tok)))
},
diff --git a/crates/ide/src/highlight_related.rs b/crates/ide/src/highlight_related.rs
index fce033382b..c8e01e21ec 100644
--- a/crates/ide/src/highlight_related.rs
+++ b/crates/ide/src/highlight_related.rs
@@ -473,7 +473,7 @@ pub(crate) fn highlight_exit_points(
},
ast::BlockExpr(blk) => match blk.modifier() {
Some(ast::BlockModifier::Async(t)) => hl_exit_points(sema, Some(t), blk.into()),
- Some(ast::BlockModifier::Try(t)) if token.kind() != T![return] => {
+ Some(ast::BlockModifier::Try { try_token: t, .. }) if token.kind() != T![return] => {
hl_exit_points(sema, Some(t), blk.into())
},
_ => continue,
diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs
index 15ea92d1c6..cf5f137cdd 100644
--- a/crates/ide/src/hover/render.rs
+++ b/crates/ide/src/hover/render.rs
@@ -74,7 +74,7 @@ pub(super) fn try_expr(
ast::Fn(fn_) => sema.to_def(&fn_)?.ret_type(sema.db),
ast::Item(__) => return None,
ast::ClosureExpr(closure) => sema.type_of_expr(&closure.body()?)?.original,
- ast::BlockExpr(block_expr) => if matches!(block_expr.modifier(), Some(ast::BlockModifier::Async(_) | ast::BlockModifier::Try(_)| ast::BlockModifier::Const(_))) {
+ ast::BlockExpr(block_expr) => if matches!(block_expr.modifier(), Some(ast::BlockModifier::Async(_) | ast::BlockModifier::Try { .. } | ast::BlockModifier::Const(_))) {
sema.type_of_expr(&block_expr.into())?.original
} else {
continue;
diff --git a/crates/ide/src/inlay_hints/bind_pat.rs b/crates/ide/src/inlay_hints/bind_pat.rs
index c74e3104c1..caf7cc714d 100644
--- a/crates/ide/src/inlay_hints/bind_pat.rs
+++ b/crates/ide/src/inlay_hints/bind_pat.rs
@@ -1382,4 +1382,21 @@ fn f<'a>() {
"#]],
);
}
+
+ #[test]
+ fn ref_multi_trait_impl_trait() {
+ check_with_config(
+ InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG },
+ r#"
+//- minicore: sized
+trait Eq {}
+trait Ord {}
+
+fn foo(argument: &(impl Eq + Ord)) {
+ let x = argument;
+ // ^ &(impl Eq + Ord)
+}
+ "#,
+ );
+ }
}
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 930eaf2262..2e618550f9 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -67,7 +67,7 @@ use ide_db::{
FxHashMap, FxIndexSet, LineIndexDatabase,
base_db::{
CrateOrigin, CrateWorkspaceData, Env, FileSet, RootQueryDb, SourceDatabase, VfsPath,
- salsa::{Cancelled, Database},
+ salsa::{CancellationToken, Cancelled, Database},
},
prime_caches, symbol_index,
};
@@ -947,6 +947,10 @@ impl Analysis {
// We use `attach_db_allow_change()` and not `attach_db()` because fixture injection can change the database.
hir::attach_db_allow_change(&self.db, || Cancelled::catch(|| f(&self.db)))
}
+
+ pub fn cancellation_token(&self) -> CancellationToken {
+ self.db.cancellation_token()
+ }
}
#[test]
diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs
index 9ab07565e9..f86974b4ec 100644
--- a/crates/ide/src/signature_help.rs
+++ b/crates/ide/src/signature_help.rs
@@ -1975,8 +1975,8 @@ trait Sub: Super + Super {
fn f() -> impl Sub<$0
"#,
expect![[r#"
- trait Sub<SubTy = …, SuperTy = …>
- ^^^^^^^^^ -----------
+ trait Sub<SuperTy = …, SubTy = …>
+ ^^^^^^^^^^^ ---------
"#]],
);
}
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index e64fd6488f..ce1df6a1e7 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -14,7 +14,7 @@ mod tests;
use std::ops::ControlFlow;
use either::Either;
-use hir::{DefWithBody, EditionedFileId, InFile, InRealFile, MacroKind, Name, Semantics};
+use hir::{DefWithBody, EditionedFileId, InFile, InRealFile, MacroKind, Semantics};
use ide_db::{FxHashMap, FxHashSet, MiniCore, Ranker, RootDatabase, SymbolKind};
use syntax::{
AstNode, AstToken, NodeOrToken,
@@ -257,8 +257,7 @@ fn traverse(
// FIXME: accommodate range highlighting
let mut body_stack: Vec<Option<DefWithBody>> = vec![];
- let mut per_body_cache: FxHashMap<DefWithBody, (FxHashSet<_>, FxHashMap<Name, u32>)> =
- FxHashMap::default();
+ let mut per_body_cache: FxHashMap<DefWithBody, FxHashSet<_>> = FxHashMap::default();
// Walk all nodes, keeping track of whether we are inside a macro or not.
// If in macro, expand it first and highlight the expanded code.
@@ -422,14 +421,11 @@ fn traverse(
}
let edition = descended_element.file_id.edition(sema.db);
- let (unsafe_ops, bindings_shadow_count) = match current_body {
- Some(current_body) => {
- let (ops, bindings) = per_body_cache
- .entry(current_body)
- .or_insert_with(|| (sema.get_unsafe_ops(current_body), Default::default()));
- (&*ops, Some(bindings))
- }
- None => (&empty, None),
+ let unsafe_ops = match current_body {
+ Some(current_body) => per_body_cache
+ .entry(current_body)
+ .or_insert_with(|| sema.get_unsafe_ops(current_body)),
+ None => &empty,
};
let is_unsafe_node =
|node| unsafe_ops.contains(&InFile::new(descended_element.file_id, node));
@@ -438,7 +434,6 @@ fn traverse(
let hl = highlight::name_like(
sema,
krate,
- bindings_shadow_count,
&is_unsafe_node,
config.syntactic_name_ref_highlighting,
name_like,
diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs
index dcc9a8c0d5..a94bbc9f04 100644
--- a/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/crates/ide/src/syntax_highlighting/highlight.rs
@@ -5,12 +5,11 @@ use std::ops::ControlFlow;
use either::Either;
use hir::{AsAssocItem, HasAttrs, HasVisibility, Semantics};
use ide_db::{
- FxHashMap, RootDatabase, SymbolKind,
+ RootDatabase, SymbolKind,
defs::{Definition, IdentClass, NameClass, NameRefClass},
syntax_helpers::node_ext::walk_pat,
};
use span::Edition;
-use stdx::hash_once;
use syntax::{
AstNode, AstPtr, AstToken, NodeOrToken,
SyntaxKind::{self, *},
@@ -64,7 +63,6 @@ pub(super) fn token(
pub(super) fn name_like(
sema: &Semantics<'_, RootDatabase>,
krate: Option<hir::Crate>,
- bindings_shadow_count: Option<&mut FxHashMap<hir::Name, u32>>,
is_unsafe_node: &impl Fn(AstPtr<Either<ast::Expr, ast::Pat>>) -> bool,
syntactic_name_ref_highlighting: bool,
name_like: ast::NameLike,
@@ -75,22 +73,15 @@ pub(super) fn name_like(
ast::NameLike::NameRef(name_ref) => highlight_name_ref(
sema,
krate,
- bindings_shadow_count,
&mut binding_hash,
is_unsafe_node,
syntactic_name_ref_highlighting,
name_ref,
edition,
),
- ast::NameLike::Name(name) => highlight_name(
- sema,
- bindings_shadow_count,
- &mut binding_hash,
- is_unsafe_node,
- krate,
- name,
- edition,
- ),
+ ast::NameLike::Name(name) => {
+ highlight_name(sema, &mut binding_hash, is_unsafe_node, krate, name, edition)
+ }
ast::NameLike::Lifetime(lifetime) => match IdentClass::classify_lifetime(sema, &lifetime) {
Some(IdentClass::NameClass(NameClass::Definition(def))) => {
highlight_def(sema, krate, def, edition, false) | HlMod::Definition
@@ -273,7 +264,6 @@ fn keyword(token: SyntaxToken, kind: SyntaxKind) -> Highlight {
fn highlight_name_ref(
sema: &Semantics<'_, RootDatabase>,
krate: Option<hir::Crate>,
- bindings_shadow_count: Option<&mut FxHashMap<hir::Name, u32>>,
binding_hash: &mut Option<u64>,
is_unsafe_node: &impl Fn(AstPtr<Either<ast::Expr, ast::Pat>>) -> bool,
syntactic_name_ref_highlighting: bool,
@@ -306,12 +296,8 @@ fn highlight_name_ref(
};
let mut h = match name_class {
NameRefClass::Definition(def, _) => {
- if let Definition::Local(local) = &def
- && let Some(bindings_shadow_count) = bindings_shadow_count
- {
- let name = local.name(sema.db);
- let shadow_count = bindings_shadow_count.entry(name.clone()).or_default();
- *binding_hash = Some(calc_binding_hash(&name, *shadow_count))
+ if let Definition::Local(local) = &def {
+ *binding_hash = Some(local.as_id() as u64);
};
let mut h = highlight_def(sema, krate, def, edition, true);
@@ -432,7 +418,6 @@ fn highlight_name_ref(
fn highlight_name(
sema: &Semantics<'_, RootDatabase>,
- bindings_shadow_count: Option<&mut FxHashMap<hir::Name, u32>>,
binding_hash: &mut Option<u64>,
is_unsafe_node: &impl Fn(AstPtr<Either<ast::Expr, ast::Pat>>) -> bool,
krate: Option<hir::Crate>,
@@ -440,13 +425,8 @@ fn highlight_name(
edition: Edition,
) -> Highlight {
let name_kind = NameClass::classify(sema, &name);
- if let Some(NameClass::Definition(Definition::Local(local))) = &name_kind
- && let Some(bindings_shadow_count) = bindings_shadow_count
- {
- let name = local.name(sema.db);
- let shadow_count = bindings_shadow_count.entry(name.clone()).or_default();
- *shadow_count += 1;
- *binding_hash = Some(calc_binding_hash(&name, *shadow_count))
+ if let Some(NameClass::Definition(Definition::Local(local))) = &name_kind {
+ *binding_hash = Some(local.as_id() as u64);
};
match name_kind {
Some(NameClass::Definition(def)) => {
@@ -474,10 +454,6 @@ fn highlight_name(
}
}
-fn calc_binding_hash(name: &hir::Name, shadow_count: u32) -> u64 {
- hash_once::<ide_db::FxHasher>((name.as_str(), shadow_count))
-}
-
pub(super) fn highlight_def(
sema: &Semantics<'_, RootDatabase>,
krate: Option<hir::Crate>,
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html b/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html
index d5401e7aec..7c64707ac1 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_rainbow.html
@@ -42,14 +42,14 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
</style>
<pre><code><span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
- <span class="keyword">let</span> <span class="variable declaration reference" data-binding-hash="18084384843626695225" style="color: hsl(154,95%,53%);">hello</span> <span class="operator">=</span> <span class="string_literal">"hello"</span><span class="semicolon">;</span>
- <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="5697120079570210533" style="color: hsl(268,86%,80%);">x</span> <span class="operator">=</span> <span class="variable reference" data-binding-hash="18084384843626695225" style="color: hsl(154,95%,53%);">hello</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="4222724691718692706" style="color: hsl(156,71%,51%);">y</span> <span class="operator">=</span> <span class="variable reference" data-binding-hash="18084384843626695225" style="color: hsl(154,95%,53%);">hello</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+ <span class="keyword">let</span> <span class="variable declaration reference" data-binding-hash="0" style="color: hsl(74,59%,48%);">hello</span> <span class="operator">=</span> <span class="string_literal">"hello"</span><span class="semicolon">;</span>
+ <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="1" style="color: hsl(152,51%,64%);">x</span> <span class="operator">=</span> <span class="variable reference" data-binding-hash="0" style="color: hsl(74,59%,48%);">hello</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+ <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="2" style="color: hsl(272,82%,82%);">y</span> <span class="operator">=</span> <span class="variable reference" data-binding-hash="0" style="color: hsl(74,59%,48%);">hello</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
- <span class="keyword">let</span> <span class="variable declaration reference" data-binding-hash="17855021198829413584" style="color: hsl(230,76%,79%);">x</span> <span class="operator">=</span> <span class="string_literal">"other color please!"</span><span class="semicolon">;</span>
- <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="16380625810977895757" style="color: hsl(262,75%,75%);">y</span> <span class="operator">=</span> <span class="variable reference" data-binding-hash="17855021198829413584" style="color: hsl(230,76%,79%);">x</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
+ <span class="keyword">let</span> <span class="variable declaration reference" data-binding-hash="3" style="color: hsl(107,98%,81%);">x</span> <span class="operator">=</span> <span class="string_literal">"other color please!"</span><span class="semicolon">;</span>
+ <span class="keyword">let</span> <span class="variable declaration" data-binding-hash="4" style="color: hsl(241,93%,64%);">y</span> <span class="operator">=</span> <span class="variable reference" data-binding-hash="3" style="color: hsl(107,98%,81%);">x</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
<span class="brace">}</span>
<span class="keyword">fn</span> <span class="function declaration">bar</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
- <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable reference" data-binding-hash="18084384843626695225" style="color: hsl(154,95%,53%);">hello</span> <span class="operator">=</span> <span class="string_literal">"hello"</span><span class="semicolon">;</span>
+ <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable reference" data-binding-hash="0" style="color: hsl(74,59%,48%);">hello</span> <span class="operator">=</span> <span class="string_literal">"hello"</span><span class="semicolon">;</span>
<span class="brace">}</span></code></pre> \ No newline at end of file
diff --git a/crates/ide/src/typing.rs b/crates/ide/src/typing.rs
index 0381865fed..f8b0dbfe62 100644
--- a/crates/ide/src/typing.rs
+++ b/crates/ide/src/typing.rs
@@ -17,7 +17,10 @@ mod on_enter;
use either::Either;
use hir::EditionedFileId;
-use ide_db::{FilePosition, RootDatabase, base_db::RootQueryDb};
+use ide_db::{
+ FilePosition, RootDatabase,
+ base_db::{RootQueryDb, SourceDatabase},
+};
use span::Edition;
use std::iter;
@@ -70,11 +73,12 @@ pub(crate) fn on_char_typed(
if !TRIGGER_CHARS.contains(&char_typed) {
return None;
}
- // FIXME: We need to figure out the edition of the file here, but that means hitting the
- // database for more than just parsing the file which is bad.
+ let edition = db
+ .source_root_crates(db.file_source_root(position.file_id).source_root_id(db))
+ .first()
+ .map_or(Edition::CURRENT, |crates| crates.data(db).edition);
// FIXME: We are hitting the database here, if we are unlucky this call might block momentarily
- // causing the editor to feel sluggish!
- let edition = Edition::CURRENT_FIXME;
+ // causing the editor to feel sluggish! We need to make this bail if it would block too long?
let editioned_file_id_wrapper = EditionedFileId::from_span_guess_origin(
db,
span::EditionedFileId::new(position.file_id, edition),
@@ -457,8 +461,8 @@ mod tests {
let (offset, mut before) = extract_offset(before);
let edit = TextEdit::insert(offset, char_typed.to_string());
edit.apply(&mut before);
- let parse = SourceFile::parse(&before, span::Edition::CURRENT_FIXME);
- on_char_typed_(&parse, offset, char_typed, span::Edition::CURRENT_FIXME).map(|it| {
+ let parse = SourceFile::parse(&before, span::Edition::CURRENT);
+ on_char_typed_(&parse, offset, char_typed, span::Edition::CURRENT).map(|it| {
it.apply(&mut before);
before.to_string()
})
diff --git a/crates/intern/src/symbol/symbols.rs b/crates/intern/src/symbol/symbols.rs
index 2be4e41f4f..cc09a1aae7 100644
--- a/crates/intern/src/symbol/symbols.rs
+++ b/crates/intern/src/symbol/symbols.rs
@@ -110,6 +110,7 @@ define_symbols! {
win64_dash_unwind = "win64-unwind",
x86_dash_interrupt = "x86-interrupt",
rust_dash_preserve_dash_none = "preserve-none",
+ _0_u8 = "0_u8",
@PLAIN:
__ra_fixup,
@@ -285,6 +286,7 @@ define_symbols! {
Into,
into_future,
into_iter,
+ into_try_type,
IntoFuture,
IntoIter,
IntoIterator,
diff --git a/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs
index d83e2eb2b4..b75474ee2b 100644
--- a/crates/parser/src/grammar/expressions/atom.rs
+++ b/crates/parser/src/grammar/expressions/atom.rs
@@ -976,11 +976,17 @@ fn break_expr(p: &mut Parser<'_>, r: Restrictions) -> CompletedMarker {
// test try_block_expr
// fn foo() {
// let _ = try {};
+// let _ = try bikeshed T<U> {};
// }
fn try_block_expr(p: &mut Parser<'_>, m: Option<Marker>) -> CompletedMarker {
assert!(p.at(T![try]));
let m = m.unwrap_or_else(|| p.start());
+ let try_modifier = p.start();
p.bump(T![try]);
+ if p.eat_contextual_kw(T![bikeshed]) {
+ type_(p);
+ }
+ try_modifier.complete(p, TRY_BLOCK_MODIFIER);
if p.at(T!['{']) {
stmt_list(p);
} else {
diff --git a/crates/parser/src/syntax_kind/generated.rs b/crates/parser/src/syntax_kind/generated.rs
index 5d22d966b2..a2295e4495 100644
--- a/crates/parser/src/syntax_kind/generated.rs
+++ b/crates/parser/src/syntax_kind/generated.rs
@@ -114,6 +114,7 @@ pub enum SyntaxKind {
ATT_SYNTAX_KW,
AUTO_KW,
AWAIT_KW,
+ BIKESHED_KW,
BUILTIN_KW,
CLOBBER_ABI_KW,
DEFAULT_KW,
@@ -285,6 +286,7 @@ pub enum SyntaxKind {
STRUCT,
TOKEN_TREE,
TRAIT,
+ TRY_BLOCK_MODIFIER,
TRY_EXPR,
TUPLE_EXPR,
TUPLE_FIELD,
@@ -458,6 +460,7 @@ impl SyntaxKind {
| STRUCT
| TOKEN_TREE
| TRAIT
+ | TRY_BLOCK_MODIFIER
| TRY_EXPR
| TUPLE_EXPR
| TUPLE_FIELD
@@ -596,6 +599,7 @@ impl SyntaxKind {
ASM_KW => "asm",
ATT_SYNTAX_KW => "att_syntax",
AUTO_KW => "auto",
+ BIKESHED_KW => "bikeshed",
BUILTIN_KW => "builtin",
CLOBBER_ABI_KW => "clobber_abi",
DEFAULT_KW => "default",
@@ -698,6 +702,7 @@ impl SyntaxKind {
ASM_KW => true,
ATT_SYNTAX_KW => true,
AUTO_KW => true,
+ BIKESHED_KW => true,
BUILTIN_KW => true,
CLOBBER_ABI_KW => true,
DEFAULT_KW => true,
@@ -788,6 +793,7 @@ impl SyntaxKind {
ASM_KW => true,
ATT_SYNTAX_KW => true,
AUTO_KW => true,
+ BIKESHED_KW => true,
BUILTIN_KW => true,
CLOBBER_ABI_KW => true,
DEFAULT_KW => true,
@@ -941,6 +947,7 @@ impl SyntaxKind {
"asm" => ASM_KW,
"att_syntax" => ATT_SYNTAX_KW,
"auto" => AUTO_KW,
+ "bikeshed" => BIKESHED_KW,
"builtin" => BUILTIN_KW,
"clobber_abi" => CLOBBER_ABI_KW,
"default" => DEFAULT_KW,
@@ -1112,6 +1119,7 @@ macro_rules ! T_ {
[asm] => { $ crate :: SyntaxKind :: ASM_KW };
[att_syntax] => { $ crate :: SyntaxKind :: ATT_SYNTAX_KW };
[auto] => { $ crate :: SyntaxKind :: AUTO_KW };
+ [bikeshed] => { $ crate :: SyntaxKind :: BIKESHED_KW };
[builtin] => { $ crate :: SyntaxKind :: BUILTIN_KW };
[clobber_abi] => { $ crate :: SyntaxKind :: CLOBBER_ABI_KW };
[default] => { $ crate :: SyntaxKind :: DEFAULT_KW };
diff --git a/crates/parser/test_data/parser/err/0042_weird_blocks.rast b/crates/parser/test_data/parser/err/0042_weird_blocks.rast
index d6d2e75cca..9e4e9dbf9d 100644
--- a/crates/parser/test_data/parser/err/0042_weird_blocks.rast
+++ b/crates/parser/test_data/parser/err/0042_weird_blocks.rast
@@ -45,7 +45,8 @@ SOURCE_FILE
WHITESPACE " "
EXPR_STMT
BLOCK_EXPR
- TRY_KW "try"
+ TRY_BLOCK_MODIFIER
+ TRY_KW "try"
WHITESPACE " "
LITERAL
INT_NUMBER "92"
diff --git a/crates/parser/test_data/parser/inline/ok/try_block_expr.rast b/crates/parser/test_data/parser/inline/ok/try_block_expr.rast
index aec8fbf477..472ce711c5 100644
--- a/crates/parser/test_data/parser/inline/ok/try_block_expr.rast
+++ b/crates/parser/test_data/parser/inline/ok/try_block_expr.rast
@@ -21,7 +21,42 @@ SOURCE_FILE
EQ "="
WHITESPACE " "
BLOCK_EXPR
- TRY_KW "try"
+ TRY_BLOCK_MODIFIER
+ TRY_KW "try"
+ WHITESPACE " "
+ STMT_LIST
+ L_CURLY "{"
+ R_CURLY "}"
+ SEMICOLON ";"
+ WHITESPACE "\n "
+ LET_STMT
+ LET_KW "let"
+ WHITESPACE " "
+ WILDCARD_PAT
+ UNDERSCORE "_"
+ WHITESPACE " "
+ EQ "="
+ WHITESPACE " "
+ BLOCK_EXPR
+ TRY_BLOCK_MODIFIER
+ TRY_KW "try"
+ WHITESPACE " "
+ BIKESHED_KW "bikeshed"
+ WHITESPACE " "
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "T"
+ GENERIC_ARG_LIST
+ L_ANGLE "<"
+ TYPE_ARG
+ PATH_TYPE
+ PATH
+ PATH_SEGMENT
+ NAME_REF
+ IDENT "U"
+ R_ANGLE ">"
WHITESPACE " "
STMT_LIST
L_CURLY "{"
diff --git a/crates/parser/test_data/parser/inline/ok/try_block_expr.rs b/crates/parser/test_data/parser/inline/ok/try_block_expr.rs
index 0f1b41eb64..719980473c 100644
--- a/crates/parser/test_data/parser/inline/ok/try_block_expr.rs
+++ b/crates/parser/test_data/parser/inline/ok/try_block_expr.rs
@@ -1,3 +1,4 @@
fn foo() {
let _ = try {};
+ let _ = try bikeshed T<U> {};
}
diff --git a/crates/proc-macro-srv/src/dylib/proc_macros.rs b/crates/proc-macro-srv/src/dylib/proc_macros.rs
index 76c5097101..4065dbd0b4 100644
--- a/crates/proc-macro-srv/src/dylib/proc_macros.rs
+++ b/crates/proc-macro-srv/src/dylib/proc_macros.rs
@@ -30,7 +30,7 @@ impl ProcMacros {
if *trait_name == macro_name =>
{
let res = client.run(
- &bridge::server::SameThread,
+ &bridge::server::SAME_THREAD,
S::make_server(call_site, def_site, mixed_site, callback),
macro_body,
cfg!(debug_assertions),
@@ -39,7 +39,7 @@ impl ProcMacros {
}
bridge::client::ProcMacro::Bang { name, client } if *name == macro_name => {
let res = client.run(
- &bridge::server::SameThread,
+ &bridge::server::SAME_THREAD,
S::make_server(call_site, def_site, mixed_site, callback),
macro_body,
cfg!(debug_assertions),
@@ -48,7 +48,7 @@ impl ProcMacros {
}
bridge::client::ProcMacro::Attr { name, client } if *name == macro_name => {
let res = client.run(
- &bridge::server::SameThread,
+ &bridge::server::SAME_THREAD,
S::make_server(call_site, def_site, mixed_site, callback),
parsed_attributes,
macro_body,
diff --git a/crates/project-model/src/sysroot.rs b/crates/project-model/src/sysroot.rs
index f244c9736c..546a1e05a0 100644
--- a/crates/project-model/src/sysroot.rs
+++ b/crates/project-model/src/sysroot.rs
@@ -275,7 +275,10 @@ impl Sysroot {
}
tracing::debug!("Stitching sysroot library: {src_root}");
- let mut stitched = stitched::Stitched { crates: Default::default() };
+ let mut stitched = stitched::Stitched {
+ crates: Default::default(),
+ edition: span::Edition::Edition2024,
+ };
for path in stitched::SYSROOT_CRATES.trim().lines() {
let name = path.split('/').next_back().unwrap();
@@ -511,6 +514,7 @@ pub(crate) mod stitched {
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Stitched {
pub(super) crates: Arena<RustLibSrcCrateData>,
+ pub(crate) edition: span::Edition,
}
impl ops::Index<RustLibSrcCrate> for Stitched {
diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs
index 8f15f7e150..581b5fa514 100644
--- a/crates/project-model/src/workspace.rs
+++ b/crates/project-model/src/workspace.rs
@@ -1831,7 +1831,7 @@ fn sysroot_to_crate_graph(
let display_name = CrateDisplayName::from_canonical_name(&stitched[krate].name);
let crate_id = crate_graph.add_crate_root(
file_id,
- Edition::CURRENT_FIXME,
+ stitched.edition,
Some(display_name),
None,
cfg_options.clone(),
diff --git a/crates/rust-analyzer/src/flycheck.rs b/crates/rust-analyzer/src/flycheck.rs
index 512c231990..c74f4550fd 100644
--- a/crates/rust-analyzer/src/flycheck.rs
+++ b/crates/rust-analyzer/src/flycheck.rs
@@ -22,7 +22,6 @@ use serde_derive::Deserialize;
pub(crate) use cargo_metadata::diagnostic::{
Applicability, Diagnostic, DiagnosticCode, DiagnosticLevel, DiagnosticSpan,
};
-use toolchain::DISPLAY_COMMAND_IGNORE_ENVS;
use toolchain::Tool;
use triomphe::Arc;
@@ -144,6 +143,7 @@ impl FlycheckConfig {
}
impl fmt::Display for FlycheckConfig {
+ /// Show a shortened version of the check command.
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FlycheckConfig::Automatic { cargo_options, .. } => {
@@ -153,12 +153,23 @@ impl fmt::Display for FlycheckConfig {
// Don't show `my_custom_check --foo $saved_file` literally to the user, as it
// looks like we've forgotten to substitute $saved_file.
//
+ // `my_custom_check --foo /home/user/project/src/dir/foo.rs` is too verbose.
+ //
// Instead, show `my_custom_check --foo ...`. The
// actual path is often too long to be worth showing
// in the IDE (e.g. in the VS Code status bar).
let display_args = args
.iter()
- .map(|arg| if arg == SAVED_FILE_PLACEHOLDER_DOLLAR { "..." } else { arg })
+ .map(|arg| {
+ if (arg == SAVED_FILE_PLACEHOLDER_DOLLAR)
+ || (arg == SAVED_FILE_INLINE)
+ || arg.ends_with(".rs")
+ {
+ "..."
+ } else {
+ arg
+ }
+ })
.collect::<Vec<_>>();
write!(f, "{command} {}", display_args.join(" "))
@@ -403,24 +414,30 @@ struct FlycheckActor {
/// doesn't provide a way to read sub-process output without blocking, so we
/// have to wrap sub-processes output handling in a thread and pass messages
/// back over a channel.
- command_handle: Option<CommandHandle<CargoCheckMessage>>,
+ command_handle: Option<CommandHandle<CheckMessage>>,
/// The receiver side of the channel mentioned above.
- command_receiver: Option<Receiver<CargoCheckMessage>>,
+ command_receiver: Option<Receiver<CheckMessage>>,
diagnostics_cleared_for: FxHashSet<PackageSpecifier>,
diagnostics_received: DiagnosticsReceived,
}
-#[derive(PartialEq)]
+#[derive(PartialEq, Debug)]
enum DiagnosticsReceived {
- Yes,
- No,
- YesAndClearedForAll,
+ /// We started a flycheck, but we haven't seen any diagnostics yet.
+ NotYet,
+ /// We received a non-zero number of diagnostics from rustc or clippy (via
+ /// cargo or custom check command). This means there were errors or
+ /// warnings.
+ AtLeastOne,
+ /// We received a non-zero number of diagnostics, and the scope is
+ /// workspace, so we've discarded the previous workspace diagnostics.
+ AtLeastOneAndClearedWorkspace,
}
#[allow(clippy::large_enum_variant)]
enum Event {
RequestStateChange(StateChange),
- CheckEvent(Option<CargoCheckMessage>),
+ CheckEvent(Option<CheckMessage>),
}
/// This is stable behaviour. Don't change.
@@ -511,7 +528,7 @@ impl FlycheckActor {
command_handle: None,
command_receiver: None,
diagnostics_cleared_for: Default::default(),
- diagnostics_received: DiagnosticsReceived::No,
+ diagnostics_received: DiagnosticsReceived::NotYet,
}
}
@@ -563,23 +580,13 @@ impl FlycheckActor {
};
let debug_command = format!("{command:?}");
- let user_facing_command = match origin {
- // Don't show all the --format=json-with-blah-blah args, just the simple
- // version
- FlycheckCommandOrigin::Cargo => self.config.to_string(),
- // show them the full command but pretty printed. advanced user
- FlycheckCommandOrigin::ProjectJsonRunnable
- | FlycheckCommandOrigin::CheckOverrideCommand => display_command(
- &command,
- Some(std::path::Path::new(self.root.as_path())),
- ),
- };
+ let user_facing_command = self.config.to_string();
tracing::debug!(?origin, ?command, "will restart flycheck");
let (sender, receiver) = unbounded();
match CommandHandle::spawn(
command,
- CargoCheckParser,
+ CheckParser,
sender,
match &self.config {
FlycheckConfig::Automatic { cargo_options, .. } => {
@@ -640,7 +647,7 @@ impl FlycheckActor {
error
);
}
- if self.diagnostics_received == DiagnosticsReceived::No {
+ if self.diagnostics_received == DiagnosticsReceived::NotYet {
tracing::trace!(flycheck_id = self.id, "clearing diagnostics");
// We finished without receiving any diagnostics.
// Clear everything for good measure
@@ -699,7 +706,7 @@ impl FlycheckActor {
self.report_progress(Progress::DidFinish(res));
}
Event::CheckEvent(Some(message)) => match message {
- CargoCheckMessage::CompilerArtifact(msg) => {
+ CheckMessage::CompilerArtifact(msg) => {
tracing::trace!(
flycheck_id = self.id,
artifact = msg.target.name,
@@ -729,46 +736,75 @@ impl FlycheckActor {
});
}
}
- CargoCheckMessage::Diagnostic { diagnostic, package_id } => {
+ CheckMessage::Diagnostic { diagnostic, package_id } => {
tracing::trace!(
flycheck_id = self.id,
message = diagnostic.message,
package_id = package_id.as_ref().map(|it| it.as_str()),
+ scope = ?self.scope,
"diagnostic received"
);
- if self.diagnostics_received == DiagnosticsReceived::No {
- self.diagnostics_received = DiagnosticsReceived::Yes;
- }
- if let Some(package_id) = &package_id {
- if self.diagnostics_cleared_for.insert(package_id.clone()) {
- tracing::trace!(
- flycheck_id = self.id,
- package_id = package_id.as_str(),
- "clearing diagnostics"
- );
- self.send(FlycheckMessage::ClearDiagnostics {
+
+ match &self.scope {
+ FlycheckScope::Workspace => {
+ if self.diagnostics_received == DiagnosticsReceived::NotYet {
+ self.send(FlycheckMessage::ClearDiagnostics {
+ id: self.id,
+ kind: ClearDiagnosticsKind::All(ClearScope::Workspace),
+ });
+
+ self.diagnostics_received =
+ DiagnosticsReceived::AtLeastOneAndClearedWorkspace;
+ }
+
+ if let Some(package_id) = package_id {
+ tracing::warn!(
+ "Ignoring package label {:?} and applying diagnostics to the whole workspace",
+ package_id
+ );
+ }
+
+ self.send(FlycheckMessage::AddDiagnostic {
id: self.id,
- kind: ClearDiagnosticsKind::All(ClearScope::Package(
- package_id.clone(),
- )),
+ generation: self.generation,
+ package_id: None,
+ workspace_root: self.root.clone(),
+ diagnostic,
+ });
+ }
+ FlycheckScope::Package { package: flycheck_package, .. } => {
+ if self.diagnostics_received == DiagnosticsReceived::NotYet {
+ self.diagnostics_received = DiagnosticsReceived::AtLeastOne;
+ }
+
+ // If the package has been set in the diagnostic JSON, respect that. Otherwise, use the
+ // package that the current flycheck is scoped to. This is useful when a project is
+ // directly using rustc for its checks (e.g. custom check commands in rust-project.json).
+ let package_id = package_id.unwrap_or(flycheck_package.clone());
+
+ if self.diagnostics_cleared_for.insert(package_id.clone()) {
+ tracing::trace!(
+ flycheck_id = self.id,
+ package_id = package_id.as_str(),
+ "clearing diagnostics"
+ );
+ self.send(FlycheckMessage::ClearDiagnostics {
+ id: self.id,
+ kind: ClearDiagnosticsKind::All(ClearScope::Package(
+ package_id.clone(),
+ )),
+ });
+ }
+
+ self.send(FlycheckMessage::AddDiagnostic {
+ id: self.id,
+ generation: self.generation,
+ package_id: Some(package_id),
+ workspace_root: self.root.clone(),
+ diagnostic,
});
}
- } else if self.diagnostics_received
- != DiagnosticsReceived::YesAndClearedForAll
- {
- self.diagnostics_received = DiagnosticsReceived::YesAndClearedForAll;
- self.send(FlycheckMessage::ClearDiagnostics {
- id: self.id,
- kind: ClearDiagnosticsKind::All(ClearScope::Workspace),
- });
}
- self.send(FlycheckMessage::AddDiagnostic {
- id: self.id,
- generation: self.generation,
- package_id,
- workspace_root: self.root.clone(),
- diagnostic,
- });
}
},
}
@@ -792,7 +828,7 @@ impl FlycheckActor {
fn clear_diagnostics_state(&mut self) {
self.diagnostics_cleared_for.clear();
- self.diagnostics_received = DiagnosticsReceived::No;
+ self.diagnostics_received = DiagnosticsReceived::NotYet;
}
fn explicit_check_command(
@@ -942,15 +978,18 @@ impl FlycheckActor {
}
#[allow(clippy::large_enum_variant)]
-enum CargoCheckMessage {
+enum CheckMessage {
+ /// A message from `cargo check`, including details like the path
+ /// to the relevant `Cargo.toml`.
CompilerArtifact(cargo_metadata::Artifact),
+ /// A diagnostic message from rustc itself.
Diagnostic { diagnostic: Diagnostic, package_id: Option<PackageSpecifier> },
}
-struct CargoCheckParser;
+struct CheckParser;
-impl JsonLinesParser<CargoCheckMessage> for CargoCheckParser {
- fn from_line(&self, line: &str, error: &mut String) -> Option<CargoCheckMessage> {
+impl JsonLinesParser<CheckMessage> for CheckParser {
+ fn from_line(&self, line: &str, error: &mut String) -> Option<CheckMessage> {
let mut deserializer = serde_json::Deserializer::from_str(line);
deserializer.disable_recursion_limit();
if let Ok(message) = JsonMessage::deserialize(&mut deserializer) {
@@ -958,10 +997,10 @@ impl JsonLinesParser<CargoCheckMessage> for CargoCheckParser {
// Skip certain kinds of messages to only spend time on what's useful
JsonMessage::Cargo(message) => match message {
cargo_metadata::Message::CompilerArtifact(artifact) if !artifact.fresh => {
- Some(CargoCheckMessage::CompilerArtifact(artifact))
+ Some(CheckMessage::CompilerArtifact(artifact))
}
cargo_metadata::Message::CompilerMessage(msg) => {
- Some(CargoCheckMessage::Diagnostic {
+ Some(CheckMessage::Diagnostic {
diagnostic: msg.message,
package_id: Some(PackageSpecifier::Cargo {
package_id: Arc::new(msg.package_id),
@@ -971,7 +1010,7 @@ impl JsonLinesParser<CargoCheckMessage> for CargoCheckParser {
_ => None,
},
JsonMessage::Rustc(message) => {
- Some(CargoCheckMessage::Diagnostic { diagnostic: message, package_id: None })
+ Some(CheckMessage::Diagnostic { diagnostic: message, package_id: None })
}
};
}
@@ -981,7 +1020,7 @@ impl JsonLinesParser<CargoCheckMessage> for CargoCheckParser {
None
}
- fn from_eof(&self) -> Option<CargoCheckMessage> {
+ fn from_eof(&self) -> Option<CheckMessage> {
None
}
}
@@ -993,64 +1032,14 @@ enum JsonMessage {
Rustc(Diagnostic),
}
-/// Not good enough to execute in a shell, but good enough to show the user without all the noisy
-/// quotes
-///
-/// Pass implicit_cwd if there is one regarded as the obvious by the user, so we can skip showing it.
-/// Compactness is the aim of the game, the output typically gets truncated quite a lot.
-fn display_command(c: &Command, implicit_cwd: Option<&std::path::Path>) -> String {
- let mut o = String::new();
- use std::fmt::Write;
- let lossy = std::ffi::OsStr::to_string_lossy;
- if let Some(dir) = c.get_current_dir() {
- if Some(dir) == implicit_cwd.map(std::path::Path::new) {
- // pass
- } else if dir.to_string_lossy().contains(" ") {
- write!(o, "cd {:?} && ", dir).unwrap();
- } else {
- write!(o, "cd {} && ", dir.display()).unwrap();
- }
- }
- for (env, val) in c.get_envs() {
- let (env, val) = (lossy(env), val.map(lossy).unwrap_or(std::borrow::Cow::Borrowed("")));
- if DISPLAY_COMMAND_IGNORE_ENVS.contains(&env.as_ref()) {
- continue;
- }
- if env.contains(" ") {
- write!(o, "\"{}={}\" ", env, val).unwrap();
- } else if val.contains(" ") {
- write!(o, "{}=\"{}\" ", env, val).unwrap();
- } else {
- write!(o, "{}={} ", env, val).unwrap();
- }
- }
- let prog = lossy(c.get_program());
- if prog.contains(" ") {
- write!(o, "{:?}", prog).unwrap();
- } else {
- write!(o, "{}", prog).unwrap();
- }
- for arg in c.get_args() {
- let arg = lossy(arg);
- if arg.contains(" ") {
- write!(o, " \"{}\"", arg).unwrap();
- } else {
- write!(o, " {}", arg).unwrap();
- }
- }
- o
-}
-
#[cfg(test)]
mod tests {
+ use super::*;
use ide_db::FxHashMap;
use itertools::Itertools;
use paths::Utf8Path;
use project_model::project_json;
- use crate::flycheck::Substitutions;
- use crate::flycheck::display_command;
-
#[test]
fn test_substitutions() {
let label = ":label";
@@ -1139,34 +1128,47 @@ mod tests {
}
#[test]
- fn test_display_command() {
- use std::path::Path;
- let workdir = Path::new("workdir");
- let mut cmd = toolchain::command("command", workdir, &FxHashMap::default());
- assert_eq!(display_command(cmd.arg("--arg"), Some(workdir)), "command --arg");
- assert_eq!(
- display_command(cmd.arg("spaced arg"), Some(workdir)),
- "command --arg \"spaced arg\""
- );
- assert_eq!(
- display_command(cmd.env("ENVIRON", "yeah"), Some(workdir)),
- "ENVIRON=yeah command --arg \"spaced arg\""
- );
- assert_eq!(
- display_command(cmd.env("OTHER", "spaced env"), Some(workdir)),
- "ENVIRON=yeah OTHER=\"spaced env\" command --arg \"spaced arg\""
- );
- assert_eq!(
- display_command(cmd.current_dir("/tmp"), Some(workdir)),
- "cd /tmp && ENVIRON=yeah OTHER=\"spaced env\" command --arg \"spaced arg\""
- );
- assert_eq!(
- display_command(cmd.current_dir("/tmp and/thing"), Some(workdir)),
- "cd \"/tmp and/thing\" && ENVIRON=yeah OTHER=\"spaced env\" command --arg \"spaced arg\""
- );
- assert_eq!(
- display_command(cmd.current_dir("/tmp and/thing"), Some(Path::new("/tmp and/thing"))),
- "ENVIRON=yeah OTHER=\"spaced env\" command --arg \"spaced arg\""
- );
+ fn test_flycheck_config_display() {
+ let clippy = FlycheckConfig::Automatic {
+ cargo_options: CargoOptions {
+ subcommand: "clippy".to_owned(),
+ target_tuples: vec![],
+ all_targets: false,
+ set_test: false,
+ no_default_features: false,
+ all_features: false,
+ features: vec![],
+ extra_args: vec![],
+ extra_test_bin_args: vec![],
+ extra_env: FxHashMap::default(),
+ target_dir_config: TargetDirectoryConfig::default(),
+ },
+ ansi_color_output: true,
+ };
+ assert_eq!(clippy.to_string(), "cargo clippy");
+
+ let custom_dollar = FlycheckConfig::CustomCommand {
+ command: "check".to_owned(),
+ args: vec!["--input".to_owned(), "$saved_file".to_owned()],
+ extra_env: FxHashMap::default(),
+ invocation_strategy: InvocationStrategy::Once,
+ };
+ assert_eq!(custom_dollar.to_string(), "check --input ...");
+
+ let custom_inline = FlycheckConfig::CustomCommand {
+ command: "check".to_owned(),
+ args: vec!["--input".to_owned(), "{saved_file}".to_owned()],
+ extra_env: FxHashMap::default(),
+ invocation_strategy: InvocationStrategy::Once,
+ };
+ assert_eq!(custom_inline.to_string(), "check --input ...");
+
+ let custom_rs = FlycheckConfig::CustomCommand {
+ command: "check".to_owned(),
+ args: vec!["--input".to_owned(), "/path/to/file.rs".to_owned()],
+ extra_env: FxHashMap::default(),
+ invocation_strategy: InvocationStrategy::Once,
+ };
+ assert_eq!(custom_rs.to_string(), "check --input ...");
}
}
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index afd4162de6..1462727df4 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -14,7 +14,7 @@ use hir::ChangeWithProcMacros;
use ide::{Analysis, AnalysisHost, Cancellable, FileId, SourceRootId};
use ide_db::{
MiniCore,
- base_db::{Crate, ProcMacroPaths, SourceDatabase, salsa::Revision},
+ base_db::{Crate, ProcMacroPaths, SourceDatabase, salsa::CancellationToken, salsa::Revision},
};
use itertools::Itertools;
use load_cargo::SourceRootConfig;
@@ -88,6 +88,7 @@ pub(crate) struct GlobalState {
pub(crate) task_pool: Handle<TaskPool<Task>, Receiver<Task>>,
pub(crate) fmt_pool: Handle<TaskPool<Task>, Receiver<Task>>,
pub(crate) cancellation_pool: thread::Pool,
+ pub(crate) cancellation_tokens: FxHashMap<lsp_server::RequestId, CancellationToken>,
pub(crate) config: Arc<Config>,
pub(crate) config_errors: Option<ConfigErrors>,
@@ -265,6 +266,7 @@ impl GlobalState {
task_pool,
fmt_pool,
cancellation_pool,
+ cancellation_tokens: Default::default(),
loader,
config: Arc::new(config.clone()),
analysis_host,
@@ -617,6 +619,7 @@ impl GlobalState {
}
pub(crate) fn respond(&mut self, response: lsp_server::Response) {
+ self.cancellation_tokens.remove(&response.id);
if let Some((method, start)) = self.req_queue.incoming.complete(&response.id) {
if let Some(err) = &response.error
&& err.message.starts_with("server panicked")
@@ -631,6 +634,9 @@ impl GlobalState {
}
pub(crate) fn cancel(&mut self, request_id: lsp_server::RequestId) {
+ if let Some(token) = self.cancellation_tokens.remove(&request_id) {
+ token.cancel();
+ }
if let Some(response) = self.req_queue.incoming.cancel(request_id) {
self.send(response.into());
}
diff --git a/crates/rust-analyzer/src/handlers/dispatch.rs b/crates/rust-analyzer/src/handlers/dispatch.rs
index 90deae2d90..63b4e6430c 100644
--- a/crates/rust-analyzer/src/handlers/dispatch.rs
+++ b/crates/rust-analyzer/src/handlers/dispatch.rs
@@ -253,6 +253,9 @@ impl RequestDispatcher<'_> {
tracing::debug!(?params);
let world = self.global_state.snapshot();
+ self.global_state
+ .cancellation_tokens
+ .insert(req.id.clone(), world.analysis.cancellation_token());
if RUSTFMT {
&mut self.global_state.fmt_pool.handle
} else {
@@ -265,7 +268,19 @@ impl RequestDispatcher<'_> {
});
match thread_result_to_response::<R>(req.id.clone(), result) {
Ok(response) => Task::Response(response),
- Err(_cancelled) if ALLOW_RETRYING => Task::Retry(req),
+ Err(HandlerCancelledError::Inner(
+ Cancelled::PendingWrite | Cancelled::PropagatedPanic,
+ )) if ALLOW_RETRYING => Task::Retry(req),
+ // Note: Technically the return value here does not matter as we have already responded to the client with this error.
+ Err(HandlerCancelledError::Inner(Cancelled::Local)) => Task::Response(Response {
+ id: req.id,
+ result: None,
+ error: Some(ResponseError {
+ code: lsp_server::ErrorCode::RequestCanceled as i32,
+ message: "canceled by client".to_owned(),
+ data: None,
+ }),
+ }),
Err(_cancelled) => {
let error = on_cancelled();
Task::Response(Response { id: req.id, result: None, error: Some(error) })
diff --git a/crates/span/src/hygiene.rs b/crates/span/src/hygiene.rs
index fe05ef9465..0a81cef52e 100644
--- a/crates/span/src/hygiene.rs
+++ b/crates/span/src/hygiene.rs
@@ -81,25 +81,24 @@ const _: () = {
#[derive(Hash)]
struct StructKey<'db, T0, T1, T2, T3>(T0, T1, T2, T3, std::marker::PhantomData<&'db ()>);
- impl<'db, T0, T1, T2, T3> zalsa_::interned::HashEqLike<StructKey<'db, T0, T1, T2, T3>>
- for SyntaxContextData
+ impl<'db, T0, T1, T2, T3> zalsa_::HashEqLike<StructKey<'db, T0, T1, T2, T3>> for SyntaxContextData
where
- Option<MacroCallId>: zalsa_::interned::HashEqLike<T0>,
- Transparency: zalsa_::interned::HashEqLike<T1>,
- Edition: zalsa_::interned::HashEqLike<T2>,
- SyntaxContext: zalsa_::interned::HashEqLike<T3>,
+ Option<MacroCallId>: zalsa_::HashEqLike<T0>,
+ Transparency: zalsa_::HashEqLike<T1>,
+ Edition: zalsa_::HashEqLike<T2>,
+ SyntaxContext: zalsa_::HashEqLike<T3>,
{
fn hash<H: std::hash::Hasher>(&self, h: &mut H) {
- zalsa_::interned::HashEqLike::<T0>::hash(&self.outer_expn, &mut *h);
- zalsa_::interned::HashEqLike::<T1>::hash(&self.outer_transparency, &mut *h);
- zalsa_::interned::HashEqLike::<T2>::hash(&self.edition, &mut *h);
- zalsa_::interned::HashEqLike::<T3>::hash(&self.parent, &mut *h);
+ zalsa_::HashEqLike::<T0>::hash(&self.outer_expn, &mut *h);
+ zalsa_::HashEqLike::<T1>::hash(&self.outer_transparency, &mut *h);
+ zalsa_::HashEqLike::<T2>::hash(&self.edition, &mut *h);
+ zalsa_::HashEqLike::<T3>::hash(&self.parent, &mut *h);
}
fn eq(&self, data: &StructKey<'db, T0, T1, T2, T3>) -> bool {
- zalsa_::interned::HashEqLike::<T0>::eq(&self.outer_expn, &data.0)
- && zalsa_::interned::HashEqLike::<T1>::eq(&self.outer_transparency, &data.1)
- && zalsa_::interned::HashEqLike::<T2>::eq(&self.edition, &data.2)
- && zalsa_::interned::HashEqLike::<T3>::eq(&self.parent, &data.3)
+ zalsa_::HashEqLike::<T0>::eq(&self.outer_expn, &data.0)
+ && zalsa_::HashEqLike::<T1>::eq(&self.outer_transparency, &data.1)
+ && zalsa_::HashEqLike::<T2>::eq(&self.edition, &data.2)
+ && zalsa_::HashEqLike::<T3>::eq(&self.parent, &data.3)
}
}
impl zalsa_struct_::Configuration for SyntaxContext {
@@ -203,10 +202,10 @@ const _: () = {
impl<'db> SyntaxContext {
pub fn new<
Db,
- T0: zalsa_::interned::Lookup<Option<MacroCallId>> + std::hash::Hash,
- T1: zalsa_::interned::Lookup<Transparency> + std::hash::Hash,
- T2: zalsa_::interned::Lookup<Edition> + std::hash::Hash,
- T3: zalsa_::interned::Lookup<SyntaxContext> + std::hash::Hash,
+ T0: zalsa_::Lookup<Option<MacroCallId>> + std::hash::Hash,
+ T1: zalsa_::Lookup<Transparency> + std::hash::Hash,
+ T2: zalsa_::Lookup<Edition> + std::hash::Hash,
+ T3: zalsa_::Lookup<SyntaxContext> + std::hash::Hash,
>(
db: &'db Db,
outer_expn: T0,
@@ -218,10 +217,10 @@ const _: () = {
) -> Self
where
Db: ?Sized + salsa::Database,
- Option<MacroCallId>: zalsa_::interned::HashEqLike<T0>,
- Transparency: zalsa_::interned::HashEqLike<T1>,
- Edition: zalsa_::interned::HashEqLike<T2>,
- SyntaxContext: zalsa_::interned::HashEqLike<T3>,
+ Option<MacroCallId>: zalsa_::HashEqLike<T0>,
+ Transparency: zalsa_::HashEqLike<T1>,
+ Edition: zalsa_::HashEqLike<T2>,
+ SyntaxContext: zalsa_::HashEqLike<T3>,
{
let (zalsa, zalsa_local) = db.zalsas();
@@ -236,10 +235,10 @@ const _: () = {
std::marker::PhantomData,
),
|id, data| SyntaxContextData {
- outer_expn: zalsa_::interned::Lookup::into_owned(data.0),
- outer_transparency: zalsa_::interned::Lookup::into_owned(data.1),
- edition: zalsa_::interned::Lookup::into_owned(data.2),
- parent: zalsa_::interned::Lookup::into_owned(data.3),
+ outer_expn: zalsa_::Lookup::into_owned(data.0),
+ outer_transparency: zalsa_::Lookup::into_owned(data.1),
+ edition: zalsa_::Lookup::into_owned(data.2),
+ parent: zalsa_::Lookup::into_owned(data.3),
opaque: opaque(zalsa_::FromId::from_id(id)),
opaque_and_semiopaque: opaque_and_semiopaque(zalsa_::FromId::from_id(id)),
},
diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram
index 991fe7d83a..544053408f 100644
--- a/crates/syntax/rust.ungram
+++ b/crates/syntax/rust.ungram
@@ -472,8 +472,11 @@ RefExpr =
TryExpr =
Attr* Expr '?'
+TryBlockModifier =
+ 'try' ('bikeshed' Type)?
+
BlockExpr =
- Attr* Label? ('try' | 'unsafe' | ('async' 'move'?) | ('gen' 'move'?) | 'const') StmtList
+ Attr* Label? (TryBlockModifier | 'unsafe' | ('async' 'move'?) | ('gen' 'move'?) | 'const') StmtList
PrefixExpr =
Attr* op:('-' | '!' | '*') Expr
diff --git a/crates/syntax/src/ast/expr_ext.rs b/crates/syntax/src/ast/expr_ext.rs
index db66995381..b44150f868 100644
--- a/crates/syntax/src/ast/expr_ext.rs
+++ b/crates/syntax/src/ast/expr_ext.rs
@@ -375,7 +375,11 @@ impl ast::Literal {
pub enum BlockModifier {
Async(SyntaxToken),
Unsafe(SyntaxToken),
- Try(SyntaxToken),
+ Try {
+ try_token: SyntaxToken,
+ bikeshed_token: Option<SyntaxToken>,
+ result_type: Option<ast::Type>,
+ },
Const(SyntaxToken),
AsyncGen(SyntaxToken),
Gen(SyntaxToken),
@@ -394,7 +398,13 @@ impl ast::BlockExpr {
})
.or_else(|| self.async_token().map(BlockModifier::Async))
.or_else(|| self.unsafe_token().map(BlockModifier::Unsafe))
- .or_else(|| self.try_token().map(BlockModifier::Try))
+ .or_else(|| {
+ let modifier = self.try_block_modifier()?;
+ let try_token = modifier.try_token()?;
+ let bikeshed_token = modifier.bikeshed_token();
+ let result_type = modifier.ty();
+ Some(BlockModifier::Try { try_token, bikeshed_token, result_type })
+ })
.or_else(|| self.const_token().map(BlockModifier::Const))
.or_else(|| self.label().map(BlockModifier::Label))
}
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs
index 7b9f5b9166..c4e72eafa7 100644
--- a/crates/syntax/src/ast/generated/nodes.rs
+++ b/crates/syntax/src/ast/generated/nodes.rs
@@ -323,6 +323,8 @@ impl BlockExpr {
#[inline]
pub fn stmt_list(&self) -> Option<StmtList> { support::child(&self.syntax) }
#[inline]
+ pub fn try_block_modifier(&self) -> Option<TryBlockModifier> { support::child(&self.syntax) }
+ #[inline]
pub fn async_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![async]) }
#[inline]
pub fn const_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![const]) }
@@ -331,8 +333,6 @@ impl BlockExpr {
#[inline]
pub fn move_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![move]) }
#[inline]
- pub fn try_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![try]) }
- #[inline]
pub fn unsafe_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![unsafe]) }
}
pub struct BoxPat {
@@ -1630,6 +1630,19 @@ impl Trait {
#[inline]
pub fn unsafe_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![unsafe]) }
}
+pub struct TryBlockModifier {
+ pub(crate) syntax: SyntaxNode,
+}
+impl TryBlockModifier {
+ #[inline]
+ pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) }
+ #[inline]
+ pub fn bikeshed_token(&self) -> Option<SyntaxToken> {
+ support::token(&self.syntax, T![bikeshed])
+ }
+ #[inline]
+ pub fn try_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![try]) }
+}
pub struct TryExpr {
pub(crate) syntax: SyntaxNode,
}
@@ -6320,6 +6333,38 @@ impl fmt::Debug for Trait {
f.debug_struct("Trait").field("syntax", &self.syntax).finish()
}
}
+impl AstNode for TryBlockModifier {
+ #[inline]
+ fn kind() -> SyntaxKind
+ where
+ Self: Sized,
+ {
+ TRY_BLOCK_MODIFIER
+ }
+ #[inline]
+ fn can_cast(kind: SyntaxKind) -> bool { kind == TRY_BLOCK_MODIFIER }
+ #[inline]
+ fn cast(syntax: SyntaxNode) -> Option<Self> {
+ if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
+ }
+ #[inline]
+ fn syntax(&self) -> &SyntaxNode { &self.syntax }
+}
+impl hash::Hash for TryBlockModifier {
+ fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); }
+}
+impl Eq for TryBlockModifier {}
+impl PartialEq for TryBlockModifier {
+ fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax }
+}
+impl Clone for TryBlockModifier {
+ fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } }
+}
+impl fmt::Debug for TryBlockModifier {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("TryBlockModifier").field("syntax", &self.syntax).finish()
+ }
+}
impl AstNode for TryExpr {
#[inline]
fn kind() -> SyntaxKind
@@ -9979,6 +10024,11 @@ impl std::fmt::Display for Trait {
std::fmt::Display::fmt(self.syntax(), f)
}
}
+impl std::fmt::Display for TryBlockModifier {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ std::fmt::Display::fmt(self.syntax(), f)
+ }
+}
impl std::fmt::Display for TryExpr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
diff --git a/crates/syntax/src/syntax_error.rs b/crates/syntax/src/syntax_error.rs
index 1c902893ab..6f00ef4ed5 100644
--- a/crates/syntax/src/syntax_error.rs
+++ b/crates/syntax/src/syntax_error.rs
@@ -9,16 +9,6 @@ use crate::{TextRange, TextSize};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct SyntaxError(String, TextRange);
-// FIXME: there was an unused SyntaxErrorKind previously (before this enum was removed)
-// It was introduced in this PR: https://github.com/rust-lang/rust-analyzer/pull/846/files#diff-827da9b03b8f9faa1bade5cdd44d5dafR95
-// but it was not removed by a mistake.
-//
-// So, we need to find a place where to stick validation for attributes in match clauses.
-// Code before refactor:
-// InvalidMatchInnerAttr => {
-// write!(f, "Inner attributes are only allowed directly after the opening brace of the match expression")
-// }
-
impl SyntaxError {
pub fn new(message: impl Into<String>, range: TextRange) -> Self {
Self(message.into(), range)
diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs
index 48c3e89525..7d95043867 100644
--- a/crates/test-utils/src/minicore.rs
+++ b/crates/test-utils/src/minicore.rs
@@ -43,6 +43,7 @@
//! dispatch_from_dyn: unsize, pin
//! hash: sized
//! include:
+//! include_bytes:
//! index: sized
//! infallible:
//! int_impl: size_of, transmute
@@ -953,6 +954,9 @@ pub mod ops {
#[lang = "from_residual"]
fn from_residual(residual: R) -> Self;
}
+ pub const trait Residual<O>: Sized {
+ type TryType: [const] Try<Output = O, Residual = Self>;
+ }
#[lang = "Try"]
pub trait Try: FromResidual<Self::Residual> {
type Output;
@@ -962,6 +966,12 @@ pub mod ops {
#[lang = "branch"]
fn branch(self) -> ControlFlow<Self::Residual, Self::Output>;
}
+ #[lang = "into_try_type"]
+ pub const fn residual_into_try_type<R: [const] Residual<O>, O>(
+ r: R,
+ ) -> <R as Residual<O>>::TryType {
+ FromResidual::from_residual(r)
+ }
impl<B, C> Try for ControlFlow<B, C> {
type Output = C;
@@ -985,6 +995,10 @@ pub mod ops {
}
}
}
+
+ impl<B, C> Residual<C> for ControlFlow<B, Infallible> {
+ type TryType = ControlFlow<B, C>;
+ }
// region:option
impl<T> Try for Option<T> {
type Output = T;
@@ -1008,6 +1022,10 @@ pub mod ops {
}
}
}
+
+ impl<T> const Residual<T> for Option<Infallible> {
+ type TryType = Option<T>;
+ }
// endregion:option
// region:result
// region:from
@@ -1037,10 +1055,14 @@ pub mod ops {
}
}
}
+
+ impl<T, E> const Residual<T> for Result<Infallible, E> {
+ type TryType = Result<T, E>;
+ }
// endregion:from
// endregion:result
}
- pub use self::try_::{ControlFlow, FromResidual, Try};
+ pub use self::try_::{ControlFlow, FromResidual, Residual, Try};
// endregion:try
// region:add
@@ -2040,6 +2062,14 @@ mod macros {
}
// endregion:include
+ // region:include_bytes
+ #[rustc_builtin_macro]
+ #[macro_export]
+ macro_rules! include_bytes {
+ ($file:expr $(,)?) => {{ /* compiler built-in */ }};
+ }
+ // endregion:include_bytes
+
// region:concat
#[rustc_builtin_macro]
#[macro_export]
diff --git a/crates/toolchain/src/lib.rs b/crates/toolchain/src/lib.rs
index 1a17269838..39319886cf 100644
--- a/crates/toolchain/src/lib.rs
+++ b/crates/toolchain/src/lib.rs
@@ -74,9 +74,6 @@ impl Tool {
// Prevent rustup from automatically installing toolchains, see https://github.com/rust-lang/rust-analyzer/issues/20719.
pub const NO_RUSTUP_AUTO_INSTALL_ENV: (&str, &str) = ("RUSTUP_AUTO_INSTALL", "0");
-// These get ignored when displaying what command is running in LSP status messages.
-pub const DISPLAY_COMMAND_IGNORE_ENVS: &[&str] = &[NO_RUSTUP_AUTO_INSTALL_ENV.0];
-
#[allow(clippy::disallowed_types)] /* generic parameter allows for FxHashMap */
pub fn command<H>(
cmd: impl AsRef<OsStr>,
diff --git a/docs/book/README.md b/docs/book/README.md
index 0a3161f3af..cd4d8783a4 100644
--- a/docs/book/README.md
+++ b/docs/book/README.md
@@ -6,7 +6,7 @@ The rust analyzer manual uses [mdbook](https://rust-lang.github.io/mdBook/).
To run the documentation site locally:
-```shell
+```bash
cargo install mdbook
cargo xtask codegen
cd docs/book
diff --git a/docs/book/src/contributing/README.md b/docs/book/src/contributing/README.md
index c95a1dba62..bb2b6081ad 100644
--- a/docs/book/src/contributing/README.md
+++ b/docs/book/src/contributing/README.md
@@ -4,7 +4,7 @@ rust-analyzer is an ordinary Rust project, which is organized as a Cargo workspa
So, just
```bash
-$ cargo test
+cargo test
```
should be enough to get you started!
@@ -203,14 +203,14 @@ It is enabled by `RA_COUNT=1`.
To measure time for from-scratch analysis, use something like this:
```bash
-$ cargo run --release -p rust-analyzer -- analysis-stats ../chalk/
+cargo run --release -p rust-analyzer -- analysis-stats ../chalk/
```
For measuring time of incremental analysis, use either of these:
```bash
-$ cargo run --release -p rust-analyzer -- analysis-bench ../chalk/ --highlight ../chalk/chalk-engine/src/logic.rs
-$ cargo run --release -p rust-analyzer -- analysis-bench ../chalk/ --complete ../chalk/chalk-engine/src/logic.rs:94:0
+cargo run --release -p rust-analyzer -- analysis-bench ../chalk/ --highlight ../chalk/chalk-engine/src/logic.rs
+cargo run --release -p rust-analyzer -- analysis-bench ../chalk/ --complete ../chalk/chalk-engine/src/logic.rs:94:0
```
Look for `fn benchmark_xxx` tests for a quick way to reproduce performance problems.
@@ -283,7 +283,8 @@ repository. We use the [rustc-josh-sync](https://github.com/rust-lang/josh-sync)
repositories. You can find documentation of the tool [here](https://github.com/rust-lang/josh-sync).
You can install the synchronization tool using the following commands:
-```
+
+```bash
cargo install --locked --git https://github.com/rust-lang/josh-sync
```
diff --git a/docs/book/src/contributing/debugging.md b/docs/book/src/contributing/debugging.md
index fcda664f5e..ace9be025a 100644
--- a/docs/book/src/contributing/debugging.md
+++ b/docs/book/src/contributing/debugging.md
@@ -68,7 +68,7 @@ while d == 4 { // set a breakpoint here and change the value
However for this to work, you will need to enable debug_assertions in your build
-```rust
+```bash
RUSTFLAGS='--cfg debug_assertions' cargo build --release
```
diff --git a/docs/book/src/installation.md b/docs/book/src/installation.md
index 3a4c0cf227..cc636c31e6 100644
--- a/docs/book/src/installation.md
+++ b/docs/book/src/installation.md
@@ -13,7 +13,9 @@ editor](./other_editors.html).
rust-analyzer will attempt to install the standard library source code
automatically. You can also install it manually with `rustup`.
- $ rustup component add rust-src
+```bash
+rustup component add rust-src
+```
Only the latest stable standard library source is officially supported
for use with rust-analyzer. If you are using an older toolchain or have
diff --git a/docs/book/src/rust_analyzer_binary.md b/docs/book/src/rust_analyzer_binary.md
index c7ac3087ce..2b62011a8e 100644
--- a/docs/book/src/rust_analyzer_binary.md
+++ b/docs/book/src/rust_analyzer_binary.md
@@ -11,9 +11,11 @@ your `$PATH`.
On Linux to install the `rust-analyzer` binary into `~/.local/bin`,
these commands should work:
- $ mkdir -p ~/.local/bin
- $ curl -L https://github.com/rust-lang/rust-analyzer/releases/latest/download/rust-analyzer-x86_64-unknown-linux-gnu.gz | gunzip -c - > ~/.local/bin/rust-analyzer
- $ chmod +x ~/.local/bin/rust-analyzer
+```bash
+mkdir -p ~/.local/bin
+curl -L https://github.com/rust-lang/rust-analyzer/releases/latest/download/rust-analyzer-x86_64-unknown-linux-gnu.gz | gunzip -c - > ~/.local/bin/rust-analyzer
+chmod +x ~/.local/bin/rust-analyzer
+```
Make sure that `~/.local/bin` is listed in the `$PATH` variable and use
the appropriate URL if you’re not on a `x86-64` system.
@@ -24,8 +26,10 @@ or `/usr/local/bin` will work just as well.
Alternatively, you can install it from source using the command below.
You’ll need the latest stable version of the Rust toolchain.
- $ git clone https://github.com/rust-lang/rust-analyzer.git && cd rust-analyzer
- $ cargo xtask install --server
+```bash
+git clone https://github.com/rust-lang/rust-analyzer.git && cd rust-analyzer
+cargo xtask install --server
+```
If your editor can’t find the binary even though the binary is on your
`$PATH`, the likely explanation is that it doesn’t see the same `$PATH`
@@ -38,7 +42,9 @@ the environment should help.
`rust-analyzer` is available in `rustup`:
- $ rustup component add rust-analyzer
+```bash
+rustup component add rust-analyzer
+```
### Arch Linux
@@ -53,7 +59,9 @@ User Repository):
Install it with pacman, for example:
- $ pacman -S rust-analyzer
+```bash
+pacman -S rust-analyzer
+```
### Gentoo Linux
@@ -64,7 +72,9 @@ Install it with pacman, for example:
The `rust-analyzer` binary can be installed via
[Homebrew](https://brew.sh/).
- $ brew install rust-analyzer
+```bash
+brew install rust-analyzer
+```
### Windows
diff --git a/docs/book/src/troubleshooting.md b/docs/book/src/troubleshooting.md
index a357cbef41..c315bfad7f 100644
--- a/docs/book/src/troubleshooting.md
+++ b/docs/book/src/troubleshooting.md
@@ -37,13 +37,13 @@ bypassing LSP machinery.
When filing issues, it is useful (but not necessary) to try to minimize
examples. An ideal bug reproduction looks like this:
-```shell
-$ git clone https://github.com/username/repo.git && cd repo && git switch --detach commit-hash
-$ rust-analyzer --version
+```bash
+git clone https://github.com/username/repo.git && cd repo && git switch --detach commit-hash
+rust-analyzer --version
rust-analyzer dd12184e4 2021-05-08 dev
-$ rust-analyzer analysis-stats .
-💀 💀 💀
+rust-analyzer analysis-stats .
```
+💀 💀 💀
It is especially useful when the `repo` doesn’t use external crates or
the standard library.
diff --git a/docs/book/src/vs_code.md b/docs/book/src/vs_code.md
index 233b862d2c..75f940e69a 100644
--- a/docs/book/src/vs_code.md
+++ b/docs/book/src/vs_code.md
@@ -49,7 +49,9 @@ Alternatively, download a VSIX corresponding to your platform from the
Install the extension with the `Extensions: Install from VSIX` command
within VS Code, or from the command line via:
- $ code --install-extension /path/to/rust-analyzer.vsix
+```bash
+code --install-extension /path/to/rust-analyzer.vsix
+```
If you are running an unsupported platform, you can install
`rust-analyzer-no-server.vsix` and compile or obtain a server binary.
@@ -64,8 +66,10 @@ example:
Both the server and the Code plugin can be installed from source:
- $ git clone https://github.com/rust-lang/rust-analyzer.git && cd rust-analyzer
- $ cargo xtask install
+```bash
+git clone https://github.com/rust-lang/rust-analyzer.git && cd rust-analyzer
+cargo xtask install
+```
You’ll need Cargo, nodejs (matching a supported version of VS Code) and
npm for this.
@@ -76,7 +80,9 @@ Remote, instead you’ll need to install the `.vsix` manually.
If you’re not using Code, you can compile and install only the LSP
server:
- $ cargo xtask install --server
+```bash
+cargo xtask install --server
+```
Make sure that `.cargo/bin` is in `$PATH` and precedes paths where
`rust-analyzer` may also be installed. Specifically, `rustup` includes a
@@ -118,4 +124,3 @@ steps might help:
A C compiler should already be available via `org.freedesktop.Sdk`. Any
other tools or libraries you will need to acquire from Flatpak.
-
diff --git a/xtask/src/codegen/grammar/ast_src.rs b/xtask/src/codegen/grammar/ast_src.rs
index b9f570fe0e..205072a6ce 100644
--- a/xtask/src/codegen/grammar/ast_src.rs
+++ b/xtask/src/codegen/grammar/ast_src.rs
@@ -112,7 +112,7 @@ const RESERVED: &[&str] = &[
// keywords that are keywords only in specific parse contexts
#[doc(alias = "WEAK_KEYWORDS")]
const CONTEXTUAL_KEYWORDS: &[&str] =
- &["macro_rules", "union", "default", "raw", "dyn", "auto", "yeet", "safe"];
+ &["macro_rules", "union", "default", "raw", "dyn", "auto", "yeet", "safe", "bikeshed"];
// keywords we use for special macro expansions
const CONTEXTUAL_BUILTIN_KEYWORDS: &[&str] = &[
"asm",