Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
c5a6131
fix: use `ExprIsRead::Yes` for binary operator operands
Albab-Hasan Feb 16, 2026
85de83b
fix: use check_infer_with_mismatches in binop_lhs_never_place_diverge…
Albab-Hasan Feb 16, 2026
05cd01b
fix: verify NeverToAny adjustment in binop_lhs_never_place_diverges test
Albab-Hasan Feb 16, 2026
c1b0b32
fix: verify NeverToAny adjustment on binop lhs never place
Albab-Hasan Feb 18, 2026
f4dcfa2
fix: offer block `.let` in ref-expr in match arm
A4-Tacks Feb 17, 2026
5ea0716
feat: offer destructure_struct_binding on self param
A4-Tacks Feb 20, 2026
05ab6d1
internal: Configure codecov
Wilfred Feb 17, 2026
cde693c
fix: Improve newline for make `match`, `if`, `while` etc
A4-Tacks Feb 27, 2026
f19a73c
build(deps-dev): bump minimatch from 3.1.2 to 3.1.5 in /editors/code
dependabot[bot] Feb 28, 2026
aa53530
fix: Fix wrong confiditon in `Visibility::min`
Veykril Feb 28, 2026
89573c1
Merge pull request #21725 from Veykril/push-skvpqzkooolm
Veykril Feb 28, 2026
a4c683f
fix: use compound assignment in binop_lhs_never_place_diverges test
Albab-Hasan Feb 28, 2026
ad7acd5
Merge pull request #21654 from Albab-Hasan/fix/binop-rhs-never-coercion
ChayimFriedman2 Feb 28, 2026
747aa5b
Fix: use `FnAbi::Rust` for tuple struct/enum constructors and align `…
Albab-Hasan Mar 1, 2026
a9e35bc
Merge pull request #21722 from rust-lang/dependabot/npm_and_yarn/edit…
lnicola Mar 1, 2026
21674fe
fix: Fix wrong descend range for add_missing_match_arms
A4-Tacks Mar 1, 2026
7a81a8e
Prepare for merging from rust-lang/rust
invalid-email-address Mar 2, 2026
cbbd454
Merge ref 'e7d90c695a39' from rust-lang/rust
invalid-email-address Mar 2, 2026
1d89279
Format code
invalid-email-address Mar 2, 2026
debbabe
Merge pull request #21731 from rust-lang/rustc-pull
lnicola Mar 2, 2026
7bd3c42
internal: Define rules for LLMs
Wilfred Mar 2, 2026
02e1aea
Merge pull request #21728 from A4-Tacks/add-match-arms-descend-range
ChayimFriedman2 Mar 4, 2026
a64732c
Merge pull request #21726 from Albab-Hasan/fix/next-solver-is-rust
ChayimFriedman2 Mar 4, 2026
91c193d
Merge pull request #21719 from A4-Tacks/make-block-newline
ChayimFriedman2 Mar 4, 2026
4dc4e42
Merge pull request #21687 from A4-Tacks/destruct-self-param
ChayimFriedman2 Mar 4, 2026
2c69534
Release a new smol-str minor version with borsh fix
ChayimFriedman2 Mar 4, 2026
8454a15
Merge pull request #21743 from ChayimFriedman2/bump-smol-str
ChayimFriedman2 Mar 4, 2026
6b13e5e
Fix a bug in associated type lowering
ChayimFriedman2 Mar 4, 2026
560ec97
Merge pull request #21671 from A4-Tacks/postfix-tree-climbing
ChayimFriedman2 Mar 4, 2026
0f1a1ad
Only complain about uncanonicalized issue links
lnicola Mar 4, 2026
2129e09
Merge pull request #21746 from lnicola/triagebot-issue-links
lnicola Mar 4, 2026
312a764
Merge pull request #21745 from ChayimFriedman2/assoc-type-fixup
Veykril Mar 4, 2026
83bedb3
Allow never coercions in struct update syntax
ChayimFriedman2 Mar 4, 2026
b1eedb6
Merge pull request #21747 from ChayimFriedman2/never-upd-syn
ShoyuVanilla Mar 4, 2026
bd8c9f3
Do not use PostAnalysis TypingMode for IDE method resolution
ChayimFriedman2 Mar 4, 2026
8bfd981
When going to def on `?` on `Result` that goes through `From`, go to …
ChayimFriedman2 Mar 4, 2026
61fbc07
Merge pull request #21750 from ChayimFriedman2/method-res-typingmode
ShoyuVanilla Mar 5, 2026
e502782
Prepare for merging from rust-lang/rust
invalid-email-address Mar 5, 2026
3ef39ff
Merge ref 'f8704be04fe1' from rust-lang/rust
invalid-email-address Mar 5, 2026
43b6fa2
Merge pull request #21753 from rust-lang/rustc-pull
lnicola Mar 5, 2026
4343bc7
Add `has_pending` methods to `Incoming` and `Outgoing` in `lsp_server`
oeb25 Mar 5, 2026
d63bb88
Merge pull request #21752 from ChayimFriedman2/question-mark-conversion
Veykril Mar 5, 2026
a5c4f69
Merge pull request #21755 from oeb25/lsp-server-has-pending
Veykril Mar 5, 2026
1bd871f
Merge pull request #21732 from Wilfred/add_claude_rules
Veykril Mar 5, 2026
92845ed
Merge pull request #21660 from Wilfred/configure_codecov
Veykril Mar 5, 2026
06f959c
fix clippy on master
Shourya742 Mar 6, 2026
f2abae6
Merge pull request #21760 from Shourya742/2026-03-06-fix-clippy
ShoyuVanilla Mar 6, 2026
006929f
Tweak pre-release check for beta
lnicola Mar 6, 2026
80af448
Merge pull request #21763 from lnicola/lockfile-beta
lnicola Mar 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/tools/rust-analyzer/.codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
coverage:
range: 40...60
status:
patch: off
project:
default:
informational: true

# Don't leave comments on PRs
comment: false
44 changes: 44 additions & 0 deletions src/tools/rust-analyzer/.github/workflows/coverage.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: Coverage

on: [pull_request, push]

env:
CARGO_INCREMENTAL: 0
CARGO_NET_RETRY: 10
CI: 1
RUST_BACKTRACE: short
RUSTUP_MAX_RETRIES: 10

jobs:
coverage:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install Rust toolchain
run: |
rustup update --no-self-update nightly
rustup default nightly
rustup component add --toolchain nightly rust-src rustc-dev rustfmt
# We also install a nightly rustfmt, because we use `--file-lines` in
# a test.
rustup toolchain install nightly --profile minimal --component rustfmt

rustup toolchain install nightly --component llvm-tools-preview

- name: Install cargo-llvm-cov
uses: taiki-e/install-action@cargo-llvm-cov

- name: Install nextest
uses: taiki-e/install-action@nextest

- name: Generate code coverage
run: cargo llvm-cov --workspace --lcov --output-path lcov.info

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
with:
files: lcov.info
fail_ci_if_error: false
token: ${{ secrets.CODECOV_TOKEN }}
verbose: true
1 change: 1 addition & 0 deletions src/tools/rust-analyzer/AGENTS.md
40 changes: 40 additions & 0 deletions src/tools/rust-analyzer/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
**Reminder: All AI usage must be disclosed in commit messages, see
CONTRIBUTING.md for more details.**

## Build Commands

```bash
cargo build # Build all crates
cargo test # Run all tests
cargo test -p <crate> # Run tests for a specific crate (e.g., cargo test -p hir-ty)
cargo lint # Run clippy on all targets
cargo xtask codegen # Run code generation
cargo xtask tidy # Run tidy checks
UPDATE_EXPECT=1 cargo test # Update test expectations (snapshot tests)
RUN_SLOW_TESTS=1 cargo test # Run heavy/slow tests
```

## Key Architectural Invariants

- Typing in a function body never invalidates global derived data
- Parser/syntax tree is built per-file to enable parallel parsing
- The server is stateless (HTTP-like); context must be re-created from request parameters
- Cancellation uses salsa's cancellation mechanism; computations panic with a `Cancelled` payload

### Code Generation

Generated code is committed to the repo. Grammar and AST are generated from `ungrammar`. Run `cargo test -p xtask` after adding inline parser tests (`// test test_name` comments).

