Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 8 additions & 0 deletions crates/hir-ty/src/mir/lower/pattern_matching.rs
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,14 @@ impl<'db> MirLowerCtx<'_, 'db> {
shape: AdtPatternShape<'_>,
mode: MatchingMode,
) -> Result<'db, (BasicBlockId, Option<BasicBlockId>)> {
let place_ty = cond_place.ty(&self.result, &self.infcx, self.env).ty;
let Some((place_adt, _)) = place_ty.as_adt() else {
return Err(MirLowerError::TypeError("non ADT type matched with ADT pattern"));
};
if place_adt != variant.adt_id(self.db) {
return Err(MirLowerError::TypeError("ADT pattern does not match place type"));
}

Ok(match variant {
VariantId::EnumVariantId(v) => {
if mode == MatchingMode::Check {
Expand Down
16 changes: 16 additions & 0 deletions crates/hir-ty/src/mir/lower/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,19 @@ fn alias<T: Tr>(x: T::A) {
"#,
);
}

#[test]
fn borrowck_opaque_downcast_recovery_does_not_panic() {
check_borrowck(
r#"
//- minicore: option, sized
struct PathBuf;
fn opaque<T>(path: T) -> impl Sized {
Some(path)
}
fn caller(path: &PathBuf) {
let Some(value) = opaque(path) else { return };
}
"#,
);
}
111 changes: 100 additions & 11 deletions crates/ide-assists/src/handlers/extract_variable.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
use std::ops::RangeInclusive;

use hir::{HirDisplay, TypeInfo};
use ide_db::{
assists::GroupLabel,
syntax_helpers::{LexedStr, suggest_name},
};
use syntax::{
Direction, NodeOrToken, SyntaxKind, SyntaxNode, SyntaxToken, T, TextRange,
Direction, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxToken, T, TextRange,
algo::{ancestors_at_offset, skip_trivia_token},
ast::{
self, AstNode,
edit::{AstNodeEdit, IndentLevel},
},
hacks::parse_expr_from_str,
syntax_editor::{Element, Position},
};

Expand Down Expand Up @@ -92,7 +95,7 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -
let node = node.ancestors().take_while(|anc| anc.text_range() == node.text_range()).last()?;
let range = node.text_range();

let (to_replace, analysis) = if node.kind() == SyntaxKind::TOKEN_TREE {
let (to_replace, analysis, use_source_expr) = if node.kind() == SyntaxKind::TOKEN_TREE {
let (first, last) = extract_token_range_of(&node, ctx.selection_trimmed())?;

let first_descend = ctx.sema.descend_into_macros_single_exact(first.clone());
Expand All @@ -111,14 +114,14 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -
if !node.text_range().contains_range(original_range.range) {
return None;
}
(cover_edit_range(&node, original_range.range), expr)
(cover_edit_range(&node, original_range.range), expr, true)
} else {
let expr = node
.descendants()
.take_while(|it| range.contains_range(it.text_range()))
.find_map(valid_target_expr(ctx))?;
let to_extract = expr.syntax().syntax_element();
(to_extract.clone()..=to_extract, expr)
(to_extract.clone()..=to_extract, expr, false)
};
let place = match to_replace.start() {
NodeOrToken::Node(node) => node.clone(),
Expand Down Expand Up @@ -217,6 +220,11 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -
editor.add_annotation(pat_name.syntax().clone(), tabstop);
}

let to_extract_no_ref = if use_source_expr {
source_expr(ctx, to_replace.clone()).unwrap()
} else {
to_extract_no_ref.clone()
};
let initializer = match ty.as_ref().filter(|_| needs_ref) {
Some(receiver_type) if receiver_type.is_mutable_reference() => {
make.expr_ref(to_extract_no_ref.clone(), true)
Expand Down Expand Up @@ -331,6 +339,15 @@ fn peel_parens(mut expr: ast::Expr) -> ast::Expr {
expr
}

fn source_expr(
ctx: &AssistContext<'_, '_>,
range: RangeInclusive<SyntaxElement>,
) -> Option<ast::Expr> {
let range = range.start().text_range().cover(range.end().text_range());
let text = ctx.source_file().syntax().text().slice(range).to_string();
parse_expr_from_str(&text, ctx.edition())
}

/// Check whether the node is a valid expression which can be extracted to a variable.
/// In general that's true for any expression, but in some cases that would produce invalid code.
fn valid_target_expr(ctx: &AssistContext<'_, '_>) -> impl Fn(SyntaxNode) -> Option<ast::Expr> {
Expand Down Expand Up @@ -2828,7 +2845,6 @@ fn main() {

#[test]
fn extract_variable_in_token_tree() {
// FIXME: Keep the original trivia instead of extracting macro expanded?
check_assist_by_label(
extract_variable,
r#"
Expand All @@ -2850,7 +2866,7 @@ macro_rules! foo {
}

fn main() {
let $0var_name = 2+3;
let $0var_name = 2 + 3;
let x = foo!(= var_name + 4);
}
"#,
Expand Down Expand Up @@ -2878,7 +2894,7 @@ macro_rules! foo {
}

fn main() {
let $0var_name = 2+3;
let $0var_name = 2 + 3;
let x = foo!(= var_name + 4);
}
"#,
Expand Down Expand Up @@ -2906,7 +2922,7 @@ macro_rules! foo {
}

fn main() {
let $0var_name = 2+3+4;
let $0var_name = 2 + 3 + 4;
let x = foo!(= var_name);
}
"#,
Expand Down Expand Up @@ -2937,11 +2953,39 @@ macro_rules! foo {
}

fn main() {
let $0var_name = 2+3+4;
let $0var_name = 2 + 3 + 4;
let x = foo!(= {
var_name
});
}
"#,
"Extract into variable",
);

check_assist_by_label(
extract_variable,
r#"
macro_rules! identity {
($e:expr) => {
$e
};
}

fn main() {
let x = identity!($0(1+2)$0);
}
"#,
r#"
macro_rules! identity {
($e:expr) => {
$e
};
}

fn main() {
let $0var_name = (1+2);
let x = identity!(var_name);
}
"#,
"Extract into variable",
);
Expand Down Expand Up @@ -2970,7 +3014,7 @@ macro_rules! foo {
}

fn main() {
let $0x = 2+3;
let $0x = 2 + 3;
let x = foo!(= Foo { x: x });
}
"#,
Expand Down Expand Up @@ -2998,14 +3042,59 @@ macro_rules! foo {
}

fn main() {
let $0var_name = 2+3;
let $0var_name = 2 + 3;
let x = foo!(= Foo { x: var_name + 4 });
}
"#,
"Extract into variable",
);
}

#[test]
fn extract_variable_in_assert_macro_preserves_required_whitespace() {
check_assist_by_label(
extract_variable,
r#"
//- minicore: assert
fn check(value: &mut usize) -> bool {
false
}

fn foo(mut bar: usize) {
assert!(check($0&mut bar$0));
}
"#,
r#"
fn check(value: &mut usize) -> bool {
false
}

fn foo(mut bar: usize) {
let $0value = &mut bar;
assert!(check(value));
}
"#,
"Extract into variable",
);

check_assist_by_label(
extract_variable,
r#"
//- minicore: assert
fn main() {
assert!($0if true {true} else {false}$0);
}
"#,
r#"
fn main() {
let $0var_name = if true {true} else {false};
assert!(var_name);
}
"#,
"Extract into variable",
);
}

#[test]
fn regression_22441() {
check_assist_by_label(
Expand Down
43 changes: 29 additions & 14 deletions crates/ide-completion/src/completions/flyimport.rs
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@ fn import_on_the_fly<'db>(
}
};
let user_input_lowercased = potential_import_name.to_lowercase();
let mut import_name_buffer = String::new();

let import_cfg = ctx.config.import_path_config();

Expand All @@ -276,9 +277,13 @@ fn import_on_the_fly<'db>(
})
.filter(|import| filter_excluded_flyimport(ctx, import))
.sorted_by(|a, b| {
let key = |import_path| {
let mut key = |import_path| {
(
compute_fuzzy_completion_order_key(import_path, &user_input_lowercased),
compute_fuzzy_completion_order_key(
import_path,
&user_input_lowercased,
&mut import_name_buffer,
),
import_path,
)
};
Expand Down Expand Up @@ -310,6 +315,7 @@ fn import_on_the_fly_pat_<'db>(
ItemInNs::Values(def) => matches!(def, hir::ModuleDef::Const(_)),
};
let user_input_lowercased = potential_import_name.to_lowercase();
let mut import_name_buffer = String::new();
let cfg = ctx.config.import_path_config();

import_assets
Expand All @@ -322,9 +328,13 @@ fn import_on_the_fly_pat_<'db>(
&& ctx.check_stability(original_item.attrs(ctx.db).as_ref())
})
.sorted_by(|a, b| {
let key = |import_path| {
let mut key = |import_path| {
(
compute_fuzzy_completion_order_key(import_path, &user_input_lowercased),
compute_fuzzy_completion_order_key(
import_path,
&user_input_lowercased,
&mut import_name_buffer,
),
import_path,
)
};
Expand All @@ -351,6 +361,7 @@ fn import_on_the_fly_method<'db>(
ImportScope::find_insert_use_container(&position, &ctx.sema)?;

let user_input_lowercased = potential_import_name.to_lowercase();
let mut import_name_buffer = String::new();

let cfg = ctx.config.import_path_config();

Expand All @@ -362,9 +373,13 @@ fn import_on_the_fly_method<'db>(
})
.filter(|import| filter_excluded_flyimport(ctx, import))
.sorted_by(|a, b| {
let key = |import_path| {
let mut key = |import_path| {
(
compute_fuzzy_completion_order_key(import_path, &user_input_lowercased),
compute_fuzzy_completion_order_key(
import_path,
&user_input_lowercased,
&mut import_name_buffer,
),
import_path,
)
};
Expand Down Expand Up @@ -437,15 +452,15 @@ fn import_assets_for_path<'db>(
fn compute_fuzzy_completion_order_key(
proposed_mod_path: &hir::ModPath,
user_input_lowercased: &str,
import_name_buffer: &mut String,
) -> usize {
cov_mark::hit!(certain_fuzzy_order_test);
let import_name = match proposed_mod_path.segments().last() {
// FIXME: nasty alloc, this is a hot path!
Some(name) => name.as_str().to_ascii_lowercase(),
None => return usize::MAX,
let Some(import_name) = proposed_mod_path.segments().last() else {
return usize::MAX;
};
match import_name.match_indices(user_input_lowercased).next() {
Some((first_matching_index, _)) => first_matching_index,
None => usize::MAX,
}

import_name_buffer.clear();
import_name_buffer.push_str(import_name.as_str());
import_name_buffer.make_ascii_lowercase();
import_name_buffer.find(user_input_lowercased).unwrap_or(usize::MAX)
}
28 changes: 28 additions & 0 deletions crates/ide-completion/src/tests/flyimport.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,34 @@ fn main() {
);
}

#[test]
fn fuzzy_completion_order_is_case_insensitive_and_deterministic() {
check(
r#"
//- /lib.rs crate:dep
pub mod zed {
pub struct HIRThing;
}
pub mod alpha {
pub struct HIRThing;
}
pub struct BeforeHIRThing;
pub struct HiiirThing;

//- /main.rs crate:main deps:dep
fn main() {
hir$0
}
"#,
expect![[r#"
st HIRThing (use dep::alpha::HIRThing) HIRThing
st HIRThing (use dep::zed::HIRThing) HIRThing
st BeforeHIRThing (use dep::BeforeHIRThing) BeforeHIRThing
st HiiirThing (use dep::HiiirThing) HiiirThing
"#]],
);
}

#[test]
fn trait_function_fuzzy_completion() {
let fixture = r#"
Expand Down
Loading