aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/assists/src/handlers/merge_imports.rs5
-rw-r--r--crates/assists/src/handlers/remove_dbg.rs3
-rw-r--r--crates/assists/src/handlers/remove_unused_param.rs131
-rw-r--r--crates/assists/src/lib.rs2
-rw-r--r--crates/assists/src/tests/generated.rs21
-rw-r--r--crates/assists/src/utils.rs6
-rw-r--r--crates/ide/src/syntax_highlighting.rs40
-rw-r--r--crates/ide_db/src/search.rs2
8 files changed, 178 insertions, 32 deletions
diff --git a/crates/assists/src/handlers/merge_imports.rs b/crates/assists/src/handlers/merge_imports.rs
index 47d465404..35b884206 100644
--- a/crates/assists/src/handlers/merge_imports.rs
+++ b/crates/assists/src/handlers/merge_imports.rs
@@ -8,6 +8,7 @@ use syntax::{
8 8
9use crate::{ 9use crate::{
10 assist_context::{AssistContext, Assists}, 10 assist_context::{AssistContext, Assists},
11 utils::next_prev,
11 AssistId, AssistKind, 12 AssistId, AssistKind,
12}; 13};
13 14
@@ -66,10 +67,6 @@ pub(crate) fn merge_imports(acc: &mut Assists, ctx: &AssistContext) -> Option<()
66 ) 67 )
67} 68}
68 69
69fn next_prev() -> impl Iterator<Item = Direction> {
70 [Direction::Next, Direction::Prev].iter().copied()
71}
72
73fn try_merge_trees(old: &ast::UseTree, new: &ast::UseTree) -> Option<ast::UseTree> { 70fn try_merge_trees(old: &ast::UseTree, new: &ast::UseTree) -> Option<ast::UseTree> {
74 let lhs_path = old.path()?; 71 let lhs_path = old.path()?;
75 let rhs_path = new.path()?; 72 let rhs_path = new.path()?;
diff --git a/crates/assists/src/handlers/remove_dbg.rs b/crates/assists/src/handlers/remove_dbg.rs
index f3dcca534..4e252edf0 100644
--- a/crates/assists/src/handlers/remove_dbg.rs
+++ b/crates/assists/src/handlers/remove_dbg.rs
@@ -82,9 +82,10 @@ fn is_valid_macrocall(macro_call: &ast::MacroCall, macro_name: &str) -> Option<b
82 82
83#[cfg(test)] 83#[cfg(test)]
84mod tests { 84mod tests {
85 use super::*;
86 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target}; 85 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
87 86
87 use super::*;
88
88 #[test] 89 #[test]
89 fn test_remove_dbg() { 90 fn test_remove_dbg() {
90 check_assist(remove_dbg, "<|>dbg!(1 + 1)", "1 + 1"); 91 check_assist(remove_dbg, "<|>dbg!(1 + 1)", "1 + 1");
diff --git a/crates/assists/src/handlers/remove_unused_param.rs b/crates/assists/src/handlers/remove_unused_param.rs
new file mode 100644
index 000000000..5fccca54b
--- /dev/null
+++ b/crates/assists/src/handlers/remove_unused_param.rs
@@ -0,0 +1,131 @@
1use ide_db::{defs::Definition, search::Reference};
2use syntax::{
3 algo::find_node_at_range,
4 ast::{self, ArgListOwner},
5 AstNode, SyntaxNode, TextRange, T,
6};
7use test_utils::mark;
8
9use crate::{
10 assist_context::AssistBuilder, utils::next_prev, AssistContext, AssistId, AssistKind, Assists,
11};
12
13// Assist: remove_unused_param
14//
15// Removes unused function parameter.
16//
17// ```
18// fn frobnicate(x: i32<|>) {}
19//
20// fn main() {
21// frobnicate(92);
22// }
23// ```
24// ->
25// ```
26// fn frobnicate() {}
27//
28// fn main() {
29// frobnicate();
30// }
31// ```
32pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
33 let param: ast::Param = ctx.find_node_at_offset()?;
34 let ident_pat = match param.pat()? {
35 ast::Pat::IdentPat(it) => it,
36 _ => return None,
37 };
38 let func = param.syntax().ancestors().find_map(ast::Fn::cast)?;
39 let param_position = func.param_list()?.params().position(|it| it == param)?;
40
41 let fn_def = {
42 let func = ctx.sema.to_def(&func)?;
43 Definition::ModuleDef(func.into())
44 };
45
46 let param_def = {
47 let local = ctx.sema.to_def(&ident_pat)?;
48 Definition::Local(local)
49 };
50 if param_def.usages(&ctx.sema).at_least_one() {
51 mark::hit!(keep_used);
52 return None;
53 }
54 acc.add(
55 AssistId("remove_unused_param", AssistKind::Refactor),
56 "Remove unused parameter",
57 param.syntax().text_range(),
58 |builder| {
59 builder.delete(range_with_coma(param.syntax()));
60 for usage in fn_def.usages(&ctx.sema).all() {
61 process_usage(ctx, builder, usage, param_position);
62 }
63 },
64 )
65}
66
67fn process_usage(
68 ctx: &AssistContext,
69 builder: &mut AssistBuilder,
70 usage: Reference,
71 arg_to_remove: usize,
72) -> Option<()> {
73 let source_file = ctx.sema.parse(usage.file_range.file_id);
74 let call_expr: ast::CallExpr =
75 find_node_at_range(source_file.syntax(), usage.file_range.range)?;
76 if call_expr.expr()?.syntax().text_range() != usage.file_range.range {
77 return None;
78 }
79 let arg = call_expr.arg_list()?.args().nth(arg_to_remove)?;
80
81 builder.edit_file(usage.file_range.file_id);
82 builder.delete(range_with_coma(arg.syntax()));
83
84 Some(())
85}
86
87fn range_with_coma(node: &SyntaxNode) -> TextRange {
88 let up_to = next_prev().find_map(|dir| {
89 node.siblings_with_tokens(dir)
90 .filter_map(|it| it.into_token())
91 .find(|it| it.kind() == T![,])
92 });
93 let up_to = up_to.map_or(node.text_range(), |it| it.text_range());
94 node.text_range().cover(up_to)
95}
96
97#[cfg(test)]
98mod tests {
99 use crate::tests::{check_assist, check_assist_not_applicable};
100
101 use super::*;
102
103 #[test]
104 fn remove_unused() {
105 check_assist(
106 remove_unused_param,
107 r#"
108fn a() { foo(9, 2) }
109fn foo(x: i32, <|>y: i32) { x; }
110fn b() { foo(9, 2,) }
111"#,
112 r#"
113fn a() { foo(9) }
114fn foo(x: i32) { x; }
115fn b() { foo(9, ) }
116"#,
117 );
118 }
119
120 #[test]
121 fn keep_used() {
122 mark::check!(keep_used);
123 check_assist_not_applicable(
124 remove_unused_param,
125 r#"
126fn foo(x: i32, <|>y: i32) { y; }
127fn main() { foo(9, 2) }
128"#,
129 );
130 }
131}
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs
index 14834480a..2e0d191a6 100644
--- a/crates/assists/src/lib.rs
+++ b/crates/assists/src/lib.rs
@@ -152,6 +152,7 @@ mod handlers {
152 mod raw_string; 152 mod raw_string;
153 mod remove_dbg; 153 mod remove_dbg;
154 mod remove_mut; 154 mod remove_mut;
155 mod remove_unused_param;
155 mod reorder_fields; 156 mod reorder_fields;
156 mod replace_if_let_with_match; 157 mod replace_if_let_with_match;
157 mod replace_let_with_if_let; 158 mod replace_let_with_if_let;
@@ -198,6 +199,7 @@ mod handlers {
198 raw_string::remove_hash, 199 raw_string::remove_hash,
199 remove_dbg::remove_dbg, 200 remove_dbg::remove_dbg,
200 remove_mut::remove_mut, 201 remove_mut::remove_mut,
202 remove_unused_param::remove_unused_param,
201 reorder_fields::reorder_fields, 203 reorder_fields::reorder_fields,
202 replace_if_let_with_match::replace_if_let_with_match, 204 replace_if_let_with_match::replace_if_let_with_match,
203 replace_let_with_if_let::replace_let_with_if_let, 205 replace_let_with_if_let::replace_let_with_if_let,
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs
index 173567003..04c8fd1f9 100644
--- a/crates/assists/src/tests/generated.rs
+++ b/crates/assists/src/tests/generated.rs
@@ -751,6 +751,27 @@ impl Walrus {
751} 751}
752 752
753#[test] 753#[test]
754fn doctest_remove_unused_param() {
755 check_doc_test(
756 "remove_unused_param",
757 r#####"
758fn frobnicate(x: i32<|>) {}
759
760fn main() {
761 frobnicate(92);
762}
763"#####,
764 r#####"
765fn frobnicate() {}
766
767fn main() {
768 frobnicate();
769}
770"#####,
771 )
772}
773
774#[test]
754fn doctest_reorder_fields() { 775fn doctest_reorder_fields() {
755 check_doc_test( 776 check_doc_test(
756 "reorder_fields", 777 "reorder_fields",
diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs
index 84ccacafe..d071d6502 100644
--- a/crates/assists/src/utils.rs
+++ b/crates/assists/src/utils.rs
@@ -9,7 +9,7 @@ use itertools::Itertools;
9use rustc_hash::FxHashSet; 9use rustc_hash::FxHashSet;
10use syntax::{ 10use syntax::{
11 ast::{self, make, NameOwner}, 11 ast::{self, make, NameOwner},
12 AstNode, 12 AstNode, Direction,
13 SyntaxKind::*, 13 SyntaxKind::*,
14 SyntaxNode, TextSize, T, 14 SyntaxNode, TextSize, T,
15}; 15};
@@ -311,3 +311,7 @@ pub use prelude::*;
311 Some(def) 311 Some(def)
312 } 312 }
313} 313}
314
315pub(crate) fn next_prev() -> impl Iterator<Item = Direction> {
316 [Direction::Next, Direction::Prev].iter().copied()
317}
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index db8aaed48..dd8cfe42d 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -484,9 +484,9 @@ fn highlight_element(
484 match name_kind { 484 match name_kind {
485 Some(NameClass::ExternCrate(_)) => HighlightTag::Module.into(), 485 Some(NameClass::ExternCrate(_)) => HighlightTag::Module.into(),
486 Some(NameClass::Definition(def)) => { 486 Some(NameClass::Definition(def)) => {
487 highlight_def(sema, db, def, None) | HighlightModifier::Definition 487 highlight_def(db, def) | HighlightModifier::Definition
488 } 488 }
489 Some(NameClass::ConstReference(def)) => highlight_def(sema, db, def, None), 489 Some(NameClass::ConstReference(def)) => highlight_def(db, def),
490 Some(NameClass::FieldShorthand { field, .. }) => { 490 Some(NameClass::FieldShorthand { field, .. }) => {
491 let mut h = HighlightTag::Field.into(); 491 let mut h = HighlightTag::Field.into();
492 if let Definition::Field(field) = field { 492 if let Definition::Field(field) = field {
@@ -520,7 +520,7 @@ fn highlight_element(
520 } 520 }
521 }; 521 };
522 522
523 let mut h = highlight_def(sema, db, def, Some(name_ref.clone())); 523 let mut h = highlight_def(db, def);
524 524
525 if let Some(parent) = name_ref.syntax().parent() { 525 if let Some(parent) = name_ref.syntax().parent() {
526 if matches!(parent.kind(), FIELD_EXPR | RECORD_PAT_FIELD) { 526 if matches!(parent.kind(), FIELD_EXPR | RECORD_PAT_FIELD) {
@@ -736,34 +736,24 @@ fn highlight_method_call(
736 Some(h) 736 Some(h)
737} 737}
738 738
739fn highlight_def( 739fn highlight_def(db: &RootDatabase, def: Definition) -> Highlight {
740 sema: &Semantics<RootDatabase>,
741 db: &RootDatabase,
742 def: Definition,
743 name_ref: Option<ast::NameRef>,
744) -> Highlight {
745 match def { 740 match def {
746 Definition::Macro(_) => HighlightTag::Macro, 741 Definition::Macro(_) => HighlightTag::Macro,
747 Definition::Field(_) => HighlightTag::Field, 742 Definition::Field(_) => HighlightTag::Field,
748 Definition::ModuleDef(def) => match def { 743 Definition::ModuleDef(def) => match def {
749 hir::ModuleDef::Module(_) => HighlightTag::Module, 744 hir::ModuleDef::Module(_) => HighlightTag::Module,
750 hir::ModuleDef::Function(func) => { 745 hir::ModuleDef::Function(func) => {
751 return name_ref 746 let mut h = HighlightTag::Function.into();
752 .and_then(|name_ref| highlight_func_by_name_ref(sema, &name_ref)) 747 if func.is_unsafe(db) {
753 .unwrap_or_else(|| { 748 h |= HighlightModifier::Unsafe;
754 let mut h = HighlightTag::Function.into(); 749 }
755 if func.is_unsafe(db) { 750 if let Some(self_param) = func.self_param(db) {
756 h |= HighlightModifier::Unsafe; 751 match self_param.access(db) {
757 } 752 hir::Access::Exclusive => h |= HighlightModifier::Mutable,
758 753 hir::Access::Shared | hir::Access::Owned => (),
759 match func.self_param(db) { 754 }
760 None => h, 755 }
761 Some(self_param) => match self_param.access(db) { 756 return h;
762 hir::Access::Exclusive => h | HighlightModifier::Mutable,
763 hir::Access::Shared | hir::Access::Owned => h,
764 },
765 }
766 });
767 } 757 }
768 hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HighlightTag::Struct, 758 hir::ModuleDef::Adt(hir::Adt::Struct(_)) => HighlightTag::Struct,
769 hir::ModuleDef::Adt(hir::Adt::Enum(_)) => HighlightTag::Enum, 759 hir::ModuleDef::Adt(hir::Adt::Enum(_)) => HighlightTag::Enum,
diff --git a/crates/ide_db/src/search.rs b/crates/ide_db/src/search.rs
index ce7631c69..fa0830b23 100644
--- a/crates/ide_db/src/search.rs
+++ b/crates/ide_db/src/search.rs
@@ -203,7 +203,7 @@ impl<'a> FindUsages<'a> {
203 } 203 }
204 204
205 pub fn at_least_one(self) -> bool { 205 pub fn at_least_one(self) -> bool {
206 self.all().is_empty() 206 !self.all().is_empty()
207 } 207 }
208 208
209 pub fn all(self) -> Vec<Reference> { 209 pub fn all(self) -> Vec<Reference> {