Skip to content
Open
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
26 changes: 24 additions & 2 deletions crates/hir/src/semantics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ use stdx::{TupleExt, always};
use syntax::{
AstNode, AstPtr, AstToken, Direction, SmolStr, SmolStrBuilder, SyntaxElement, SyntaxKind,
SyntaxNode, SyntaxNodePtr, SyntaxToken, T, TextRange, TextSize,
algo::skip_trivia_token,
algo::{skip_trivia_token, skip_whitespace_token},
ast::{self, HasAttrs as _, HasGenericParams},
};

Expand Down Expand Up @@ -1534,7 +1534,20 @@ impl<'db> SemanticsImpl<'db> {
node: &SyntaxNode,
offset: TextSize,
) -> impl Iterator<Item = impl Iterator<Item = SyntaxNode> + '_> + '_ {
node.token_at_offset(offset)
let (on_whitespace, tokens) = match node.token_at_offset(offset) {
syntax::TokenAtOffset::Single(token) if token.kind() == SyntaxKind::WHITESPACE => {
let prev = token
.prev_token()
.and_then(|token| skip_whitespace_token(token, Direction::Prev));
let next = token
.next_token()
.and_then(|token| skip_whitespace_token(token, Direction::Next));
(true, Either::Left(itertools::chain!([token], prev, next)))
}
tokens => (false, Either::Right(tokens)),
};
let file_id = self.find_file(node).file_id;
tokens
Comment on lines +1537 to +1550

@ChayimFriedman2 ChayimFriedman2 Jul 31, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, not a fan of this implementation, and it also doesn't account for comments.

How about:

let tokens = node.token_at_offset(offset);
let tokens = if tokens.all(|token| token.kind().is_trivia()) {
    Either::Left(tokens.flat_map(|token| {
                let prev = token
                    .prev_token()
                    .and_then(|token| skip_trivia_token(token, Direction::Prev));
                let next = token
                    .next_token()
                    .and_then(|token| skip_trivia_token(token, Direction::Next));
                std::iter::chain(prev, next)
    }))
} else {
    Either::Right(tokens.filter(|token| token.kind().is_trivia()))
};

View changes since the review

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and it also doesn't account for comments.

I think triggering on comments is too surprising

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is similar to the behavior of trimmed_range

.map(move |token| self.descend_into_macros_exact(token))
.map(|descendants| {
descendants.into_iter().map(move |it| self.token_ancestors_with_macros(it))
Expand All @@ -1546,6 +1559,15 @@ impl<'db> SemanticsImpl<'db> {
.map(|node| node.text_range().len())
.lt(right.clone().map(|node| node.text_range().len()))
})
.map(move |ancestors| {
ancestors.filter(move |node| {
if !on_whitespace {
return true;
}
let origin = self.original_range(node);
origin.file_id == file_id && origin.range.contains(offset)
})
})
Comment on lines +1562 to +1570

@ChayimFriedman2 ChayimFriedman2 Jul 31, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should come before the kmerge_by(). Also I think it's better to not check on_whitespace.

View changes since the review

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If filtering is done before kmerge, it will result in excessive redundant calculations for kmerge

Checking the is_whitespace should prevent performance degradation most of the time and may also avoid some regressions (or fail-fast is better?)

}

/// Attempts to map the node out of macro expanded files returning the original file range.
Expand Down
37 changes: 37 additions & 0 deletions crates/ide-assists/src/handlers/add_missing_match_arms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1833,6 +1833,43 @@ enum Test {
C,
}

fn foo(t: Test) {
m!(match t {
Test::A => (),
Test::B => ${1:todo!()},
Test::C => ${2:todo!()},$0
});
}"#,
);
}

#[test]
fn works_inside_macro_call_on_whitespace() {
// On whitespace inside macro (#21729)
check_assist(
add_missing_match_arms,
r#"
macro_rules! m { ($expr:expr) => {$expr}}
enum Test {
A,
B,
C,
}

fn foo(t: Test) {
m!(match t {
Test::A => (),
$0
});
}"#,
r#"
macro_rules! m { ($expr:expr) => {$expr}}
enum Test {
A,
B,
C,
}

fn foo(t: Test) {
m!(match t {
Test::A => (),
Expand Down