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.into_iter().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 parent = macro_call.syntax().parent()?;
43 let (range, text) = match &*input_expressions {
48 ast::StmtList(__) => {
49 let range = macro_call.syntax().text_range();
50 let range = match whitespace_start(macro_call.syntax().prev_sibling_or_token()) {
51 Some(start) => range.cover_offset(start),
54 (range, String::new())
56 ast::ExprStmt(it) => {
57 let range = it.syntax().text_range();
58 let range = match whitespace_start(it.syntax().prev_sibling_or_token()) {
59 Some(start) => range.cover_offset(start),
62 (range, String::new())
64 _ => (macro_call.syntax().text_range(), "()".to_owned())
70 let wrap = match ast::Expr::cast(parent) {
71 Some(parent) => match (expr, parent) {
72 (ast::Expr::CastExpr(_), ast::Expr::CastExpr(_)) => false,
74 ast::Expr::BoxExpr(_) | ast::Expr::PrefixExpr(_) | ast::Expr::RefExpr(_),
75 ast::Expr::AwaitExpr(_)
76 | ast::Expr::CallExpr(_)
77 | ast::Expr::CastExpr(_)
78 | ast::Expr::FieldExpr(_)
79 | ast::Expr::IndexExpr(_)
80 | ast::Expr::MethodCallExpr(_)
81 | ast::Expr::RangeExpr(_)
82 | ast::Expr::TryExpr(_),
85 ast::Expr::BinExpr(_) | ast::Expr::CastExpr(_) | ast::Expr::RangeExpr(_),
86 ast::Expr::AwaitExpr(_)
87 | ast::Expr::BinExpr(_)
88 | ast::Expr::CallExpr(_)
89 | ast::Expr::CastExpr(_)
90 | ast::Expr::FieldExpr(_)
91 | ast::Expr::IndexExpr(_)
92 | ast::Expr::MethodCallExpr(_)
93 | ast::Expr::PrefixExpr(_)
94 | ast::Expr::RangeExpr(_)
95 | ast::Expr::RefExpr(_)
96 | ast::Expr::TryExpr(_),
103 macro_call.syntax().text_range(),
104 if wrap { format!("({})", expr) } else { expr.to_string() },
107 // dbg!(expr0, expr1, ...)
108 exprs => (macro_call.syntax().text_range(), format!("({})", exprs.iter().format(", "))),
111 acc.add(AssistId("remove_dbg", AssistKind::Refactor), "Remove dbg!()", range, |builder| {
112 builder.replace(range, text);
116 fn whitespace_start(it: Option<SyntaxElement>) -> Option<TextSize> {
117 Some(it?.into_token().and_then(ast::Whitespace::cast)?.syntax().text_range().start())
122 use crate::tests::{check_assist, check_assist_not_applicable};
126 fn check(ra_fixture_before: &str, ra_fixture_after: &str) {
129 &format!("fn main() {{\n{}\n}}", ra_fixture_before),
130 &format!("fn main() {{\n{}\n}}", ra_fixture_after),
135 fn test_remove_dbg() {
136 check("$0dbg!(1 + 1)", "1 + 1");
137 check("dbg!$0(1 + 1)", "1 + 1");
138 check("dbg!(1 $0+ 1)", "1 + 1");
139 check("dbg![$01 + 1]", "1 + 1");
140 check("dbg!{$01 + 1}", "1 + 1");
144 fn test_remove_dbg_not_applicable() {
145 check_assist_not_applicable(remove_dbg, "fn main() {$0vec![1, 2, 3]}");
146 check_assist_not_applicable(remove_dbg, "fn main() {$0dbg(5, 6, 7)}");
147 check_assist_not_applicable(remove_dbg, "fn main() {$0dbg!(5, 6, 7}");
151 fn test_remove_dbg_keep_semicolon_in_let() {
152 // https://github.com/rust-analyzer/rust-analyzer/issues/5129#issuecomment-651399779
154 r#"let res = $0dbg!(1 * 20); // needless comment"#,
155 r#"let res = 1 * 20; // needless comment"#,
157 check(r#"let res = $0dbg!(); // needless comment"#, r#"let res = (); // needless comment"#);
159 r#"let res = $0dbg!(1, 2); // needless comment"#,
160 r#"let res = (1, 2); // needless comment"#,
165 fn test_remove_dbg_cast_cast() {
166 check(r#"let res = $0dbg!(x as u32) as u32;"#, r#"let res = x as u32 as u32;"#);
170 fn test_remove_dbg_prefix() {
171 check(r#"let res = $0dbg!(&result).foo();"#, r#"let res = (&result).foo();"#);
172 check(r#"let res = &$0dbg!(&result);"#, r#"let res = &&result;"#);
173 check(r#"let res = $0dbg!(!result) && true;"#, r#"let res = !result && true;"#);
177 fn test_remove_dbg_post_expr() {
178 check(r#"let res = $0dbg!(fut.await).foo();"#, r#"let res = fut.await.foo();"#);
179 check(r#"let res = $0dbg!(result?).foo();"#, r#"let res = result?.foo();"#);
180 check(r#"let res = $0dbg!(foo as u32).foo();"#, r#"let res = (foo as u32).foo();"#);
181 check(r#"let res = $0dbg!(array[3]).foo();"#, r#"let res = array[3].foo();"#);
182 check(r#"let res = $0dbg!(tuple.3).foo();"#, r#"let res = tuple.3.foo();"#);
186 fn test_remove_dbg_range_expr() {
187 check(r#"let res = $0dbg!(foo..bar).foo();"#, r#"let res = (foo..bar).foo();"#);
188 check(r#"let res = $0dbg!(foo..=bar).foo();"#, r#"let res = (foo..=bar).foo();"#);
192 fn test_remove_empty_dbg() {
193 check_assist(remove_dbg, r#"fn foo() { $0dbg!(); }"#, r#"fn foo() { }"#);
222 println!("Hello, world");
229 println!("Hello, world");
236 fn test_remove_multi_dbg() {
237 check(r#"$0dbg!(0, 1)"#, r#"(0, 1)"#);
238 check(r#"$0dbg!(0, (1, 2))"#, r#"(0, (1, 2))"#);