## Testing

Tests are snapshot-based using `expect-test`. Test fixtures use a mini-language:
- `$0` marks cursor position
- `// ^^^^` labels attach to the line above
- `//- minicore: sized, fn` includes parts of minicore (minimal core library)
- `//- /path/to/file.rs crate:name deps:dep1,dep2` declares files/crates

## Style Notes

- Use `stdx::never!` and `stdx::always!` instead of `assert!` for recoverable invariants
- Use `T![fn]` macro instead of `SyntaxKind::FN_KW`
- Use keyword name mangling over underscore prefixing for identifiers: `crate` → `krate`, `fn` → `func`, `struct` → `strukt`, `type` → `ty`
2 changes: 1 addition & 1 deletion src/tools/rust-analyzer/Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2635,7 +2635,7 @@ dependencies = [

[[package]]
name = "smol_str"
version = "0.3.5"
version = "0.3.6"
dependencies = [
"arbitrary",
"borsh",
Expand Down
2 changes: 1 addition & 1 deletion src/tools/rust-analyzer/crates/hir-def/src/visibility.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ impl Visibility {
if mod_.krate(db) == krate { Some(Visibility::Module(mod_, exp)) } else { None }
}
(Visibility::Module(mod_a, expl_a), Visibility::Module(mod_b, expl_b)) => {
if mod_a.krate(db) != mod_b.krate(db) {
if mod_a == mod_b {
// Most module visibilities are `pub(self)`, and assuming no errors
// this will be the common and thus fast path.
return Some(Visibility::Module(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@ use std::borrow::Cow;
use base_db::AnchoredPath;
use cfg::CfgExpr;
use either::Either;
use intern::{
Symbol,
sym,
};
use intern::{Symbol, sym};
use itertools::Itertools;
use mbe::{DelimiterKind, expect_fragment};
use span::{Edition, FileId, Span};
Expand Down
2 changes: 1 addition & 1 deletion src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -658,7 +658,7 @@ impl<'db> InferenceContext<'_, 'db> {
}
}
if let RecordSpread::Expr(expr) = *spread {
self.infer_expr(expr, &Expectation::has_type(ty), ExprIsRead::Yes);
self.infer_expr_coerce_never(expr, &Expectation::has_type(ty), ExprIsRead::Yes);
}
ty
}
Expand Down
12 changes: 8 additions & 4 deletions src/tools/rust-analyzer/crates/hir-ty/src/infer/op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,9 +178,9 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
// trait matching creating lifetime constraints that are too strict.
// e.g., adding `&'a T` and `&'b T`, given `&'x T: Add<&'x T>`, will result
// in `&'a T <: &'x T` and `&'b T <: &'x T`, instead of `'a = 'b = 'x`.
let lhs_ty = self.infer_expr_no_expect(lhs_expr, ExprIsRead::No);
let lhs_ty = self.infer_expr_no_expect(lhs_expr, ExprIsRead::Yes);
let fresh_var = self.table.next_ty_var();
self.demand_coerce(lhs_expr, lhs_ty, fresh_var, AllowTwoPhase::No, ExprIsRead::No)
self.demand_coerce(lhs_expr, lhs_ty, fresh_var, AllowTwoPhase::No, ExprIsRead::Yes)
}
};
let lhs_ty = self.table.resolve_vars_with_obligations(lhs_ty);
Expand All @@ -200,7 +200,7 @@ impl<'a, 'db> InferenceContext<'a, 'db> {

// see `NB` above
let rhs_ty =
self.infer_expr_coerce(rhs_expr, &Expectation::HasType(rhs_ty_var), ExprIsRead::No);
self.infer_expr_coerce(rhs_expr, &Expectation::HasType(rhs_ty_var), ExprIsRead::Yes);
let rhs_ty = self.table.resolve_vars_with_obligations(rhs_ty);

let return_ty = match result {
Expand Down Expand Up @@ -320,7 +320,11 @@ impl<'a, 'db> InferenceContext<'a, 'db> {
if let Some((rhs_expr, rhs_ty)) = opt_rhs
&& rhs_ty.is_ty_var()
{
self.infer_expr_coerce(rhs_expr, &Expectation::HasType(rhs_ty), ExprIsRead::No);
self.infer_expr_coerce(
rhs_expr,
&Expectation::HasType(rhs_ty),
ExprIsRead::Yes,
);
}

// Construct an obligation `self_ty : Trait<input_tys>`
Expand Down
112 changes: 70 additions & 42 deletions src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,7 @@ use rustc_hash::FxHashSet;
use rustc_type_ir::{
AliasTyKind, BoundVarIndexKind, ConstKind, DebruijnIndex, ExistentialPredicate,
ExistentialProjection, ExistentialTraitRef, FnSig, Interner, OutlivesPredicate, TermKind,
TyKind,
TypeFoldable, TypeVisitableExt, Upcast, UpcastFrom, elaborate,
TyKind, TypeFoldable, TypeVisitableExt, Upcast, UpcastFrom, elaborate,
inherent::{Clause as _, GenericArgs as _, IntoKind as _, Region as _, Ty as _},
};
use smallvec::SmallVec;
Expand Down Expand Up @@ -1681,10 +1680,16 @@ impl SupertraitsInfo {
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum TypeParamAssocTypeShorthandError {
AssocTypeNotFound,
AmbiguousAssocType,
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
enum AssocTypeShorthandResolution {
Resolved(StoredEarlyBinder<(TypeAliasId, StoredGenericArgs)>),
Ambiguous {
/// If one resolution belongs to a sub-trait and one to a supertrait, this contains
/// the sub-trait's resolution. This can be `None` if there is no trait inheritance
/// relationship between the resolutions.
sub_trait_resolution: Option<StoredEarlyBinder<(TypeAliasId, StoredGenericArgs)>>,
},
NotFound,
Cycle,
}

Expand All @@ -1708,7 +1713,7 @@ fn resolve_type_param_assoc_type_shorthand(
def: GenericDefId,
param: TypeParamId,
assoc_name: Name,
) -> Result<StoredEarlyBinder<(TypeAliasId, StoredGenericArgs)>, TypeParamAssocTypeShorthandError> {
) -> AssocTypeShorthandResolution {
let generics = generics(db, def);
let resolver = def.resolver(db);
let mut ctx = TyLoweringContext::new(
Expand All @@ -1719,13 +1724,13 @@ fn resolve_type_param_assoc_type_shorthand(
LifetimeElisionKind::AnonymousReportError,
);
let interner = ctx.interner;
let mut result = None;
let param_ty = Ty::new_param(
interner,
param,
generics.type_or_const_param_idx(param.into()).unwrap() as u32,
);

let mut this_trait_resolution = None;
if let GenericDefId::TraitId(containing_trait) = param.parent()
&& param.local_id() == GenericParams::SELF_PARAM_ID_IN_SELF
{
Expand All @@ -1734,10 +1739,11 @@ fn resolve_type_param_assoc_type_shorthand(
containing_trait.trait_items(db).associated_type_by_name(&assoc_name)
{
let args = GenericArgs::identity_for_item(interner, containing_trait.into());
result = Some(StoredEarlyBinder::bind((assoc_type, args.store())));
this_trait_resolution = Some(StoredEarlyBinder::bind((assoc_type, args.store())));
}
}

let mut supertraits_resolution = None;
for maybe_parent_generics in
std::iter::successors(Some(&generics), |generics| generics.parent_generics())
{
Expand Down Expand Up @@ -1783,34 +1789,53 @@ fn resolve_type_param_assoc_type_shorthand(
TypeParamId::trait_self(bounded_trait),
assoc_name.clone(),
);
let lookup_on_bounded_trait = match lookup_on_bounded_trait {
Ok(it) => it,
Err(
err @ (TypeParamAssocTypeShorthandError::AmbiguousAssocType
| TypeParamAssocTypeShorthandError::Cycle),
) => return Err(*err),
Err(TypeParamAssocTypeShorthandError::AssocTypeNotFound) => {
let assoc_type_and_args = match &lookup_on_bounded_trait {
AssocTypeShorthandResolution::Resolved(trait_ref) => trait_ref,
AssocTypeShorthandResolution::Ambiguous {
sub_trait_resolution: Some(trait_ref),
} => trait_ref,
AssocTypeShorthandResolution::Ambiguous { sub_trait_resolution: None } => {
return AssocTypeShorthandResolution::Ambiguous {
sub_trait_resolution: this_trait_resolution,
};
}
AssocTypeShorthandResolution::NotFound => {
never!("we checked that the trait defines this assoc type");
continue;
}
AssocTypeShorthandResolution::Cycle => return AssocTypeShorthandResolution::Cycle,
};
let (assoc_type, args) = lookup_on_bounded_trait
.get_with(|(assoc_type, args)| (*assoc_type, args.as_ref()))
.skip_binder();
let args = EarlyBinder::bind(args).instantiate(interner, bounded_trait_ref.args);
let current_result = StoredEarlyBinder::bind((assoc_type, args.store()));
// If we already have a result, this is an ambiguity - unless this is the same result, then we are fine
// (e.g. rustc allows to write the same bound twice without ambiguity).
if let Some(existing_result) = result
&& existing_result != current_result
{
return Err(TypeParamAssocTypeShorthandError::AmbiguousAssocType);
if let Some(this_trait_resolution) = this_trait_resolution {
return AssocTypeShorthandResolution::Ambiguous {
sub_trait_resolution: Some(this_trait_resolution),
};
} else if supertraits_resolution.is_some() {
return AssocTypeShorthandResolution::Ambiguous { sub_trait_resolution: None };
} else {
let (assoc_type, args) = assoc_type_and_args
.get_with(|(assoc_type, args)| (*assoc_type, args.as_ref()))
.skip_binder();
let args = EarlyBinder::bind(args).instantiate(interner, bounded_trait_ref.args);
let current_result = StoredEarlyBinder::bind((assoc_type, args.store()));
supertraits_resolution = Some(match lookup_on_bounded_trait {
AssocTypeShorthandResolution::Resolved(_) => {
AssocTypeShorthandResolution::Resolved(current_result)
}
AssocTypeShorthandResolution::Ambiguous { .. } => {
AssocTypeShorthandResolution::Ambiguous {
sub_trait_resolution: Some(current_result),
}
}
AssocTypeShorthandResolution::NotFound
| AssocTypeShorthandResolution::Cycle => unreachable!(),
});
}
result = Some(current_result);
}
}

result.ok_or(TypeParamAssocTypeShorthandError::AssocTypeNotFound)
supertraits_resolution
.or_else(|| this_trait_resolution.map(AssocTypeShorthandResolution::Resolved))
.unwrap_or(AssocTypeShorthandResolution::NotFound)
}

fn resolve_type_param_assoc_type_shorthand_cycle_result(
Expand All @@ -1819,8 +1844,8 @@ fn resolve_type_param_assoc_type_shorthand_cycle_result(
_def: GenericDefId,
_param: TypeParamId,
_assoc_name: Name,
) -> Result<StoredEarlyBinder<(TypeAliasId, StoredGenericArgs)>, TypeParamAssocTypeShorthandError> {
Err(TypeParamAssocTypeShorthandError::Cycle)
) -> AssocTypeShorthandResolution {
AssocTypeShorthandResolution::Cycle
}

#[inline]
Expand Down Expand Up @@ -2468,7 +2493,7 @@ fn fn_sig_for_struct_constructor(
let inputs_and_output =
Tys::new_from_iter(DbInterner::new_no_crate(db), params.chain(Some(ret.as_ref())));
StoredEarlyBinder::bind(StoredPolyFnSig::new(Binder::dummy(FnSig {
abi: FnAbi::RustCall,
abi: FnAbi::Rust,
c_variadic: false,
safety: Safety::Safe,
inputs_and_output,
Expand All @@ -2487,7 +2512,7 @@ fn fn_sig_for_enum_variant_constructor(
let inputs_and_output =
Tys::new_from_iter(DbInterner::new_no_crate(db), params.chain(Some(ret.as_ref())));
StoredEarlyBinder::bind(StoredPolyFnSig::new(Binder::dummy(FnSig {
abi: FnAbi::RustCall,
abi: FnAbi::Rust,
c_variadic: false,
safety: Safety::Safe,
inputs_and_output,
Expand Down Expand Up @@ -2569,19 +2594,22 @@ pub(crate) fn associated_ty_item_bounds<'db>(
EarlyBinder::bind(BoundExistentialPredicates::new_from_slice(&bounds))
}

pub(crate) fn associated_type_by_name_including_super_traits<'db>(
pub(crate) fn associated_type_by_name_including_super_traits_allow_ambiguity<'db>(
db: &'db dyn HirDatabase,
trait_ref: TraitRef<'db>,
name: Name,
) -> Option<(TypeAliasId, GenericArgs<'db>)> {
let assoc_type = resolve_type_param_assoc_type_shorthand(
db,
trait_ref.def_id.0.into(),
TypeParamId::trait_self(trait_ref.def_id.0),
name.clone(),
)
.as_ref()
.ok()?;
let (AssocTypeShorthandResolution::Resolved(assoc_type)
| AssocTypeShorthandResolution::Ambiguous { sub_trait_resolution: Some(assoc_type) }) =
resolve_type_param_assoc_type_shorthand(
db,
trait_ref.def_id.0.into(),
TypeParamId::trait_self(trait_ref.def_id.0),
name.clone(),
)
else {
return None;
};
let (assoc_type, trait_args) = assoc_type
.get_with(|(assoc_type, trait_args)| (*assoc_type, trait_args.as_ref()))
.skip_binder();
Expand Down
Loading
Loading