aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_assists/src/ast_transform.rs2
-rw-r--r--crates/ra_assists/src/handlers/fill_match_arms.rs50
-rw-r--r--crates/ra_hir_def/src/path.rs2
-rw-r--r--crates/ra_hir_ty/src/_match.rs255
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs1
-rw-r--r--crates/ra_ide/src/snapshots/highlighting.html8
-rw-r--r--crates/ra_ide/src/syntax_highlighting.rs37
-rw-r--r--crates/ra_ide/src/syntax_highlighting/tests.rs8
-rw-r--r--crates/ra_parser/src/grammar/expressions.rs6
-rw-r--r--crates/ra_syntax/src/lib.rs3
-rw-r--r--crates/ra_syntax/test_data/parser/inline/err/0009_attr_on_expr_not_allowed.rast1
-rw-r--r--crates/test_utils/src/lib.rs12
12 files changed, 240 insertions, 145 deletions
diff --git a/crates/ra_assists/src/ast_transform.rs b/crates/ra_assists/src/ast_transform.rs
index 3079a02a2..00fa95b6c 100644
--- a/crates/ra_assists/src/ast_transform.rs
+++ b/crates/ra_assists/src/ast_transform.rs
@@ -106,6 +106,7 @@ impl<'a> SubstituteTypeParams<'a> {
106 _ => return None, 106 _ => return None,
107 }; 107 };
108 // FIXME: use `hir::Path::from_src` instead. 108 // FIXME: use `hir::Path::from_src` instead.
109 #[allow(deprecated)]
109 let path = hir::Path::from_ast(path)?; 110 let path = hir::Path::from_ast(path)?;
110 let resolution = self.source_scope.resolve_hir_path(&path)?; 111 let resolution = self.source_scope.resolve_hir_path(&path)?;
111 match resolution { 112 match resolution {
@@ -150,6 +151,7 @@ impl<'a> QualifyPaths<'a> {
150 return None; 151 return None;
151 } 152 }
152 // FIXME: use `hir::Path::from_src` instead. 153 // FIXME: use `hir::Path::from_src` instead.
154 #[allow(deprecated)]
153 let hir_path = hir::Path::from_ast(p.clone()); 155 let hir_path = hir::Path::from_ast(p.clone());
154 let resolution = self.source_scope.resolve_hir_path(&hir_path?)?; 156 let resolution = self.source_scope.resolve_hir_path(&hir_path?)?;
155 match resolution { 157 match resolution {
diff --git a/crates/ra_assists/src/handlers/fill_match_arms.rs b/crates/ra_assists/src/handlers/fill_match_arms.rs
index cc303285b..569efb768 100644
--- a/crates/ra_assists/src/handlers/fill_match_arms.rs
+++ b/crates/ra_assists/src/handlers/fill_match_arms.rs
@@ -136,8 +136,20 @@ fn is_variant_missing(existing_arms: &mut Vec<MatchArm>, var: &Pat) -> bool {
136} 136}
137 137
138fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool { 138fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool {
139 let pat_head = pat.syntax().first_child().map(|node| node.text()); 139 let first_node_text = |pat: &Pat| pat.syntax().first_child().map(|node| node.text());
140 let var_head = var.syntax().first_child().map(|node| node.text()); 140
141 let pat_head = match pat {
142 Pat::BindPat(bind_pat) => {
143 if let Some(p) = bind_pat.pat() {
144 first_node_text(&p)
145 } else {
146 return false;
147 }
148 }
149 pat => first_node_text(pat),
150 };
151
152 let var_head = first_node_text(var);
141 153
142 pat_head == var_head 154 pat_head == var_head
143} 155}
@@ -351,6 +363,40 @@ mod tests {
351 } 363 }
352 364
353 #[test] 365 #[test]
366 fn partial_fill_bind_pat() {
367 check_assist(
368 fill_match_arms,
369 r#"
370 enum A {
371 As,
372 Bs,
373 Cs(Option<i32>),
374 }
375 fn main() {
376 match A::As<|> {
377 A::As(_) => {}
378 a @ A::Bs(_) => {}
379 }
380 }
381 "#,
382 r#"
383 enum A {
384 As,
385 Bs,
386 Cs(Option<i32>),
387 }
388 fn main() {
389 match A::As {
390 A::As(_) => {}
391 a @ A::Bs(_) => {}
392 $0A::Cs(_) => {}
393 }
394 }
395 "#,
396 );
397 }
398
399 #[test]
354 fn fill_match_arms_empty_body() { 400 fn fill_match_arms_empty_body() {
355 check_assist( 401 check_assist(
356 fill_match_arms, 402 fill_match_arms,
diff --git a/crates/ra_hir_def/src/path.rs b/crates/ra_hir_def/src/path.rs
index ba16442bd..190d6d98d 100644
--- a/crates/ra_hir_def/src/path.rs
+++ b/crates/ra_hir_def/src/path.rs
@@ -154,7 +154,7 @@ pub enum GenericArg {
154 154
155impl Path { 155impl Path {
156 /// Converts an `ast::Path` to `Path`. Works with use trees. 156 /// Converts an `ast::Path` to `Path`. Works with use trees.
157 /// DEPRECATED: It does not handle `$crate` from macro call. 157 #[deprecated = "Doesn't handle hygiene, don't add new calls, remove old ones"]
158 pub fn from_ast(path: ast::Path) -> Option<Path> { 158 pub fn from_ast(path: ast::Path) -> Option<Path> {
159 lower::lower_path(path, &Hygiene::new_unhygienic()) 159 lower::lower_path(path, &Hygiene::new_unhygienic())
160 } 160 }
diff --git a/crates/ra_hir_ty/src/_match.rs b/crates/ra_hir_ty/src/_match.rs
index 3e6e1e333..fff257193 100644
--- a/crates/ra_hir_ty/src/_match.rs
+++ b/crates/ra_hir_ty/src/_match.rs
@@ -8,11 +8,11 @@
8//! This file includes the logic for exhaustiveness and usefulness checking for 8//! This file includes the logic for exhaustiveness and usefulness checking for
9//! pattern-matching. Specifically, given a list of patterns for a type, we can 9//! pattern-matching. Specifically, given a list of patterns for a type, we can
10//! tell whether: 10//! tell whether:
11//! (a) the patterns cover every possible constructor for the type [exhaustiveness] 11//! - (a) the patterns cover every possible constructor for the type (exhaustiveness).
12//! (b) each pattern is necessary [usefulness] 12//! - (b) each pattern is necessary (usefulness).
13//! 13//!
14//! The algorithm implemented here is a modified version of the one described in: 14//! The algorithm implemented here is a modified version of the one described in
15//! http://moscova.inria.fr/~maranget/papers/warn/index.html 15//! <http://moscova.inria.fr/~maranget/papers/warn/index.html>.
16//! However, to save future implementors from reading the original paper, we 16//! However, to save future implementors from reading the original paper, we
17//! summarise the algorithm here to hopefully save time and be a little clearer 17//! summarise the algorithm here to hopefully save time and be a little clearer
18//! (without being so rigorous). 18//! (without being so rigorous).
@@ -37,20 +37,26 @@
37//! new pattern `p`. 37//! new pattern `p`.
38//! 38//!
39//! For example, say we have the following: 39//! For example, say we have the following:
40//!
41//! ```ignore
42//! // x: (Option<bool>, Result<()>)
43//! match x {
44//! (Some(true), _) => {}
45//! (None, Err(())) => {}
46//! (None, Err(_)) => {}
47//! }
40//! ``` 48//! ```
41//! // x: (Option<bool>, Result<()>) 49//!
42//! match x {
43//! (Some(true), _) => {}
44//! (None, Err(())) => {}
45//! (None, Err(_)) => {}
46//! }
47//! ```
48//! Here, the matrix `P` starts as: 50//! Here, the matrix `P` starts as:
51//!
52//! ```text
49//! [ 53//! [
50//! [(Some(true), _)], 54//! [(Some(true), _)],
51//! [(None, Err(()))], 55//! [(None, Err(()))],
52//! [(None, Err(_))], 56//! [(None, Err(_))],
53//! ] 57//! ]
58//! ```
59//!
54//! We can tell it's not exhaustive, because `U(P, _)` is true (we're not covering 60//! We can tell it's not exhaustive, because `U(P, _)` is true (we're not covering
55//! `[(Some(false), _)]`, for instance). In addition, row 3 is not useful, because 61//! `[(Some(false), _)]`, for instance). In addition, row 3 is not useful, because
56//! all the values it covers are already covered by row 2. 62//! all the values it covers are already covered by row 2.
@@ -60,53 +66,61 @@
60//! To match the paper, the top of the stack is at the beginning / on the left. 66//! To match the paper, the top of the stack is at the beginning / on the left.
61//! 67//!
62//! There are two important operations on pattern-stacks necessary to understand the algorithm: 68//! There are two important operations on pattern-stacks necessary to understand the algorithm:
63//! 1. We can pop a given constructor off the top of a stack. This operation is called
64//! `specialize`, and is denoted `S(c, p)` where `c` is a constructor (like `Some` or
65//! `None`) and `p` a pattern-stack.
66//! If the pattern on top of the stack can cover `c`, this removes the constructor and
67//! pushes its arguments onto the stack. It also expands OR-patterns into distinct patterns.
68//! Otherwise the pattern-stack is discarded.
69//! This essentially filters those pattern-stacks whose top covers the constructor `c` and
70//! discards the others.
71//! 69//!
72//! For example, the first pattern above initially gives a stack `[(Some(true), _)]`. If we 70//! 1. We can pop a given constructor off the top of a stack. This operation is called
73//! pop the tuple constructor, we are left with `[Some(true), _]`, and if we then pop the 71//! `specialize`, and is denoted `S(c, p)` where `c` is a constructor (like `Some` or
74//! `Some` constructor we get `[true, _]`. If we had popped `None` instead, we would get 72//! `None`) and `p` a pattern-stack.
75//! nothing back. 73//! If the pattern on top of the stack can cover `c`, this removes the constructor and
74//! pushes its arguments onto the stack. It also expands OR-patterns into distinct patterns.
75//! Otherwise the pattern-stack is discarded.
76//! This essentially filters those pattern-stacks whose top covers the constructor `c` and
77//! discards the others.
78//!
79//! For example, the first pattern above initially gives a stack `[(Some(true), _)]`. If we
80//! pop the tuple constructor, we are left with `[Some(true), _]`, and if we then pop the
81//! `Some` constructor we get `[true, _]`. If we had popped `None` instead, we would get
82//! nothing back.
83//!
84//! This returns zero or more new pattern-stacks, as follows. We look at the pattern `p_1`
85//! on top of the stack, and we have four cases:
86//!
87//! * 1.1. `p_1 = c(r_1, .., r_a)`, i.e. the top of the stack has constructor `c`. We push onto
88//! the stack the arguments of this constructor, and return the result:
89//!
90//! r_1, .., r_a, p_2, .., p_n
91//!
92//! * 1.2. `p_1 = c'(r_1, .., r_a')` where `c ≠ c'`. We discard the current stack and return
93//! nothing.
94//! * 1.3. `p_1 = _`. We push onto the stack as many wildcards as the constructor `c` has
95//! arguments (its arity), and return the resulting stack:
76//! 96//!
77//! This returns zero or more new pattern-stacks, as follows. We look at the pattern `p_1` 97//! _, .., _, p_2, .., p_n
78//! on top of the stack, and we have four cases:
79//! 1.1. `p_1 = c(r_1, .., r_a)`, i.e. the top of the stack has constructor `c`. We
80//! push onto the stack the arguments of this constructor, and return the result:
81//! r_1, .., r_a, p_2, .., p_n
82//! 1.2. `p_1 = c'(r_1, .., r_a')` where `c ≠ c'`. We discard the current stack and
83//! return nothing.
84//! 1.3. `p_1 = _`. We push onto the stack as many wildcards as the constructor `c` has
85//! arguments (its arity), and return the resulting stack:
86//! _, .., _, p_2, .., p_n
87//! 1.4. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting
88//! stack:
89//! S(c, (r_1, p_2, .., p_n))
90//! S(c, (r_2, p_2, .., p_n))
91//! 98//!
92//! 2. We can pop a wildcard off the top of the stack. This is called `D(p)`, where `p` is 99//! * 1.4. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting stack:
93//! a pattern-stack.
94//! This is used when we know there are missing constructor cases, but there might be
95//! existing wildcard patterns, so to check the usefulness of the matrix, we have to check
96//! all its *other* components.
97//! 100//!
98//! It is computed as follows. We look at the pattern `p_1` on top of the stack, 101//! S(c, (r_1, p_2, .., p_n))
99//! and we have three cases: 102//! S(c, (r_2, p_2, .., p_n))
100//! 1.1. `p_1 = c(r_1, .., r_a)`. We discard the current stack and return nothing.
101//! 1.2. `p_1 = _`. We return the rest of the stack:
102//! p_2, .., p_n
103//! 1.3. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting
104//! stack.
105//! D((r_1, p_2, .., p_n))
106//! D((r_2, p_2, .., p_n))
107//! 103//!
108//! Note that the OR-patterns are not always used directly in Rust, but are used to derive the 104//! 2. We can pop a wildcard off the top of the stack. This is called `D(p)`, where `p` is
109//! exhaustive integer matching rules, so they're written here for posterity. 105//! a pattern-stack.
106//! This is used when we know there are missing constructor cases, but there might be
107//! existing wildcard patterns, so to check the usefulness of the matrix, we have to check
108//! all its *other* components.
109//!
110//! It is computed as follows. We look at the pattern `p_1` on top of the stack,
111//! and we have three cases:
112//! * 1.1. `p_1 = c(r_1, .., r_a)`. We discard the current stack and return nothing.
113//! * 1.2. `p_1 = _`. We return the rest of the stack:
114//!
115//! p_2, .., p_n
116//!
117//! * 1.3. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting stack:
118//!
119//! D((r_1, p_2, .., p_n))
120//! D((r_2, p_2, .., p_n))
121//!
122//! Note that the OR-patterns are not always used directly in Rust, but are used to derive the
123//! exhaustive integer matching rules, so they're written here for posterity.
110//! 124//!
111//! Both those operations extend straightforwardly to a list or pattern-stacks, i.e. a matrix, by 125//! Both those operations extend straightforwardly to a list or pattern-stacks, i.e. a matrix, by
112//! working row-by-row. Popping a constructor ends up keeping only the matrix rows that start with 126//! working row-by-row. Popping a constructor ends up keeping only the matrix rows that start with
@@ -120,73 +134,88 @@
120//! operates principally on the first component of the matrix and new pattern-stack `p`. 134//! operates principally on the first component of the matrix and new pattern-stack `p`.
121//! This algorithm is realised in the `is_useful` function. 135//! This algorithm is realised in the `is_useful` function.
122//! 136//!
123//! Base case. (`n = 0`, i.e., an empty tuple pattern) 137//! Base case (`n = 0`, i.e., an empty tuple pattern):
124//! - If `P` already contains an empty pattern (i.e., if the number of patterns `m > 0`), 138//! - If `P` already contains an empty pattern (i.e., if the number of patterns `m > 0`), then
125//! then `U(P, p)` is false. 139//! `U(P, p)` is false.
126//! - Otherwise, `P` must be empty, so `U(P, p)` is true. 140//! - Otherwise, `P` must be empty, so `U(P, p)` is true.
141//!
142//! Inductive step (`n > 0`, i.e., whether there's at least one column [which may then be expanded
143//! into further columns later]). We're going to match on the top of the new pattern-stack, `p_1`:
144//!
145//! - If `p_1 == c(r_1, .., r_a)`, i.e. we have a constructor pattern.
146//! Then, the usefulness of `p_1` can be reduced to whether it is useful when
147//! we ignore all the patterns in the first column of `P` that involve other constructors.
148//! This is where `S(c, P)` comes in:
149//!
150//! ```text
151//! U(P, p) := U(S(c, P), S(c, p))
152//! ```
153//!
154//! This special case is handled in `is_useful_specialized`.
155//!
156//! For example, if `P` is:
157//!
158//! ```text
159//! [
160//! [Some(true), _],
161//! [None, 0],
162//! ]
163//! ```
127//! 164//!
128//! Inductive step. (`n > 0`, i.e., whether there's at least one column 165//! and `p` is `[Some(false), 0]`, then we don't care about row 2 since we know `p` only
129//! [which may then be expanded into further columns later]) 166//! matches values that row 2 doesn't. For row 1 however, we need to dig into the
130//! We're going to match on the top of the new pattern-stack, `p_1`. 167//! arguments of `Some` to know whether some new value is covered. So we compute
131//! - If `p_1 == c(r_1, .., r_a)`, i.e. we have a constructor pattern. 168//! `U([[true, _]], [false, 0])`.
132//! Then, the usefulness of `p_1` can be reduced to whether it is useful when
133//! we ignore all the patterns in the first column of `P` that involve other constructors.
134//! This is where `S(c, P)` comes in:
135//! `U(P, p) := U(S(c, P), S(c, p))`
136//! This special case is handled in `is_useful_specialized`.
137//! 169//!
138//! For example, if `P` is: 170//! - If `p_1 == _`, then we look at the list of constructors that appear in the first component of
139//! [ 171//! the rows of `P`:
140//! [Some(true), _], 172//! - If there are some constructors that aren't present, then we might think that the
141//! [None, 0], 173//! wildcard `_` is useful, since it covers those constructors that weren't covered
142//! ] 174//! before.
143//! and `p` is [Some(false), 0], then we don't care about row 2 since we know `p` only 175//! That's almost correct, but only works if there were no wildcards in those first
144//! matches values that row 2 doesn't. For row 1 however, we need to dig into the 176//! components. So we need to check that `p` is useful with respect to the rows that
145//! arguments of `Some` to know whether some new value is covered. So we compute 177//! start with a wildcard, if there are any. This is where `D` comes in:
146//! `U([[true, _]], [false, 0])`. 178//! `U(P, p) := U(D(P), D(p))`
147//! 179//!
148//! - If `p_1 == _`, then we look at the list of constructors that appear in the first 180//! For example, if `P` is:
149//! component of the rows of `P`: 181//! ```text
150//! + If there are some constructors that aren't present, then we might think that the 182//! [
151//! wildcard `_` is useful, since it covers those constructors that weren't covered 183//! [_, true, _],
152//! before. 184//! [None, false, 1],
153//! That's almost correct, but only works if there were no wildcards in those first 185//! ]
154//! components. So we need to check that `p` is useful with respect to the rows that 186//! ```
155//! start with a wildcard, if there are any. This is where `D` comes in: 187//! and `p` is `[_, false, _]`, the `Some` constructor doesn't appear in `P`. So if we
156//! `U(P, p) := U(D(P), D(p))` 188//! only had row 2, we'd know that `p` is useful. However row 1 starts with a
189//! wildcard, so we need to check whether `U([[true, _]], [false, 1])`.
157//! 190//!
158//! For example, if `P` is: 191//! - Otherwise, all possible constructors (for the relevant type) are present. In this
159//! [ 192//! case we must check whether the wildcard pattern covers any unmatched value. For
160//! [_, true, _], 193//! that, we can think of the `_` pattern as a big OR-pattern that covers all
161//! [None, false, 1], 194//! possible constructors. For `Option`, that would mean `_ = None | Some(_)` for
162//! ] 195//! example. The wildcard pattern is useful in this case if it is useful when
163//! and `p` is [_, false, _], the `Some` constructor doesn't appear in `P`. So if we 196//! specialized to one of the possible constructors. So we compute:
164//! only had row 2, we'd know that `p` is useful. However row 1 starts with a 197//! `U(P, p) := ∃(k ϵ constructors) U(S(k, P), S(k, p))`
165//! wildcard, so we need to check whether `U([[true, _]], [false, 1])`.
166//! 198//!
167//! + Otherwise, all possible constructors (for the relevant type) are present. In this 199//! For example, if `P` is:
168//! case we must check whether the wildcard pattern covers any unmatched value. For 200//! ```text
169//! that, we can think of the `_` pattern as a big OR-pattern that covers all 201//! [
170//! possible constructors. For `Option`, that would mean `_ = None | Some(_)` for 202//! [Some(true), _],
171//! example. The wildcard pattern is useful in this case if it is useful when 203//! [None, false],
172//! specialized to one of the possible constructors. So we compute: 204//! ]
173//! `U(P, p) := ∃(k ϵ constructors) U(S(k, P), S(k, p))` 205//! ```
206//! and `p` is `[_, false]`, both `None` and `Some` constructors appear in the first
207//! components of `P`. We will therefore try popping both constructors in turn: we
208//! compute `U([[true, _]], [_, false])` for the `Some` constructor, and `U([[false]],
209//! [false])` for the `None` constructor. The first case returns true, so we know that
210//! `p` is useful for `P`. Indeed, it matches `[Some(false), _]` that wasn't matched
211//! before.
174//! 212//!
175//! For example, if `P` is: 213//! - If `p_1 == r_1 | r_2`, then the usefulness depends on each `r_i` separately:
176//! [
177//! [Some(true), _],
178//! [None, false],
179//! ]
180//! and `p` is [_, false], both `None` and `Some` constructors appear in the first
181//! components of `P`. We will therefore try popping both constructors in turn: we
182//! compute U([[true, _]], [_, false]) for the `Some` constructor, and U([[false]],
183//! [false]) for the `None` constructor. The first case returns true, so we know that
184//! `p` is useful for `P`. Indeed, it matches `[Some(false), _]` that wasn't matched
185//! before.
186//! 214//!
187//! - If `p_1 == r_1 | r_2`, then the usefulness depends on each `r_i` separately: 215//! ```text
188//! `U(P, p) := U(P, (r_1, p_2, .., p_n)) 216//! U(P, p) := U(P, (r_1, p_2, .., p_n))
189//! || U(P, (r_2, p_2, .., p_n))` 217//! || U(P, (r_2, p_2, .., p_n))
218//! ```
190use std::sync::Arc; 219use std::sync::Arc;
191 220
192use smallvec::{smallvec, SmallVec}; 221use smallvec::{smallvec, SmallVec};
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index 9f4c582d0..560fb19e6 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -381,6 +381,7 @@ impl<'a> CompletionContext<'a> {
381 self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); 381 self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some();
382 self.has_type_args = segment.type_arg_list().is_some(); 382 self.has_type_args = segment.type_arg_list().is_some();
383 383
384 #[allow(deprecated)]
384 if let Some(path) = hir::Path::from_ast(path.clone()) { 385 if let Some(path) = hir::Path::from_ast(path.clone()) {
385 if let Some(path_prefix) = path.qualifier() { 386 if let Some(path_prefix) = path.qualifier() {
386 self.path_prefix = Some(path_prefix); 387 self.path_prefix = Some(path_prefix);
diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/src/snapshots/highlighting.html
index 33548d43c..5c2ff6ab5 100644
--- a/crates/ra_ide/src/snapshots/highlighting.html
+++ b/crates/ra_ide/src/snapshots/highlighting.html
@@ -62,6 +62,12 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
62 } 62 }
63} 63}
64 64
65<span class="macro">macro_rules!</span> <span class="macro declaration">noop</span> {
66 ($expr:expr) =&gt; {
67 $expr
68 }
69}
70
65<span class="comment">// comment</span> 71<span class="comment">// comment</span>
66<span class="keyword">fn</span> <span class="function declaration">main</span>() { 72<span class="keyword">fn</span> <span class="function declaration">main</span>() {
67 <span class="macro">println!</span>(<span class="string_literal">"Hello, {}!"</span>, <span class="numeric_literal">92</span>); 73 <span class="macro">println!</span>(<span class="string_literal">"Hello, {}!"</span>, <span class="numeric_literal">92</span>);
@@ -80,6 +86,8 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
80 <span class="comment">// Do nothing</span> 86 <span class="comment">// Do nothing</span>
81 } 87 }
82 88
89 <span class="macro">noop!</span>(<span class="macro">noop</span><span class="macro">!</span>(<span class="numeric_literal">1</span>));
90
83 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">x</span> = <span class="numeric_literal">42</span>; 91 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">x</span> = <span class="numeric_literal">42</span>;
84 <span class="keyword">let</span> <span class="variable declaration mutable">y</span> = &<span class="keyword">mut</span> <span class="variable mutable">x</span>; 92 <span class="keyword">let</span> <span class="variable declaration mutable">y</span> = &<span class="keyword">mut</span> <span class="variable mutable">x</span>;
85 <span class="keyword">let</span> <span class="variable declaration">z</span> = &<span class="variable mutable">y</span>; 93 <span class="keyword">let</span> <span class="variable declaration">z</span> = &<span class="variable mutable">y</span>;
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index ab45c364a..bbcd52a1c 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -160,23 +160,25 @@ pub(crate) fn highlight(
160 // Check if macro takes a format string and remember it for highlighting later. 160 // Check if macro takes a format string and remember it for highlighting later.
161 // The macros that accept a format string expand to a compiler builtin macros 161 // The macros that accept a format string expand to a compiler builtin macros
162 // `format_args` and `format_args_nl`. 162 // `format_args` and `format_args_nl`.
163 if let Some(fmt_macro_call) = parent.parent().and_then(ast::MacroCall::cast) { 163 if let Some(name) = parent
164 if let Some(name) = 164 .parent()
165 fmt_macro_call.path().and_then(|p| p.segment()).and_then(|s| s.name_ref()) 165 .and_then(ast::MacroCall::cast)
166 { 166 .and_then(|mc| mc.path())
167 match name.text().as_str() { 167 .and_then(|p| p.segment())
168 "format_args" | "format_args_nl" => { 168 .and_then(|s| s.name_ref())
169 format_string = parent 169 {
170 .children_with_tokens() 170 match name.text().as_str() {
171 .filter(|t| t.kind() != WHITESPACE) 171 "format_args" | "format_args_nl" => {
172 .nth(1) 172 format_string = parent
173 .filter(|e| { 173 .children_with_tokens()
174 ast::String::can_cast(e.kind()) 174 .filter(|t| t.kind() != WHITESPACE)
175 || ast::RawString::can_cast(e.kind()) 175 .nth(1)
176 }) 176 .filter(|e| {
177 } 177 ast::String::can_cast(e.kind())
178 _ => {} 178 || ast::RawString::can_cast(e.kind())
179 })
179 } 180 }
181 _ => {}
180 } 182 }
181 } 183 }
182 184
@@ -493,6 +495,9 @@ fn highlight_element(
493 h |= HighlightModifier::Unsafe; 495 h |= HighlightModifier::Unsafe;
494 h 496 h
495 } 497 }
498 T![!] if element.parent().and_then(ast::MacroCall::cast).is_some() => {
499 Highlight::new(HighlightTag::Macro)
500 }
496 501
497 k if k.is_keyword() => { 502 k if k.is_keyword() => {
498 let h = Highlight::new(HighlightTag::Keyword); 503 let h = Highlight::new(HighlightTag::Keyword);
diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs
index 949bf59a0..070b24f45 100644
--- a/crates/ra_ide/src/syntax_highlighting/tests.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tests.rs
@@ -43,6 +43,12 @@ def_fn! {
43 } 43 }
44} 44}
45 45
46macro_rules! noop {
47 ($expr:expr) => {
48 $expr
49 }
50}
51
46// comment 52// comment
47fn main() { 53fn main() {
48 println!("Hello, {}!", 92); 54 println!("Hello, {}!", 92);
@@ -61,6 +67,8 @@ fn main() {
61 // Do nothing 67 // Do nothing
62 } 68 }
63 69
70 noop!(noop!(1));
71
64 let mut x = 42; 72 let mut x = 42;
65 let y = &mut x; 73 let y = &mut x;
66 let z = &y; 74 let z = &y;
diff --git a/crates/ra_parser/src/grammar/expressions.rs b/crates/ra_parser/src/grammar/expressions.rs
index d6e8df32a..6e72eea66 100644
--- a/crates/ra_parser/src/grammar/expressions.rs
+++ b/crates/ra_parser/src/grammar/expressions.rs
@@ -50,10 +50,8 @@ fn expr_no_struct(p: &mut Parser) {
50} 50}
51 51
52fn is_expr_stmt_attr_allowed(kind: SyntaxKind) -> bool { 52fn is_expr_stmt_attr_allowed(kind: SyntaxKind) -> bool {
53 match kind { 53 let forbid = matches!(kind, BIN_EXPR | RANGE_EXPR);
54 BIN_EXPR | RANGE_EXPR | IF_EXPR => false, 54 !forbid
55 _ => true,
56 }
57} 55}
58 56
59pub(super) fn stmt(p: &mut Parser, with_semi: StmtWithSemi) { 57pub(super) fn stmt(p: &mut Parser, with_semi: StmtWithSemi) {
diff --git a/crates/ra_syntax/src/lib.rs b/crates/ra_syntax/src/lib.rs
index 61e686da5..a33a35cc1 100644
--- a/crates/ra_syntax/src/lib.rs
+++ b/crates/ra_syntax/src/lib.rs
@@ -51,7 +51,8 @@ pub use crate::{
51 ptr::{AstPtr, SyntaxNodePtr}, 51 ptr::{AstPtr, SyntaxNodePtr},
52 syntax_error::SyntaxError, 52 syntax_error::SyntaxError,
53 syntax_node::{ 53 syntax_node::{
54 Direction, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken, SyntaxTreeBuilder, 54 Direction, NodeOrToken, SyntaxElement, SyntaxElementChildren, SyntaxNode,
55 SyntaxNodeChildren, SyntaxToken, SyntaxTreeBuilder,
55 }, 56 },
56}; 57};
57pub use ra_parser::{SyntaxKind, T}; 58pub use ra_parser::{SyntaxKind, T};
diff --git a/crates/ra_syntax/test_data/parser/inline/err/0009_attr_on_expr_not_allowed.rast b/crates/ra_syntax/test_data/parser/inline/err/0009_attr_on_expr_not_allowed.rast
index 0656fdf73..4e3fa704e 100644
--- a/crates/ra_syntax/test_data/parser/inline/err/0009_attr_on_expr_not_allowed.rast
+++ b/crates/ra_syntax/test_data/parser/inline/err/0009_attr_on_expr_not_allowed.rast
@@ -56,4 +56,3 @@ [email protected]
56 [email protected] "}" 56 [email protected] "}"
57 [email protected] "\n" 57 [email protected] "\n"
58error 24..24: attributes are not allowed on BIN_EXPR 58error 24..24: attributes are not allowed on BIN_EXPR
59error 44..44: attributes are not allowed on IF_EXPR
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs
index 2141bfc20..981565cd7 100644
--- a/crates/test_utils/src/lib.rs
+++ b/crates/test_utils/src/lib.rs
@@ -10,17 +10,17 @@
10pub mod mark; 10pub mod mark;
11 11
12use std::{ 12use std::{
13 fs, 13 env, fs,
14 path::{Path, PathBuf}, 14 path::{Path, PathBuf},
15}; 15};
16 16
17pub use ra_cfg::CfgOptions; 17use serde_json::Value;
18use stdx::split1; 18use stdx::split1;
19use text_size::{TextRange, TextSize};
19 20
21pub use ra_cfg::CfgOptions;
20pub use relative_path::{RelativePath, RelativePathBuf}; 22pub use relative_path::{RelativePath, RelativePathBuf};
21pub use rustc_hash::FxHashMap; 23pub use rustc_hash::FxHashMap;
22use serde_json::Value;
23use text_size::{TextRange, TextSize};
24 24
25pub use difference::Changeset as __Changeset; 25pub use difference::Changeset as __Changeset;
26 26
@@ -625,8 +625,6 @@ pub fn skip_slow_tests() -> bool {
625 should_skip 625 should_skip
626} 626}
627 627
628const REWRITE: bool = false;
629
630/// Asserts that `expected` and `actual` strings are equal. If they differ only 628/// Asserts that `expected` and `actual` strings are equal. If they differ only
631/// in trailing or leading whitespace the test won't fail and 629/// in trailing or leading whitespace the test won't fail and
632/// the contents of `actual` will be written to the file located at `path`. 630/// the contents of `actual` will be written to the file located at `path`.
@@ -642,7 +640,7 @@ fn assert_equal_text(expected: &str, actual: &str, path: &Path) {
642 fs::write(path, actual).unwrap(); 640 fs::write(path, actual).unwrap();
643 return; 641 return;
644 } 642 }
645 if REWRITE { 643 if env::var("UPDATE_EXPECTATIONS").is_ok() {
646 println!("rewriting {}", pretty_path.display()); 644 println!("rewriting {}", pretty_path.display());
647 fs::write(path, actual).unwrap(); 645 fs::write(path, actual).unwrap();
648 return; 646 return;