aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_assists/src/assists/apply_demorgan.rs14
-rw-r--r--crates/ra_assists/src/assists/early_return.rs11
-rw-r--r--crates/ra_assists/src/assists/invert_if.rs33
-rw-r--r--crates/ra_lsp_server/src/main_loop.rs13
-rw-r--r--crates/ra_syntax/src/ast/make.rs35
5 files changed, 71 insertions, 35 deletions
diff --git a/crates/ra_assists/src/assists/apply_demorgan.rs b/crates/ra_assists/src/assists/apply_demorgan.rs
index dac6280ad..ba08a8223 100644
--- a/crates/ra_assists/src/assists/apply_demorgan.rs
+++ b/crates/ra_assists/src/assists/apply_demorgan.rs
@@ -31,12 +31,14 @@ pub(crate) fn apply_demorgan(ctx: AssistCtx) -> Option<Assist> {
31 if !cursor_in_range { 31 if !cursor_in_range {
32 return None; 32 return None;
33 } 33 }
34
34 let lhs = expr.lhs()?; 35 let lhs = expr.lhs()?;
35 let lhs_range = lhs.syntax().text_range(); 36 let lhs_range = lhs.syntax().text_range();
37 let not_lhs = invert_boolean_expression(lhs);
38
36 let rhs = expr.rhs()?; 39 let rhs = expr.rhs()?;
37 let rhs_range = rhs.syntax().text_range(); 40 let rhs_range = rhs.syntax().text_range();
38 let not_lhs = invert_boolean_expression(&lhs)?; 41 let not_rhs = invert_boolean_expression(rhs);
39 let not_rhs = invert_boolean_expression(&rhs)?;
40 42
41 ctx.add_assist(AssistId("apply_demorgan"), "Apply De Morgan's law", |edit| { 43 ctx.add_assist(AssistId("apply_demorgan"), "Apply De Morgan's law", |edit| {
42 edit.target(op_range); 44 edit.target(op_range);
@@ -77,12 +79,12 @@ mod tests {
77 } 79 }
78 80
79 #[test] 81 #[test]
80 fn demorgan_doesnt_apply_with_cursor_not_on_op() { 82 fn demorgan_general_case() {
81 check_assist_not_applicable(apply_demorgan, "fn f() { <|> !x || !x }") 83 check_assist(apply_demorgan, "fn f() { x ||<|> x }", "fn f() { !(!x &&<|> !x) }")
82 } 84 }
83 85
84 #[test] 86 #[test]
85 fn demorgan_doesnt_apply_when_operands_arent_negated_already() { 87 fn demorgan_doesnt_apply_with_cursor_not_on_op() {
86 check_assist_not_applicable(apply_demorgan, "fn f() { x ||<|> x }") 88 check_assist_not_applicable(apply_demorgan, "fn f() { <|> !x || !x }")
87 } 89 }
88} 90}
diff --git a/crates/ra_assists/src/assists/early_return.rs b/crates/ra_assists/src/assists/early_return.rs
index 7d510b055..8f30dc586 100644
--- a/crates/ra_assists/src/assists/early_return.rs
+++ b/crates/ra_assists/src/assists/early_return.rs
@@ -10,6 +10,7 @@ use ra_syntax::{
10 10
11use crate::{ 11use crate::{
12 assist_ctx::{Assist, AssistCtx}, 12 assist_ctx::{Assist, AssistCtx},
13 assists::invert_if::invert_boolean_expression,
13 AssistId, 14 AssistId,
14}; 15};
15 16
@@ -99,9 +100,13 @@ pub(crate) fn convert_to_guarded_return(ctx: AssistCtx) -> Option<Assist> {
99 let new_block = match if_let_pat { 100 let new_block = match if_let_pat {
100 None => { 101 None => {
101 // If. 102 // If.
102 let early_expression = &(early_expression.syntax().to_string() + ";"); 103 let new_expr = {
103 let new_expr = if_indent_level 104 let then_branch =
104 .increase_indent(make::if_expression(&cond_expr, early_expression)); 105 make::block_expr(once(make::expr_stmt(early_expression).into()), None);
106 let cond = invert_boolean_expression(cond_expr);
107 let e = make::expr_if(cond, then_branch);
108 if_indent_level.increase_indent(e)
109 };
105 replace(new_expr.syntax(), &then_block, &parent_block, &if_expr) 110 replace(new_expr.syntax(), &then_block, &parent_block, &if_expr)
106 } 111 }
107 Some((path, bound_ident)) => { 112 Some((path, bound_ident)) => {
diff --git a/crates/ra_assists/src/assists/invert_if.rs b/crates/ra_assists/src/assists/invert_if.rs
index 694c3642c..983392f21 100644
--- a/crates/ra_assists/src/assists/invert_if.rs
+++ b/crates/ra_assists/src/assists/invert_if.rs
@@ -1,4 +1,4 @@
1use ra_syntax::ast::{self, AstNode}; 1use ra_syntax::ast::{self, make, AstNode};
2use ra_syntax::T; 2use ra_syntax::T;
3 3
4use crate::{Assist, AssistCtx, AssistId}; 4use crate::{Assist, AssistCtx, AssistId};
@@ -35,8 +35,8 @@ pub(crate) fn invert_if(ctx: AssistCtx) -> Option<Assist> {
35 let then_node = expr.then_branch()?.syntax().clone(); 35 let then_node = expr.then_branch()?.syntax().clone();
36 36
37 if let ast::ElseBranch::Block(else_block) = expr.else_branch()? { 37 if let ast::ElseBranch::Block(else_block) = expr.else_branch()? {
38 let flip_cond = invert_boolean_expression(&cond)?;
39 let cond_range = cond.syntax().text_range(); 38 let cond_range = cond.syntax().text_range();
39 let flip_cond = invert_boolean_expression(cond);
40 let else_node = else_block.syntax(); 40 let else_node = else_block.syntax();
41 let else_range = else_node.text_range(); 41 let else_range = else_node.text_range();
42 let then_range = then_node.text_range(); 42 let then_range = then_node.text_range();
@@ -51,16 +51,23 @@ pub(crate) fn invert_if(ctx: AssistCtx) -> Option<Assist> {
51 None 51 None
52} 52}
53 53
54pub(crate) fn invert_boolean_expression(expr: &ast::Expr) -> Option<ast::Expr> { 54pub(crate) fn invert_boolean_expression(expr: ast::Expr) -> ast::Expr {
55 if let Some(expr) = invert_special_case(&expr) {
56 return expr;
57 }
58 make::expr_prefix(T![!], expr)
59}
60
61pub(crate) fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> {
55 match expr { 62 match expr {
56 ast::Expr::BinExpr(bin) => match bin.op_kind()? { 63 ast::Expr::BinExpr(bin) => match bin.op_kind()? {
57 ast::BinOp::NegatedEqualityTest => bin.replace_op(T![==]).map(|it| it.into()), 64 ast::BinOp::NegatedEqualityTest => bin.replace_op(T![==]).map(|it| it.into()),
65 ast::BinOp::EqualityTest => bin.replace_op(T![!=]).map(|it| it.into()),
58 _ => None, 66 _ => None,
59 }, 67 },
60 ast::Expr::PrefixExpr(pe) => match pe.op_kind()? { 68 ast::Expr::PrefixExpr(pe) if pe.op_kind()? == ast::PrefixOp::Not => pe.expr(),
61 ast::PrefixOp::Not => pe.expr(), 69 // FIXME:
62 _ => None, 70 // ast::Expr::Literal(true | false )
63 },
64 _ => None, 71 _ => None,
65 } 72 }
66} 73}
@@ -90,12 +97,16 @@ mod tests {
90 } 97 }
91 98
92 #[test] 99 #[test]
93 fn invert_if_doesnt_apply_with_cursor_not_on_if() { 100 fn invert_if_general_case() {
94 check_assist_not_applicable(invert_if, "fn f() { if !<|>cond { 3 * 2 } else { 1 } }") 101 check_assist(
102 invert_if,
103 "fn f() { i<|>f cond { 3 * 2 } else { 1 } }",
104 "fn f() { i<|>f !cond { 1 } else { 3 * 2 } }",
105 )
95 } 106 }
96 107
97 #[test] 108 #[test]
98 fn invert_if_doesnt_apply_without_negated() { 109 fn invert_if_doesnt_apply_with_cursor_not_on_if() {
99 check_assist_not_applicable(invert_if, "fn f() { i<|>f cond { 3 * 2 } else { 1 } }") 110 check_assist_not_applicable(invert_if, "fn f() { if !<|>cond { 3 * 2 } else { 1 } }")
100 } 111 }
101} 112}
diff --git a/crates/ra_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs
index 12961ba37..ceff82fda 100644
--- a/crates/ra_lsp_server/src/main_loop.rs
+++ b/crates/ra_lsp_server/src/main_loop.rs
@@ -635,12 +635,13 @@ fn on_check_task(
635 635
636 CheckTask::AddDiagnostic { url, diagnostic, fixes } => { 636 CheckTask::AddDiagnostic { url, diagnostic, fixes } => {
637 let path = url.to_file_path().map_err(|()| format!("invalid uri: {}", url))?; 637 let path = url.to_file_path().map_err(|()| format!("invalid uri: {}", url))?;
638 let file_id = world_state 638 let file_id = match world_state.vfs.read().path2file(&path) {
639 .vfs 639 Some(file) => FileId(file.0),
640 .read() 640 None => {
641 .path2file(&path) 641 log::error!("File with cargo diagnostic not found in VFS: {}", path.display());
642 .map(|it| FileId(it.0)) 642 return Ok(());
643 .ok_or_else(|| format!("unknown file: {}", path.to_string_lossy()))?; 643 }
644 };
644 645
645 task_sender 646 task_sender
646 .send(Task::Diagnostic(DiagnosticTask::AddCheck(file_id, diagnostic, fixes)))?; 647 .send(Task::Diagnostic(DiagnosticTask::AddCheck(file_id, diagnostic, fixes)))?;
diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs
index 629503dc5..862eb1172 100644
--- a/crates/ra_syntax/src/ast/make.rs
+++ b/crates/ra_syntax/src/ast/make.rs
@@ -33,6 +33,21 @@ pub fn record_field(name: ast::NameRef, expr: Option<ast::Expr>) -> ast::RecordF
33 } 33 }
34} 34}
35 35
36pub fn block_expr(
37 stmts: impl IntoIterator<Item = ast::Stmt>,
38 tail_expr: Option<ast::Expr>,
39) -> ast::BlockExpr {
40 let mut text = "{\n".to_string();
41 for stmt in stmts.into_iter() {
42 text += &format!(" {}\n", stmt.syntax());
43 }
44 if let Some(tail_expr) = tail_expr {
45 text += &format!(" {}\n", tail_expr.syntax())
46 }
47 text += "}";
48 ast_from_text(&format!("fn f() {}", text))
49}
50
36pub fn block_from_expr(e: ast::Expr) -> ast::Block { 51pub fn block_from_expr(e: ast::Expr) -> ast::Block {
37 return from_text(&format!("{{ {} }}", e.syntax())); 52 return from_text(&format!("{{ {} }}", e.syntax()));
38 53
@@ -62,6 +77,13 @@ pub fn expr_return() -> ast::Expr {
62pub fn expr_match(expr: ast::Expr, match_arm_list: ast::MatchArmList) -> ast::Expr { 77pub fn expr_match(expr: ast::Expr, match_arm_list: ast::MatchArmList) -> ast::Expr {
63 expr_from_text(&format!("match {} {}", expr.syntax(), match_arm_list.syntax())) 78 expr_from_text(&format!("match {} {}", expr.syntax(), match_arm_list.syntax()))
64} 79}
80pub fn expr_if(condition: ast::Expr, then_branch: ast::BlockExpr) -> ast::Expr {
81 expr_from_text(&format!("if {} {}", condition.syntax(), then_branch.syntax()))
82}
83pub fn expr_prefix(op: SyntaxKind, expr: ast::Expr) -> ast::Expr {
84 let token = token(op);
85 expr_from_text(&format!("{}{}", token, expr.syntax()))
86}
65fn expr_from_text(text: &str) -> ast::Expr { 87fn expr_from_text(text: &str) -> ast::Expr {
66 ast_from_text(&format!("const C: () = {};", text)) 88 ast_from_text(&format!("const C: () = {};", text))
67} 89}
@@ -158,14 +180,6 @@ pub fn where_clause(preds: impl IntoIterator<Item = ast::WherePred>) -> ast::Whe
158 } 180 }
159} 181}
160 182
161pub fn if_expression(condition: &ast::Expr, statement: &str) -> ast::IfExpr {
162 ast_from_text(&format!(
163 "fn f() {{ if !{} {{\n {}\n}}\n}}",
164 condition.syntax().text(),
165 statement
166 ))
167}
168
169pub fn let_stmt(pattern: ast::Pat, initializer: Option<ast::Expr>) -> ast::LetStmt { 183pub fn let_stmt(pattern: ast::Pat, initializer: Option<ast::Expr>) -> ast::LetStmt {
170 let text = match initializer { 184 let text = match initializer {
171 Some(it) => format!("let {} = {};", pattern.syntax(), it.syntax()), 185 Some(it) => format!("let {} = {};", pattern.syntax(), it.syntax()),
@@ -173,6 +187,9 @@ pub fn let_stmt(pattern: ast::Pat, initializer: Option<ast::Expr>) -> ast::LetSt
173 }; 187 };
174 ast_from_text(&format!("fn f() {{ {} }}", text)) 188 ast_from_text(&format!("fn f() {{ {} }}", text))
175} 189}
190pub fn expr_stmt(expr: ast::Expr) -> ast::ExprStmt {
191 ast_from_text(&format!("fn f() {{ {}; }}", expr.syntax()))
192}
176 193
177pub fn token(kind: SyntaxKind) -> SyntaxToken { 194pub fn token(kind: SyntaxKind) -> SyntaxToken {
178 tokens::SOURCE_FILE 195 tokens::SOURCE_FILE
@@ -203,7 +220,7 @@ pub mod tokens {
203 use once_cell::sync::Lazy; 220 use once_cell::sync::Lazy;
204 221
205 pub(super) static SOURCE_FILE: Lazy<Parse<SourceFile>> = 222 pub(super) static SOURCE_FILE: Lazy<Parse<SourceFile>> =
206 Lazy::new(|| SourceFile::parse("const C: <()>::Item = (1 != 1, 2 == 2)\n;")); 223 Lazy::new(|| SourceFile::parse("const C: <()>::Item = (1 != 1, 2 == 2, !true)\n;"));
207 224
208 pub fn comma() -> SyntaxToken { 225 pub fn comma() -> SyntaxToken {
209 SOURCE_FILE 226 SOURCE_FILE