aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/release.yaml17
-rw-r--r--Cargo.lock4
-rw-r--r--crates/hir/src/lib.rs5
-rw-r--r--crates/hir_def/src/body/lower.rs105
-rw-r--r--crates/hir_def/src/item_tree.rs5
-rw-r--r--crates/hir_expand/src/db.rs3
-rw-r--r--crates/hir_ty/src/tests/macros.rs16
-rw-r--r--crates/ide_assists/src/handlers/generate_is_empty_from_len.rs35
-rw-r--r--crates/mbe/src/tests.rs11
-rw-r--r--crates/parser/src/grammar.rs6
-rw-r--r--crates/parser/src/grammar/expressions.rs6
-rw-r--r--crates/syntax/src/ast/generated/nodes.rs10
-rw-r--r--docs/dev/style.md21
-rw-r--r--xtask/Cargo.toml2
14 files changed, 172 insertions, 74 deletions
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index 32c7cf7ef..ae9dccce9 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -13,6 +13,7 @@ env:
13 CARGO_NET_RETRY: 10 13 CARGO_NET_RETRY: 10
14 RUSTFLAGS: "-D warnings -W unreachable-pub" 14 RUSTFLAGS: "-D warnings -W unreachable-pub"
15 RUSTUP_MAX_RETRIES: 10 15 RUSTUP_MAX_RETRIES: 10
16 FETCH_DEPTH: 200
16 17
17jobs: 18jobs:
18 dist-x86_64-pc-windows-msvc: 19 dist-x86_64-pc-windows-msvc:
@@ -24,6 +25,8 @@ jobs:
24 steps: 25 steps:
25 - name: Checkout repository 26 - name: Checkout repository
26 uses: actions/checkout@v2 27 uses: actions/checkout@v2
28 with:
29 fetch-depth: ${{ env.FETCH_DEPTH }}
27 30
28 # We need to disable the existing toolchain to avoid updating rust-docs 31 # We need to disable the existing toolchain to avoid updating rust-docs
29 # which takes a long time. The fastest way to do this is to rename the 32 # which takes a long time. The fastest way to do this is to rename the
@@ -57,6 +60,8 @@ jobs:
57 steps: 60 steps:
58 - name: Checkout repository 61 - name: Checkout repository
59 uses: actions/checkout@v2 62 uses: actions/checkout@v2
63 with:
64 fetch-depth: ${{ env.FETCH_DEPTH }}
60 65
61 - name: Rename existing rust toolchain 66 - name: Rename existing rust toolchain
62 run: Rename-Item C:\Users\runneradmin\.rustup\toolchains\stable-x86_64-pc-windows-msvc C:\Users\runneradmin\.rustup\toolchains\stable-x86_64-pc-windows-msvc.old 67 run: Rename-Item C:\Users\runneradmin\.rustup\toolchains\stable-x86_64-pc-windows-msvc C:\Users\runneradmin\.rustup\toolchains\stable-x86_64-pc-windows-msvc.old
@@ -87,6 +92,8 @@ jobs:
87 steps: 92 steps:
88 - name: Checkout repository 93 - name: Checkout repository
89 uses: actions/checkout@v2 94 uses: actions/checkout@v2
95 with:
96 fetch-depth: ${{ env.FETCH_DEPTH }}
90 97
91 - name: Install Rust toolchain 98 - name: Install Rust toolchain
92 uses: actions-rs/toolchain@v1 99 uses: actions-rs/toolchain@v1
@@ -139,6 +146,8 @@ jobs:
139 146
140 - name: Checkout repository 147 - name: Checkout repository
141 uses: actions/checkout@v2 148 uses: actions/checkout@v2
149 with:
150 fetch-depth: ${{ env.FETCH_DEPTH }}
142 151
143 - name: Dist 152 - name: Dist
144 run: cargo xtask dist 153 run: cargo xtask dist
@@ -159,6 +168,8 @@ jobs:
159 steps: 168 steps:
160 - name: Checkout repository 169 - name: Checkout repository
161 uses: actions/checkout@v2 170 uses: actions/checkout@v2
171 with:
172 fetch-depth: ${{ env.FETCH_DEPTH }}
162 173
163 - name: Install Rust toolchain 174 - name: Install Rust toolchain
164 uses: actions-rs/toolchain@v1 175 uses: actions-rs/toolchain@v1
@@ -193,6 +204,8 @@ jobs:
193 204
194 - name: Checkout repository 205 - name: Checkout repository
195 uses: actions/checkout@v2 206 uses: actions/checkout@v2
207 with:
208 fetch-depth: ${{ env.FETCH_DEPTH }}
196 209
197 - name: Install Rust toolchain 210 - name: Install Rust toolchain
198 uses: actions-rs/toolchain@v1 211 uses: actions-rs/toolchain@v1
@@ -223,6 +236,8 @@ jobs:
223 236
224 - name: Checkout repository 237 - name: Checkout repository
225 uses: actions/checkout@v2 238 uses: actions/checkout@v2
239 with:
240 fetch-depth: ${{ env.FETCH_DEPTH }}
226 241
227 - name: Install Rust toolchain 242 - name: Install Rust toolchain
228 uses: actions-rs/toolchain@v1 243 uses: actions-rs/toolchain@v1
@@ -259,6 +274,8 @@ jobs:
259 274
260 - name: Checkout repository 275 - name: Checkout repository
261 uses: actions/checkout@v2 276 uses: actions/checkout@v2
277 with:
278 fetch-depth: ${{ env.FETCH_DEPTH }}
262 279
263 - run: echo "HEAD_SHA=$(git rev-parse HEAD)" >> $GITHUB_ENV 280 - run: echo "HEAD_SHA=$(git rev-parse HEAD)" >> $GITHUB_ENV
264 - run: 'echo "HEAD_SHA: $HEAD_SHA"' 281 - run: 'echo "HEAD_SHA: $HEAD_SHA"'
diff --git a/Cargo.lock b/Cargo.lock
index b2d009e38..2efae6a01 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1811,9 +1811,9 @@ checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
1811 1811
1812[[package]] 1812[[package]]
1813name = "ungrammar" 1813name = "ungrammar"
1814version = "1.11.0" 1814version = "1.12.2"
1815source = "registry+https://github.com/rust-lang/crates.io-index" 1815source = "registry+https://github.com/rust-lang/crates.io-index"
1816checksum = "84c629795d377049f2a1dc5f42cf505dc5ba8b28a5df0a03f4183a24480e4a6a" 1816checksum = "df6586a7c530704efe803d49a0b4132dcbdb4063163df39110548e6b5f2373ba"
1817 1817
1818[[package]] 1818[[package]]
1819name = "unicase" 1819name = "unicase"
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index c5161dadd..52939e990 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -52,6 +52,7 @@ use hir_ty::{
52 autoderef, 52 autoderef,
53 display::{write_bounds_like_dyn_trait_with_prefix, HirDisplayError, HirFormatter}, 53 display::{write_bounds_like_dyn_trait_with_prefix, HirDisplayError, HirFormatter},
54 method_resolution::{self, TyFingerprint}, 54 method_resolution::{self, TyFingerprint},
55 primitive::UintTy,
55 to_assoc_type_id, 56 to_assoc_type_id,
56 traits::{FnTrait, Solution, SolutionVariables}, 57 traits::{FnTrait, Solution, SolutionVariables},
57 AliasTy, BoundVar, CallableDefId, CallableSig, Canonical, DebruijnIndex, GenericPredicate, 58 AliasTy, BoundVar, CallableDefId, CallableSig, Canonical, DebruijnIndex, GenericPredicate,
@@ -1631,6 +1632,10 @@ impl Type {
1631 matches!(self.ty.value.interned(&Interner), TyKind::Ref(hir_ty::Mutability::Mut, ..)) 1632 matches!(self.ty.value.interned(&Interner), TyKind::Ref(hir_ty::Mutability::Mut, ..))
1632 } 1633 }
1633 1634
1635 pub fn is_usize(&self) -> bool {
1636 matches!(self.ty.value.interned(&Interner), TyKind::Scalar(Scalar::Uint(UintTy::Usize)))
1637 }
1638
1634 pub fn remove_ref(&self) -> Option<Type> { 1639 pub fn remove_ref(&self) -> Option<Type> {
1635 match &self.ty.value.interned(&Interner) { 1640 match &self.ty.value.interned(&Interner) {
1636 TyKind::Ref(.., ty) => Some(self.derived(ty.clone())), 1641 TyKind::Ref(.., ty) => Some(self.derived(ty.clone())),
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs
index 8934ae6c9..7052058f2 100644
--- a/crates/hir_def/src/body/lower.rs
+++ b/crates/hir_def/src/body/lower.rs
@@ -519,7 +519,7 @@ impl ExprCollector<'_> {
519 } 519 }
520 ast::Expr::MacroCall(e) => { 520 ast::Expr::MacroCall(e) => {
521 let mut ids = vec![]; 521 let mut ids = vec![];
522 self.collect_macro_call(e, syntax_ptr.clone(), |this, expansion| { 522 self.collect_macro_call(e, syntax_ptr.clone(), true, |this, expansion| {
523 ids.push(match expansion { 523 ids.push(match expansion {
524 Some(it) => this.collect_expr(it), 524 Some(it) => this.collect_expr(it),
525 None => this.alloc_expr(Expr::Missing, syntax_ptr.clone()), 525 None => this.alloc_expr(Expr::Missing, syntax_ptr.clone()),
@@ -527,6 +527,17 @@ impl ExprCollector<'_> {
527 }); 527 });
528 ids[0] 528 ids[0]
529 } 529 }
530 ast::Expr::MacroStmts(e) => {
531 // FIXME: these statements should be held by some hir containter
532 for stmt in e.statements() {
533 self.collect_stmt(stmt);
534 }
535 if let Some(expr) = e.expr() {
536 self.collect_expr(expr)
537 } else {
538 self.alloc_expr(Expr::Missing, syntax_ptr)
539 }
540 }
530 } 541 }
531 } 542 }
532 543
@@ -534,6 +545,7 @@ impl ExprCollector<'_> {
534 &mut self, 545 &mut self,
535 e: ast::MacroCall, 546 e: ast::MacroCall,
536 syntax_ptr: AstPtr<ast::Expr>, 547 syntax_ptr: AstPtr<ast::Expr>,
548 is_error_recoverable: bool,
537 mut collector: F, 549 mut collector: F,
538 ) { 550 ) {
539 // File containing the macro call. Expansion errors will be attached here. 551 // File containing the macro call. Expansion errors will be attached here.
@@ -567,7 +579,7 @@ impl ExprCollector<'_> {
567 Some((mark, expansion)) => { 579 Some((mark, expansion)) => {
568 // FIXME: Statements are too complicated to recover from error for now. 580 // FIXME: Statements are too complicated to recover from error for now.
569 // It is because we don't have any hygiene for local variable expansion right now. 581 // It is because we don't have any hygiene for local variable expansion right now.
570 if T::can_cast(syntax::SyntaxKind::MACRO_STMTS) && res.err.is_some() { 582 if !is_error_recoverable && res.err.is_some() {
571 self.expander.exit(self.db, mark); 583 self.expander.exit(self.db, mark);
572 collector(self, None); 584 collector(self, None);
573 } else { 585 } else {
@@ -591,56 +603,55 @@ impl ExprCollector<'_> {
591 } 603 }
592 604
593 fn collect_stmt(&mut self, s: ast::Stmt) -> Option<Vec<Statement>> { 605 fn collect_stmt(&mut self, s: ast::Stmt) -> Option<Vec<Statement>> {
594 let stmt = 606 let stmt = match s {
595 match s { 607 ast::Stmt::LetStmt(stmt) => {
596 ast::Stmt::LetStmt(stmt) => { 608 self.check_cfg(&stmt)?;
597 self.check_cfg(&stmt)?; 609
598 610 let pat = self.collect_pat_opt(stmt.pat());
599 let pat = self.collect_pat_opt(stmt.pat()); 611 let type_ref = stmt.ty().map(|it| TypeRef::from_ast(&self.ctx(), it));
600 let type_ref = stmt.ty().map(|it| TypeRef::from_ast(&self.ctx(), it)); 612 let initializer = stmt.initializer().map(|e| self.collect_expr(e));
601 let initializer = stmt.initializer().map(|e| self.collect_expr(e)); 613 vec![Statement::Let { pat, type_ref, initializer }]
602 vec![Statement::Let { pat, type_ref, initializer }] 614 }
603 } 615 ast::Stmt::ExprStmt(stmt) => {
604 ast::Stmt::ExprStmt(stmt) => { 616 self.check_cfg(&stmt)?;
605 self.check_cfg(&stmt)?; 617
606 618 // Note that macro could be expended to multiple statements
607 // Note that macro could be expended to multiple statements 619 if let Some(ast::Expr::MacroCall(m)) = stmt.expr() {
608 if let Some(ast::Expr::MacroCall(m)) = stmt.expr() { 620 let syntax_ptr = AstPtr::new(&stmt.expr().unwrap());
609 let syntax_ptr = AstPtr::new(&stmt.expr().unwrap()); 621 let mut stmts = vec![];
610 let mut stmts = vec![]; 622
611 623 self.collect_macro_call(m, syntax_ptr.clone(), false, |this, expansion| {
612 self.collect_macro_call(m, syntax_ptr.clone(), |this, expansion| { 624 match expansion {
613 match expansion { 625 Some(expansion) => {
614 Some(expansion) => { 626 let statements: ast::MacroStmts = expansion;
615 let statements: ast::MacroStmts = expansion; 627
616 628 statements.statements().for_each(|stmt| {
617 statements.statements().for_each(|stmt| { 629 if let Some(mut r) = this.collect_stmt(stmt) {
618 if let Some(mut r) = this.collect_stmt(stmt) { 630 stmts.append(&mut r);
619 stmts.append(&mut r);
620 }
621 });
622 if let Some(expr) = statements.expr() {
623 stmts.push(Statement::Expr(this.collect_expr(expr)));
624 } 631 }
625 } 632 });
626 None => { 633 if let Some(expr) = statements.expr() {
627 stmts.push(Statement::Expr( 634 stmts.push(Statement::Expr(this.collect_expr(expr)));
628 this.alloc_expr(Expr::Missing, syntax_ptr.clone()),
629 ));
630 } 635 }
631 } 636 }
632 }); 637 None => {
633 stmts 638 stmts.push(Statement::Expr(
634 } else { 639 this.alloc_expr(Expr::Missing, syntax_ptr.clone()),
635 vec![Statement::Expr(self.collect_expr_opt(stmt.expr()))] 640 ));
636 } 641 }
642 }
643 });
644 stmts
645 } else {
646 vec![Statement::Expr(self.collect_expr_opt(stmt.expr()))]
637 } 647 }
638 ast::Stmt::Item(item) => { 648 }
639 self.check_cfg(&item)?; 649 ast::Stmt::Item(item) => {
650 self.check_cfg(&item)?;
640 651
641 return None; 652 return None;
642 } 653 }
643 }; 654 };
644 655
645 Some(stmt) 656 Some(stmt)
646 } 657 }
diff --git a/crates/hir_def/src/item_tree.rs b/crates/hir_def/src/item_tree.rs
index 09bcb10dc..86239d903 100644
--- a/crates/hir_def/src/item_tree.rs
+++ b/crates/hir_def/src/item_tree.rs
@@ -110,6 +110,11 @@ impl ItemTree {
110 // still need to collect inner items. 110 // still need to collect inner items.
111 ctx.lower_inner_items(e.syntax()) 111 ctx.lower_inner_items(e.syntax())
112 }, 112 },
113 ast::ExprStmt(stmt) => {
114 // Macros can expand to stmt. We return an empty item tree in this case, but
115 // still need to collect inner items.
116 ctx.lower_inner_items(stmt.syntax())
117 },
113 _ => { 118 _ => {
114 panic!("cannot create item tree from {:?} {}", syntax, syntax); 119 panic!("cannot create item tree from {:?} {}", syntax, syntax);
115 }, 120 },
diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs
index 9086e6c17..a3070f1f9 100644
--- a/crates/hir_expand/src/db.rs
+++ b/crates/hir_expand/src/db.rs
@@ -401,13 +401,14 @@ fn to_fragment_kind(db: &dyn AstDatabase, id: MacroCallId) -> FragmentKind {
401 401
402 match parent.kind() { 402 match parent.kind() {
403 MACRO_ITEMS | SOURCE_FILE => FragmentKind::Items, 403 MACRO_ITEMS | SOURCE_FILE => FragmentKind::Items,
404 MACRO_STMTS => FragmentKind::Statement,
404 ITEM_LIST => FragmentKind::Items, 405 ITEM_LIST => FragmentKind::Items,
405 LET_STMT => { 406 LET_STMT => {
406 // FIXME: Handle Pattern 407 // FIXME: Handle Pattern
407 FragmentKind::Expr 408 FragmentKind::Expr
408 } 409 }
409 EXPR_STMT => FragmentKind::Statements, 410 EXPR_STMT => FragmentKind::Statements,
410 BLOCK_EXPR => FragmentKind::Expr, 411 BLOCK_EXPR => FragmentKind::Statements,
411 ARG_LIST => FragmentKind::Expr, 412 ARG_LIST => FragmentKind::Expr,
412 TRY_EXPR => FragmentKind::Expr, 413 TRY_EXPR => FragmentKind::Expr,
413 TUPLE_EXPR => FragmentKind::Expr, 414 TUPLE_EXPR => FragmentKind::Expr,
diff --git a/crates/hir_ty/src/tests/macros.rs b/crates/hir_ty/src/tests/macros.rs
index fb3afaedc..af4f8bb11 100644
--- a/crates/hir_ty/src/tests/macros.rs
+++ b/crates/hir_ty/src/tests/macros.rs
@@ -216,6 +216,22 @@ fn expr_macro_expanded_in_various_places() {
216} 216}
217 217
218#[test] 218#[test]
219fn expr_macro_expanded_in_stmts() {
220 check_infer(
221 r#"
222 macro_rules! id { ($($es:tt)*) => { $($es)* } }
223 fn foo() {
224 id! { let a = (); }
225 }
226 "#,
227 expect![[r#"
228 !0..8 'leta=();': ()
229 57..84 '{ ...); } }': ()
230 "#]],
231 );
232}
233
234#[test]
219fn infer_type_value_macro_having_same_name() { 235fn infer_type_value_macro_having_same_name() {
220 check_infer( 236 check_infer(
221 r#" 237 r#"
diff --git a/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs b/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs
index aa7072f25..b8834d283 100644
--- a/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs
+++ b/crates/ide_assists/src/handlers/generate_is_empty_from_len.rs
@@ -1,7 +1,7 @@
1use hir::{known, HasSource, Name}; 1use hir::{known, HasSource, Name};
2use syntax::{ 2use syntax::{
3 ast::{self, NameOwner}, 3 ast::{self, NameOwner},
4 AstNode, TextRange, 4 AstNode,
5}; 5};
6 6
7use crate::{ 7use crate::{
@@ -51,12 +51,19 @@ pub(crate) fn generate_is_empty_from_len(acc: &mut Assists, ctx: &AssistContext)
51 } 51 }
52 52
53 let impl_ = fn_node.syntax().ancestors().find_map(ast::Impl::cast)?; 53 let impl_ = fn_node.syntax().ancestors().find_map(ast::Impl::cast)?;
54 let len_fn = get_impl_method(ctx, &impl_, &known::len)?;
55 if !len_fn.ret_type(ctx.sema.db).is_usize() {
56 cov_mark::hit!(len_fn_different_return_type);
57 return None;
58 }
59
54 if get_impl_method(ctx, &impl_, &known::is_empty).is_some() { 60 if get_impl_method(ctx, &impl_, &known::is_empty).is_some() {
55 cov_mark::hit!(is_empty_already_implemented); 61 cov_mark::hit!(is_empty_already_implemented);
56 return None; 62 return None;
57 } 63 }
58 64
59 let range = get_text_range_of_len_function(ctx, &impl_)?; 65 let node = len_fn.source(ctx.sema.db)?;
66 let range = node.syntax().value.text_range();
60 67
61 acc.add( 68 acc.add(
62 AssistId("generate_is_empty_from_len", AssistKind::Generate), 69 AssistId("generate_is_empty_from_len", AssistKind::Generate),
@@ -89,13 +96,6 @@ fn get_impl_method(
89 ty.iterate_method_candidates(db, krate, &traits_in_scope, Some(fn_name), |_, func| Some(func)) 96 ty.iterate_method_candidates(db, krate, &traits_in_scope, Some(fn_name), |_, func| Some(func))
90} 97}
91 98
92fn get_text_range_of_len_function(ctx: &AssistContext, impl_: &ast::Impl) -> Option<TextRange> {
93 let db = ctx.sema.db;
94 let func = get_impl_method(ctx, impl_, &known::len)?;
95 let node = func.source(db)?;
96 Some(node.syntax().value.text_range())
97}
98
99#[cfg(test)] 99#[cfg(test)]
100mod tests { 100mod tests {
101 use crate::tests::{check_assist, check_assist_not_applicable}; 101 use crate::tests::{check_assist, check_assist_not_applicable};
@@ -158,6 +158,23 @@ impl MyStruct {
158 } 158 }
159 159
160 #[test] 160 #[test]
161 fn len_fn_different_return_type() {
162 cov_mark::check!(len_fn_different_return_type);
163 check_assist_not_applicable(
164 generate_is_empty_from_len,
165 r#"
166struct MyStruct { data: Vec<String> }
167
168impl MyStruct {
169 p$0ub fn len(&self) -> u32 {
170 self.data.len()
171 }
172}
173"#,
174 );
175 }
176
177 #[test]
161 fn generate_is_empty() { 178 fn generate_is_empty() {
162 check_assist( 179 check_assist(
163 generate_is_empty_from_len, 180 generate_is_empty_from_len,
diff --git a/crates/mbe/src/tests.rs b/crates/mbe/src/tests.rs
index 3a168bb4b..eca0bcc18 100644
--- a/crates/mbe/src/tests.rs
+++ b/crates/mbe/src/tests.rs
@@ -662,12 +662,11 @@ fn test_tt_to_stmts() {
662 [email protected] 662 [email protected]
663 [email protected] "1" 663 [email protected] "1"
664 [email protected] ";" 664 [email protected] ";"
665 [email protected] 665 [email protected]
666 [email protected] 666 [email protected]
667 [email protected] 667 [email protected]
668 [email protected] 668 [email protected]
669 [email protected] 669 [email protected] "a""#,
670 [email protected] "a""#,
671 ); 670 );
672} 671}
673 672
diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs
index 6c0e22722..cebb8f400 100644
--- a/crates/parser/src/grammar.rs
+++ b/crates/parser/src/grammar.rs
@@ -63,11 +63,11 @@ pub(crate) mod fragments {
63 } 63 }
64 64
65 pub(crate) fn stmt(p: &mut Parser) { 65 pub(crate) fn stmt(p: &mut Parser) {
66 expressions::stmt(p, expressions::StmtWithSemi::No) 66 expressions::stmt(p, expressions::StmtWithSemi::No, true)
67 } 67 }
68 68
69 pub(crate) fn stmt_optional_semi(p: &mut Parser) { 69 pub(crate) fn stmt_optional_semi(p: &mut Parser) {
70 expressions::stmt(p, expressions::StmtWithSemi::Optional) 70 expressions::stmt(p, expressions::StmtWithSemi::Optional, false)
71 } 71 }
72 72
73 pub(crate) fn opt_visibility(p: &mut Parser) { 73 pub(crate) fn opt_visibility(p: &mut Parser) {
@@ -133,7 +133,7 @@ pub(crate) mod fragments {
133 continue; 133 continue;
134 } 134 }
135 135
136 expressions::stmt(p, expressions::StmtWithSemi::Optional); 136 expressions::stmt(p, expressions::StmtWithSemi::Optional, true);
137 } 137 }
138 138
139 m.complete(p, MACRO_STMTS); 139 m.complete(p, MACRO_STMTS);
diff --git a/crates/parser/src/grammar/expressions.rs b/crates/parser/src/grammar/expressions.rs
index 5f885edfd..0d9dc9348 100644
--- a/crates/parser/src/grammar/expressions.rs
+++ b/crates/parser/src/grammar/expressions.rs
@@ -54,7 +54,7 @@ fn is_expr_stmt_attr_allowed(kind: SyntaxKind) -> bool {
54 !forbid 54 !forbid
55} 55}
56 56
57pub(super) fn stmt(p: &mut Parser, with_semi: StmtWithSemi) { 57pub(super) fn stmt(p: &mut Parser, with_semi: StmtWithSemi, prefer_expr: bool) {
58 let m = p.start(); 58 let m = p.start();
59 // test attr_on_expr_stmt 59 // test attr_on_expr_stmt
60 // fn foo() { 60 // fn foo() {
@@ -90,7 +90,7 @@ pub(super) fn stmt(p: &mut Parser, with_semi: StmtWithSemi) {
90 p.error(format!("attributes are not allowed on {:?}", kind)); 90 p.error(format!("attributes are not allowed on {:?}", kind));
91 } 91 }
92 92
93 if p.at(T!['}']) { 93 if p.at(T!['}']) || (prefer_expr && p.at(EOF)) {
94 // test attr_on_last_expr_in_block 94 // test attr_on_last_expr_in_block
95 // fn foo() { 95 // fn foo() {
96 // { #[A] bar!()? } 96 // { #[A] bar!()? }
@@ -198,7 +198,7 @@ pub(super) fn expr_block_contents(p: &mut Parser) {
198 continue; 198 continue;
199 } 199 }
200 200
201 stmt(p, StmtWithSemi::Yes) 201 stmt(p, StmtWithSemi::Yes, false)
202 } 202 }
203} 203}
204 204
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs
index 064931aec..6097178b6 100644
--- a/crates/syntax/src/ast/generated/nodes.rs
+++ b/crates/syntax/src/ast/generated/nodes.rs
@@ -1336,6 +1336,7 @@ pub enum Expr {
1336 Literal(Literal), 1336 Literal(Literal),
1337 LoopExpr(LoopExpr), 1337 LoopExpr(LoopExpr),
1338 MacroCall(MacroCall), 1338 MacroCall(MacroCall),
1339 MacroStmts(MacroStmts),
1339 MatchExpr(MatchExpr), 1340 MatchExpr(MatchExpr),
1340 MethodCallExpr(MethodCallExpr), 1341 MethodCallExpr(MethodCallExpr),
1341 ParenExpr(ParenExpr), 1342 ParenExpr(ParenExpr),
@@ -3034,6 +3035,9 @@ impl From<LoopExpr> for Expr {
3034impl From<MacroCall> for Expr { 3035impl From<MacroCall> for Expr {
3035 fn from(node: MacroCall) -> Expr { Expr::MacroCall(node) } 3036 fn from(node: MacroCall) -> Expr { Expr::MacroCall(node) }
3036} 3037}
3038impl From<MacroStmts> for Expr {
3039 fn from(node: MacroStmts) -> Expr { Expr::MacroStmts(node) }
3040}
3037impl From<MatchExpr> for Expr { 3041impl From<MatchExpr> for Expr {
3038 fn from(node: MatchExpr) -> Expr { Expr::MatchExpr(node) } 3042 fn from(node: MatchExpr) -> Expr { Expr::MatchExpr(node) }
3039} 3043}
@@ -3078,8 +3082,8 @@ impl AstNode for Expr {
3078 match kind { 3082 match kind {
3079 ARRAY_EXPR | AWAIT_EXPR | BIN_EXPR | BLOCK_EXPR | BOX_EXPR | BREAK_EXPR | CALL_EXPR 3083 ARRAY_EXPR | AWAIT_EXPR | BIN_EXPR | BLOCK_EXPR | BOX_EXPR | BREAK_EXPR | CALL_EXPR
3080 | CAST_EXPR | CLOSURE_EXPR | CONTINUE_EXPR | EFFECT_EXPR | FIELD_EXPR | FOR_EXPR 3084 | CAST_EXPR | CLOSURE_EXPR | CONTINUE_EXPR | EFFECT_EXPR | FIELD_EXPR | FOR_EXPR
3081 | IF_EXPR | INDEX_EXPR | LITERAL | LOOP_EXPR | MACRO_CALL | MATCH_EXPR 3085 | IF_EXPR | INDEX_EXPR | LITERAL | LOOP_EXPR | MACRO_CALL | MACRO_STMTS
3082 | METHOD_CALL_EXPR | PAREN_EXPR | PATH_EXPR | PREFIX_EXPR | RANGE_EXPR 3086 | MATCH_EXPR | METHOD_CALL_EXPR | PAREN_EXPR | PATH_EXPR | PREFIX_EXPR | RANGE_EXPR
3083 | RECORD_EXPR | REF_EXPR | RETURN_EXPR | TRY_EXPR | TUPLE_EXPR | WHILE_EXPR 3087 | RECORD_EXPR | REF_EXPR | RETURN_EXPR | TRY_EXPR | TUPLE_EXPR | WHILE_EXPR
3084 | YIELD_EXPR => true, 3088 | YIELD_EXPR => true,
3085 _ => false, 3089 _ => false,
@@ -3105,6 +3109,7 @@ impl AstNode for Expr {
3105 LITERAL => Expr::Literal(Literal { syntax }), 3109 LITERAL => Expr::Literal(Literal { syntax }),
3106 LOOP_EXPR => Expr::LoopExpr(LoopExpr { syntax }), 3110 LOOP_EXPR => Expr::LoopExpr(LoopExpr { syntax }),
3107 MACRO_CALL => Expr::MacroCall(MacroCall { syntax }), 3111 MACRO_CALL => Expr::MacroCall(MacroCall { syntax }),
3112 MACRO_STMTS => Expr::MacroStmts(MacroStmts { syntax }),
3108 MATCH_EXPR => Expr::MatchExpr(MatchExpr { syntax }), 3113 MATCH_EXPR => Expr::MatchExpr(MatchExpr { syntax }),
3109 METHOD_CALL_EXPR => Expr::MethodCallExpr(MethodCallExpr { syntax }), 3114 METHOD_CALL_EXPR => Expr::MethodCallExpr(MethodCallExpr { syntax }),
3110 PAREN_EXPR => Expr::ParenExpr(ParenExpr { syntax }), 3115 PAREN_EXPR => Expr::ParenExpr(ParenExpr { syntax }),
@@ -3142,6 +3147,7 @@ impl AstNode for Expr {
3142 Expr::Literal(it) => &it.syntax, 3147 Expr::Literal(it) => &it.syntax,
3143 Expr::LoopExpr(it) => &it.syntax, 3148 Expr::LoopExpr(it) => &it.syntax,
3144 Expr::MacroCall(it) => &it.syntax, 3149 Expr::MacroCall(it) => &it.syntax,
3150 Expr::MacroStmts(it) => &it.syntax,
3145 Expr::MatchExpr(it) => &it.syntax, 3151 Expr::MatchExpr(it) => &it.syntax,
3146 Expr::MethodCallExpr(it) => &it.syntax, 3152 Expr::MethodCallExpr(it) => &it.syntax,
3147 Expr::ParenExpr(it) => &it.syntax, 3153 Expr::ParenExpr(it) => &it.syntax,
diff --git a/docs/dev/style.md b/docs/dev/style.md
index c4eb7bc7a..e4a1672ca 100644
--- a/docs/dev/style.md
+++ b/docs/dev/style.md
@@ -787,6 +787,27 @@ assert!(0 > x);
787 787
788**Rationale:** Less-then comparisons are more intuitive, they correspond spatially to [real line](https://en.wikipedia.org/wiki/Real_line). 788**Rationale:** Less-then comparisons are more intuitive, they correspond spatially to [real line](https://en.wikipedia.org/wiki/Real_line).
789 789
790## If-let
791
792Avoid `if let ... { } else { }` construct, use `match` instead.
793
794```rust
795// GOOD
796match ctx.expected_type.as_ref() {
797 Some(expected_type) => completion_ty == expected_type && !expected_type.is_unit(),
798 None => false,
799}
800
801// BAD
802if let Some(expected_type) = ctx.expected_type.as_ref() {
803 completion_ty == expected_type && !expected_type.is_unit()
804} else {
805 false
806}
807```
808
809**Rational:** `match` is almost always more compact.
810The `else` branch can get a more precise pattern: `None` or `Err(_)` instead of `_`.
790 811
791## Token names 812## Token names
792 813
diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml
index e084f0df6..ad93fbe3b 100644
--- a/xtask/Cargo.toml
+++ b/xtask/Cargo.toml
@@ -11,7 +11,7 @@ anyhow = "1.0.26"
11flate2 = "1.0" 11flate2 = "1.0"
12proc-macro2 = "1.0.8" 12proc-macro2 = "1.0.8"
13quote = "1.0.2" 13quote = "1.0.2"
14ungrammar = "=1.11" 14ungrammar = "=1.12"
15walkdir = "2.3.1" 15walkdir = "2.3.1"
16write-json = "0.1.0" 16write-json = "0.1.0"
17xshell = "0.1" 17xshell = "0.1"