diff options
-rw-r--r-- | crates/assists/src/handlers/invert_if.rs | 18 | ||||
-rw-r--r-- | crates/assists/src/utils.rs | 21 | ||||
-rw-r--r-- | crates/ide/src/completion.rs | 2 | ||||
-rw-r--r-- | crates/ide/src/runnables.rs | 26 | ||||
-rw-r--r-- | crates/syntax/src/algo.rs | 2 | ||||
-rw-r--r-- | crates/syntax/src/ast/make.rs | 8 | ||||
-rw-r--r-- | docs/dev/style.md | 61 |
7 files changed, 130 insertions, 8 deletions
diff --git a/crates/assists/src/handlers/invert_if.rs b/crates/assists/src/handlers/invert_if.rs index f0e047538..294256297 100644 --- a/crates/assists/src/handlers/invert_if.rs +++ b/crates/assists/src/handlers/invert_if.rs | |||
@@ -106,4 +106,22 @@ mod tests { | |||
106 | "fn f() { i<|>f let Some(_) = Some(1) { 1 } else { 0 } }", | 106 | "fn f() { i<|>f let Some(_) = Some(1) { 1 } else { 0 } }", |
107 | ) | 107 | ) |
108 | } | 108 | } |
109 | |||
110 | #[test] | ||
111 | fn invert_if_option_case() { | ||
112 | check_assist( | ||
113 | invert_if, | ||
114 | "fn f() { if<|> doc_style.is_some() { Class::DocComment } else { Class::Comment } }", | ||
115 | "fn f() { if doc_style.is_none() { Class::Comment } else { Class::DocComment } }", | ||
116 | ) | ||
117 | } | ||
118 | |||
119 | #[test] | ||
120 | fn invert_if_result_case() { | ||
121 | check_assist( | ||
122 | invert_if, | ||
123 | "fn f() { i<|>f doc_style.is_err() { Class::Err } else { Class::Ok } }", | ||
124 | "fn f() { if doc_style.is_ok() { Class::Ok } else { Class::Err } }", | ||
125 | ) | ||
126 | } | ||
109 | } | 127 | } |
diff --git a/crates/assists/src/utils.rs b/crates/assists/src/utils.rs index d071d6502..e15c982e7 100644 --- a/crates/assists/src/utils.rs +++ b/crates/assists/src/utils.rs | |||
@@ -11,7 +11,7 @@ use syntax::{ | |||
11 | ast::{self, make, NameOwner}, | 11 | ast::{self, make, NameOwner}, |
12 | AstNode, Direction, | 12 | AstNode, Direction, |
13 | SyntaxKind::*, | 13 | SyntaxKind::*, |
14 | SyntaxNode, TextSize, T, | 14 | SyntaxNode, SyntaxText, TextSize, T, |
15 | }; | 15 | }; |
16 | 16 | ||
17 | use crate::assist_config::SnippetCap; | 17 | use crate::assist_config::SnippetCap; |
@@ -179,6 +179,25 @@ fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> { | |||
179 | ast::BinOp::EqualityTest => bin.replace_op(T![!=]).map(|it| it.into()), | 179 | ast::BinOp::EqualityTest => bin.replace_op(T![!=]).map(|it| it.into()), |
180 | _ => None, | 180 | _ => None, |
181 | }, | 181 | }, |
182 | ast::Expr::MethodCallExpr(mce) => { | ||
183 | const IS_SOME_TEXT: &str = "is_some"; | ||
184 | const IS_NONE_TEXT: &str = "is_none"; | ||
185 | const IS_OK_TEXT: &str = "is_ok"; | ||
186 | const IS_ERR_TEXT: &str = "is_err"; | ||
187 | |||
188 | let name = mce.name_ref()?; | ||
189 | let name_text = name.text(); | ||
190 | |||
191 | let caller = || -> Option<SyntaxText> { Some(mce.receiver()?.syntax().text()) }; | ||
192 | |||
193 | match name_text { | ||
194 | x if x == IS_SOME_TEXT => make::expr_method_call(IS_NONE_TEXT, caller), | ||
195 | x if x == IS_NONE_TEXT => make::expr_method_call(IS_SOME_TEXT, caller), | ||
196 | x if x == IS_OK_TEXT => make::expr_method_call(IS_ERR_TEXT, caller), | ||
197 | x if x == IS_ERR_TEXT => make::expr_method_call(IS_OK_TEXT, caller), | ||
198 | _ => None, | ||
199 | } | ||
200 | } | ||
182 | ast::Expr::PrefixExpr(pe) if pe.op_kind()? == ast::PrefixOp::Not => pe.expr(), | 201 | ast::Expr::PrefixExpr(pe) if pe.op_kind()? == ast::PrefixOp::Not => pe.expr(), |
183 | // FIXME: | 202 | // FIXME: |
184 | // ast::Expr::Literal(true | false ) | 203 | // ast::Expr::Literal(true | false ) |
diff --git a/crates/ide/src/completion.rs b/crates/ide/src/completion.rs index 25e580d80..33bed6991 100644 --- a/crates/ide/src/completion.rs +++ b/crates/ide/src/completion.rs | |||
@@ -92,7 +92,7 @@ pub use crate::completion::{ | |||
92 | /// already present, it should give all possible variants for the identifier at | 92 | /// already present, it should give all possible variants for the identifier at |
93 | /// the caret. In other words, for | 93 | /// the caret. In other words, for |
94 | /// | 94 | /// |
95 | /// ```no-run | 95 | /// ```no_run |
96 | /// fn f() { | 96 | /// fn f() { |
97 | /// let foo = 92; | 97 | /// let foo = 92; |
98 | /// let _ = bar<|> | 98 | /// let _ = bar<|> |
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index 4139f329e..dd59d9e70 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs | |||
@@ -160,7 +160,7 @@ fn runnable_fn( | |||
160 | RunnableKind::Test { test_id, attr } | 160 | RunnableKind::Test { test_id, attr } |
161 | } else if fn_def.has_atom_attr("bench") { | 161 | } else if fn_def.has_atom_attr("bench") { |
162 | RunnableKind::Bench { test_id } | 162 | RunnableKind::Bench { test_id } |
163 | } else if has_doc_test(&fn_def) { | 163 | } else if has_runnable_doc_test(&fn_def) { |
164 | RunnableKind::DocTest { test_id } | 164 | RunnableKind::DocTest { test_id } |
165 | } else { | 165 | } else { |
166 | return None; | 166 | return None; |
@@ -211,8 +211,13 @@ fn has_test_related_attribute(fn_def: &ast::Fn) -> bool { | |||
211 | .any(|attribute_text| attribute_text.contains("test")) | 211 | .any(|attribute_text| attribute_text.contains("test")) |
212 | } | 212 | } |
213 | 213 | ||
214 | fn has_doc_test(fn_def: &ast::Fn) -> bool { | 214 | fn has_runnable_doc_test(fn_def: &ast::Fn) -> bool { |
215 | fn_def.doc_comment_text().map_or(false, |comment| comment.contains("```")) | 215 | fn_def.doc_comment_text().map_or(false, |comments_text| { |
216 | comments_text.contains("```") | ||
217 | && !comments_text.contains("```ignore") | ||
218 | && !comments_text.contains("```no_run") | ||
219 | && !comments_text.contains("```compile_fail") | ||
220 | }) | ||
216 | } | 221 | } |
217 | 222 | ||
218 | fn runnable_mod( | 223 | fn runnable_mod( |
@@ -417,6 +422,21 @@ fn main() {} | |||
417 | /// let x = 5; | 422 | /// let x = 5; |
418 | /// ``` | 423 | /// ``` |
419 | fn foo() {} | 424 | fn foo() {} |
425 | |||
426 | /// ```no_run | ||
427 | /// let z = 55; | ||
428 | /// ``` | ||
429 | fn should_have_no_runnable() {} | ||
430 | |||
431 | /// ```ignore | ||
432 | /// let z = 55; | ||
433 | /// ``` | ||
434 | fn should_have_no_runnable_2() {} | ||
435 | |||
436 | /// ```compile_fail | ||
437 | /// let z = 55; | ||
438 | /// ``` | ||
439 | fn should_have_no_runnable_3() {} | ||
420 | "#, | 440 | "#, |
421 | &[&BIN, &DOCTEST], | 441 | &[&BIN, &DOCTEST], |
422 | expect![[r#" | 442 | expect![[r#" |
diff --git a/crates/syntax/src/algo.rs b/crates/syntax/src/algo.rs index 6254b38ba..ea199f9b8 100644 --- a/crates/syntax/src/algo.rs +++ b/crates/syntax/src/algo.rs | |||
@@ -32,7 +32,7 @@ pub fn ancestors_at_offset( | |||
32 | /// imprecise: if the cursor is strictly between two nodes of the desired type, | 32 | /// imprecise: if the cursor is strictly between two nodes of the desired type, |
33 | /// as in | 33 | /// as in |
34 | /// | 34 | /// |
35 | /// ```no-run | 35 | /// ```no_run |
36 | /// struct Foo {}|struct Bar; | 36 | /// struct Foo {}|struct Bar; |
37 | /// ``` | 37 | /// ``` |
38 | /// | 38 | /// |
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index d20c085aa..7958721e2 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs | |||
@@ -7,7 +7,7 @@ | |||
7 | use itertools::Itertools; | 7 | use itertools::Itertools; |
8 | use stdx::format_to; | 8 | use stdx::format_to; |
9 | 9 | ||
10 | use crate::{ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, SyntaxToken}; | 10 | use crate::{ast, AstNode, SourceFile, SyntaxKind, SyntaxNode, SyntaxText, SyntaxToken}; |
11 | 11 | ||
12 | pub fn name(text: &str) -> ast::Name { | 12 | pub fn name(text: &str) -> ast::Name { |
13 | ast_from_text(&format!("mod {};", text)) | 13 | ast_from_text(&format!("mod {};", text)) |
@@ -137,6 +137,12 @@ pub fn expr_prefix(op: SyntaxKind, expr: ast::Expr) -> ast::Expr { | |||
137 | pub fn expr_call(f: ast::Expr, arg_list: ast::ArgList) -> ast::Expr { | 137 | pub fn expr_call(f: ast::Expr, arg_list: ast::ArgList) -> ast::Expr { |
138 | expr_from_text(&format!("{}{}", f, arg_list)) | 138 | expr_from_text(&format!("{}{}", f, arg_list)) |
139 | } | 139 | } |
140 | pub fn expr_method_call<F>(text: &str, caller: F) -> Option<ast::Expr> | ||
141 | where | ||
142 | F: FnOnce() -> Option<SyntaxText>, | ||
143 | { | ||
144 | try_expr_from_text(&format!("{}.{}()", caller()?, text)) | ||
145 | } | ||
140 | fn expr_from_text(text: &str) -> ast::Expr { | 146 | fn expr_from_text(text: &str) -> ast::Expr { |
141 | ast_from_text(&format!("const C: () = {};", text)) | 147 | ast_from_text(&format!("const C: () = {};", text)) |
142 | } | 148 | } |
diff --git a/docs/dev/style.md b/docs/dev/style.md index 44f0956c2..bb99c4855 100644 --- a/docs/dev/style.md +++ b/docs/dev/style.md | |||
@@ -181,6 +181,30 @@ fn frobnicate(walrus: Option<Walrus>) { | |||
181 | } | 181 | } |
182 | ``` | 182 | ``` |
183 | 183 | ||
184 | # Early Returns | ||
185 | |||
186 | Do use early returns | ||
187 | |||
188 | ```rust | ||
189 | // Good | ||
190 | fn foo() -> Option<Bar> { | ||
191 | if !condition() { | ||
192 | return None; | ||
193 | } | ||
194 | |||
195 | Some(...) | ||
196 | } | ||
197 | |||
198 | // Not as good | ||
199 | fn foo() -> Option<Bar> { | ||
200 | if condition() { | ||
201 | Some(...) | ||
202 | } else { | ||
203 | None | ||
204 | } | ||
205 | } | ||
206 | ``` | ||
207 | |||
184 | # Getters & Setters | 208 | # Getters & Setters |
185 | 209 | ||
186 | If a field can have any value without breaking invariants, make the field public. | 210 | If a field can have any value without breaking invariants, make the field public. |
@@ -189,7 +213,7 @@ Never provide setters. | |||
189 | 213 | ||
190 | Getters should return borrowed data: | 214 | Getters should return borrowed data: |
191 | 215 | ||
192 | ``` | 216 | ```rust |
193 | struct Person { | 217 | struct Person { |
194 | // Invariant: never empty | 218 | // Invariant: never empty |
195 | first_name: String, | 219 | first_name: String, |
@@ -231,6 +255,41 @@ if words.len() != 2 { | |||
231 | } | 255 | } |
232 | ``` | 256 | ``` |
233 | 257 | ||
258 | # Avoid Monomorphization | ||
259 | |||
260 | Rust uses monomorphization to compile generic code, meaning that for each instantiation of a generic functions with concrete types, the function is compiled afresh, *per crate*. | ||
261 | This allows for exceptionally good performance, but leads to increased compile times. | ||
262 | Runtime performance obeys 80%/20% rule -- only a small fraction of code is hot. | ||
263 | Compile time **does not** obey this rule -- all code has to be compiled. | ||
264 | For this reason, avoid making a lot of code type parametric, *especially* on the boundaries between crates. | ||
265 | |||
266 | ```rust | ||
267 | // Good | ||
268 | fn frbonicate(f: impl FnMut()) { | ||
269 | frobnicate_impl(&mut f) | ||
270 | } | ||
271 | fn frobnicate_impl(f: &mut dyn FnMut()) { | ||
272 | // lots of code | ||
273 | } | ||
274 | |||
275 | // Not as good | ||
276 | fn frbonicate(f: impl FnMut()) { | ||
277 | // lots of code | ||
278 | } | ||
279 | ``` | ||
280 | |||
281 | Avoid `AsRef` polymorphism, it pays back only for widely used libraries: | ||
282 | |||
283 | ```rust | ||
284 | // Good | ||
285 | fn frbonicate(f: &Path) { | ||
286 | } | ||
287 | |||
288 | // Not as good | ||
289 | fn frbonicate(f: impl AsRef<Path>) { | ||
290 | } | ||
291 | ``` | ||
292 | |||
234 | # Documentation | 293 | # Documentation |
235 | 294 | ||
236 | For `.md` and `.adoc` files, prefer a sentence-per-line format, don't wrap lines. | 295 | For `.md` and `.adoc` files, prefer a sentence-per-line format, don't wrap lines. |