1 use itertools::Itertools;
3 ast::{self, AstNode, AstToken},
4 match_ast, NodeOrToken, SyntaxElement, TextSize, T,
7 use crate::{AssistContext, AssistId, AssistKind, Assists};
11 // Removes `dbg!()` macro call.
24 pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
25 let macro_call = ctx.find_node_at_offset::<ast::MacroCall>()?;
26 let tt = macro_call.token_tree()?;
27 let r_delim = NodeOrToken::Token(tt.right_delimiter_token()?);
28 if macro_call.path()?.segment()?.name_ref()?.text() != "dbg"
29 || macro_call.excl_token().is_none()
34 let mac_input = tt.syntax().children_with_tokens().skip(1).take_while(|it| *it != r_delim);
35 let input_expressions = mac_input.group_by(|tok| tok.kind() == T![,]);
36 let input_expressions = input_expressions
38 .filter_map(|(is_sep, group)| (!is_sep).then(|| group))
39 .map(|mut tokens| syntax::hacks::parse_expr_from_str(&tokens.join("")))
40 .collect::<Option<Vec<ast::Expr>>>()?;
42 let macro_expr = ast::MacroExpr::cast(macro_call.syntax().parent()?)?;
43 let parent = macro_expr.syntax().parent()?;
44 let (range, text) = match &*input_expressions {
49 ast::StmtList(__) => {
50 let range = macro_expr.syntax().text_range();
51 let range = match whitespace_start(macro_expr.syntax().prev_sibling_or_token()) {
52 Some(start) => range.cover_offset(start),
55 (range, String::new())
57 ast::ExprStmt(it) => {
58 let range = it.syntax().text_range();
59 let range = match whitespace_start(it.syntax().prev_sibling_or_token()) {
60 Some(start) => range.cover_offset(start),
63 (range, String::new())
65 _ => (macro_call.syntax().text_range(), "()".to_owned())
71 let wrap = match ast::Expr::cast(parent) {
72 Some(parent) => match (expr, parent) {
73 (ast::Expr::CastExpr(_), ast::Expr::CastExpr(_)) => false,
75 ast::Expr::BoxExpr(_) | ast::Expr::PrefixExpr(_) | ast::Expr::RefExpr(_),
76 ast::Expr::AwaitExpr(_)
77 | ast::Expr::CallExpr(_)
78 | ast::Expr::CastExpr(_)
79 | ast::Expr::FieldExpr(_)
80 | ast::Expr::IndexExpr(_)
81 | ast::Expr::MethodCallExpr(_)
82 | ast::Expr::RangeExpr(_)
83 | ast::Expr::TryExpr(_),
86 ast::Expr::BinExpr(_) | ast::Expr::CastExpr(_) | ast::Expr::RangeExpr(_),
87 ast::Expr::AwaitExpr(_)
88 | ast::Expr::BinExpr(_)
89 | ast::Expr::CallExpr(_)
90 | ast::Expr::CastExpr(_)
91 | ast::Expr::FieldExpr(_)
92 | ast::Expr::IndexExpr(_)
93 | ast::Expr::MethodCallExpr(_)
94 | ast::Expr::PrefixExpr(_)
95 | ast::Expr::RangeExpr(_)
96 | ast::Expr::RefExpr(_)
97 | ast::Expr::TryExpr(_),
104 macro_call.syntax().text_range(),
105 if wrap { format!("({expr})") } else { expr.to_string() },
108 // dbg!(expr0, expr1, ...)
109 exprs => (macro_call.syntax().text_range(), format!("({})", exprs.iter().format(", "))),
112 acc.add(AssistId("remove_dbg", AssistKind::Refactor), "Remove dbg!()", range, |builder| {
113 builder.replace(range, text);
117 fn whitespace_start(it: Option<SyntaxElement>) -> Option<TextSize> {
118 Some(it?.into_token().and_then(ast::Whitespace::cast)?.syntax().text_range().start())
123 use crate::tests::{check_assist, check_assist_not_applicable};
127 fn check(ra_fixture_before: &str, ra_fixture_after: &str) {
130 &format!("fn main() {{\n{ra_fixture_before}\n}}"),
131 &format!("fn main() {{\n{ra_fixture_after}\n}}"),
136 fn test_remove_dbg() {
137 check("$0dbg!(1 + 1)", "1 + 1");
138 check("dbg!$0(1 + 1)", "1 + 1");
139 check("dbg!(1 $0+ 1)", "1 + 1");
140 check("dbg![$01 + 1]", "1 + 1");
141 check("dbg!{$01 + 1}", "1 + 1");
145 fn test_remove_dbg_not_applicable() {
146 check_assist_not_applicable(remove_dbg, "fn main() {$0vec![1, 2, 3]}");
147 check_assist_not_applicable(remove_dbg, "fn main() {$0dbg(5, 6, 7)}");
148 check_assist_not_applicable(remove_dbg, "fn main() {$0dbg!(5, 6, 7}");
152 fn test_remove_dbg_keep_semicolon_in_let() {
153 // https://github.com/rust-lang/rust-analyzer/issues/5129#issuecomment-651399779
155 r#"let res = $0dbg!(1 * 20); // needless comment"#,
156 r#"let res = 1 * 20; // needless comment"#,
158 check(r#"let res = $0dbg!(); // needless comment"#, r#"let res = (); // needless comment"#);
160 r#"let res = $0dbg!(1, 2); // needless comment"#,
161 r#"let res = (1, 2); // needless comment"#,
166 fn test_remove_dbg_cast_cast() {
167 check(r#"let res = $0dbg!(x as u32) as u32;"#, r#"let res = x as u32 as u32;"#);
171 fn test_remove_dbg_prefix() {
172 check(r#"let res = $0dbg!(&result).foo();"#, r#"let res = (&result).foo();"#);
173 check(r#"let res = &$0dbg!(&result);"#, r#"let res = &&result;"#);
174 check(r#"let res = $0dbg!(!result) && true;"#, r#"let res = !result && true;"#);
178 fn test_remove_dbg_post_expr() {
179 check(r#"let res = $0dbg!(fut.await).foo();"#, r#"let res = fut.await.foo();"#);
180 check(r#"let res = $0dbg!(result?).foo();"#, r#"let res = result?.foo();"#);
181 check(r#"let res = $0dbg!(foo as u32).foo();"#, r#"let res = (foo as u32).foo();"#);
182 check(r#"let res = $0dbg!(array[3]).foo();"#, r#"let res = array[3].foo();"#);
183 check(r#"let res = $0dbg!(tuple.3).foo();"#, r#"let res = tuple.3.foo();"#);
187 fn test_remove_dbg_range_expr() {
188 check(r#"let res = $0dbg!(foo..bar).foo();"#, r#"let res = (foo..bar).foo();"#);
189 check(r#"let res = $0dbg!(foo..=bar).foo();"#, r#"let res = (foo..=bar).foo();"#);
193 fn test_remove_empty_dbg() {
194 check_assist(remove_dbg, r#"fn foo() { $0dbg!(); }"#, r#"fn foo() { }"#);
223 println!("Hello, world");
230 println!("Hello, world");
237 fn test_remove_multi_dbg() {
238 check(r#"$0dbg!(0, 1)"#, r#"(0, 1)"#);
239 check(r#"$0dbg!(0, (1, 2))"#, r#"(0, (1, 2))"#);