diff options
18 files changed, 734 insertions, 15 deletions
diff --git a/crates/ra_ide_api_light/src/assists.rs b/crates/ra_ide_api_light/src/assists.rs index aea8397c9..8905b0419 100644 --- a/crates/ra_ide_api_light/src/assists.rs +++ b/crates/ra_ide_api_light/src/assists.rs | |||
@@ -197,6 +197,14 @@ fn check_assist(assist: fn(AssistCtx) -> Option<Assist>, before: &str, after: &s | |||
197 | } | 197 | } |
198 | 198 | ||
199 | #[cfg(test)] | 199 | #[cfg(test)] |
200 | fn check_assist_not_applicable(assist: fn(AssistCtx) -> Option<Assist>, text: &str) { | ||
201 | crate::test_utils::check_action_not_applicable(text, |file, off| { | ||
202 | let range = TextRange::offset_len(off, 0.into()); | ||
203 | AssistCtx::new(file, range).apply(assist) | ||
204 | }) | ||
205 | } | ||
206 | |||
207 | #[cfg(test)] | ||
200 | fn check_assist_range(assist: fn(AssistCtx) -> Option<Assist>, before: &str, after: &str) { | 208 | fn check_assist_range(assist: fn(AssistCtx) -> Option<Assist>, before: &str, after: &str) { |
201 | crate::test_utils::check_action_range(before, after, |file, range| { | 209 | crate::test_utils::check_action_range(before, after, |file, range| { |
202 | AssistCtx::new(file, range).apply(assist) | 210 | AssistCtx::new(file, range).apply(assist) |
diff --git a/crates/ra_ide_api_light/src/assists/introduce_variable.rs b/crates/ra_ide_api_light/src/assists/introduce_variable.rs index 3e4434c23..ed13bddc4 100644 --- a/crates/ra_ide_api_light/src/assists/introduce_variable.rs +++ b/crates/ra_ide_api_light/src/assists/introduce_variable.rs | |||
@@ -1,16 +1,19 @@ | |||
1 | use ra_syntax::{ | 1 | use ra_syntax::{ |
2 | ast::{self, AstNode}, | 2 | ast::{self, AstNode}, |
3 | SyntaxKind::WHITESPACE, | 3 | SyntaxKind::{ |
4 | SyntaxNode, TextUnit, | 4 | WHITESPACE, MATCH_ARM, LAMBDA_EXPR, PATH_EXPR, BREAK_EXPR, LOOP_EXPR, RETURN_EXPR, COMMENT |
5 | }, SyntaxNode, TextUnit, | ||
5 | }; | 6 | }; |
6 | 7 | ||
7 | use crate::assists::{AssistCtx, Assist}; | 8 | use crate::assists::{AssistCtx, Assist}; |
8 | 9 | ||
9 | pub fn introduce_variable<'a>(ctx: AssistCtx) -> Option<Assist> { | 10 | pub fn introduce_variable<'a>(ctx: AssistCtx) -> Option<Assist> { |
10 | let node = ctx.covering_node(); | 11 | let node = ctx.covering_node(); |
11 | let expr = node.ancestors().filter_map(ast::Expr::cast).next()?; | 12 | if !valid_covering_node(node) { |
12 | 13 | return None; | |
13 | let anchor_stmt = anchor_stmt(expr)?; | 14 | } |
15 | let expr = node.ancestors().filter_map(valid_target_expr).next()?; | ||
16 | let (anchor_stmt, wrap_in_block) = anchor_stmt(expr)?; | ||
14 | let indent = anchor_stmt.prev_sibling()?; | 17 | let indent = anchor_stmt.prev_sibling()?; |
15 | if indent.kind() != WHITESPACE { | 18 | if indent.kind() != WHITESPACE { |
16 | return None; | 19 | return None; |
@@ -18,7 +21,14 @@ pub fn introduce_variable<'a>(ctx: AssistCtx) -> Option<Assist> { | |||
18 | ctx.build("introduce variable", move |edit| { | 21 | ctx.build("introduce variable", move |edit| { |
19 | let mut buf = String::new(); | 22 | let mut buf = String::new(); |
20 | 23 | ||
21 | buf.push_str("let var_name = "); | 24 | let cursor_offset = if wrap_in_block { |
25 | buf.push_str("{ let var_name = "); | ||
26 | TextUnit::of_str("{ let ") | ||
27 | } else { | ||
28 | buf.push_str("let var_name = "); | ||
29 | TextUnit::of_str("let ") | ||
30 | }; | ||
31 | |||
22 | expr.syntax().text().push_to(&mut buf); | 32 | expr.syntax().text().push_to(&mut buf); |
23 | let full_stmt = ast::ExprStmt::cast(anchor_stmt); | 33 | let full_stmt = ast::ExprStmt::cast(anchor_stmt); |
24 | let is_full_stmt = if let Some(expr_stmt) = full_stmt { | 34 | let is_full_stmt = if let Some(expr_stmt) = full_stmt { |
@@ -36,35 +46,64 @@ pub fn introduce_variable<'a>(ctx: AssistCtx) -> Option<Assist> { | |||
36 | indent.text().push_to(&mut buf); | 46 | indent.text().push_to(&mut buf); |
37 | edit.replace(expr.syntax().range(), "var_name".to_string()); | 47 | edit.replace(expr.syntax().range(), "var_name".to_string()); |
38 | edit.insert(anchor_stmt.range().start(), buf); | 48 | edit.insert(anchor_stmt.range().start(), buf); |
49 | if wrap_in_block { | ||
50 | edit.insert(anchor_stmt.range().end(), " }"); | ||
51 | } | ||
39 | } | 52 | } |
40 | edit.set_cursor(anchor_stmt.range().start() + TextUnit::of_str("let ")); | 53 | edit.set_cursor(anchor_stmt.range().start() + cursor_offset); |
41 | }) | 54 | }) |
42 | } | 55 | } |
43 | 56 | ||
44 | /// Statement or last in the block expression, which will follow | 57 | fn valid_covering_node(node: &SyntaxNode) -> bool { |
45 | /// the freshly introduced var. | 58 | node.kind() != COMMENT |
46 | fn anchor_stmt(expr: &ast::Expr) -> Option<&SyntaxNode> { | 59 | } |
47 | expr.syntax().ancestors().find(|&node| { | 60 | /// Check wether the node is a valid expression which can be extracted to a variable. |
61 | /// In general that's true for any expression, but in some cases that would produce invalid code. | ||
62 | fn valid_target_expr(node: &SyntaxNode) -> Option<&ast::Expr> { | ||
63 | return match node.kind() { | ||
64 | PATH_EXPR => None, | ||
65 | BREAK_EXPR => ast::BreakExpr::cast(node).and_then(|e| e.expr()), | ||
66 | RETURN_EXPR => ast::ReturnExpr::cast(node).and_then(|e| e.expr()), | ||
67 | LOOP_EXPR => ast::ReturnExpr::cast(node).and_then(|e| e.expr()), | ||
68 | _ => ast::Expr::cast(node), | ||
69 | }; | ||
70 | } | ||
71 | |||
72 | /// Returns the syntax node which will follow the freshly introduced var | ||
73 | /// and a boolean indicating whether we have to wrap it within a { } block | ||
74 | /// to produce correct code. | ||
75 | /// It can be a statement, the last in a block expression or a wanna be block | ||
76 | /// expression like a lamba or match arm. | ||
77 | fn anchor_stmt(expr: &ast::Expr) -> Option<(&SyntaxNode, bool)> { | ||
78 | expr.syntax().ancestors().find_map(|node| { | ||
48 | if ast::Stmt::cast(node).is_some() { | 79 | if ast::Stmt::cast(node).is_some() { |
49 | return true; | 80 | return Some((node, false)); |
50 | } | 81 | } |
82 | |||
51 | if let Some(expr) = node | 83 | if let Some(expr) = node |
52 | .parent() | 84 | .parent() |
53 | .and_then(ast::Block::cast) | 85 | .and_then(ast::Block::cast) |
54 | .and_then(|it| it.expr()) | 86 | .and_then(|it| it.expr()) |
55 | { | 87 | { |
56 | if expr.syntax() == node { | 88 | if expr.syntax() == node { |
57 | return true; | 89 | return Some((node, false)); |
90 | } | ||
91 | } | ||
92 | |||
93 | if let Some(parent) = node.parent() { | ||
94 | if parent.kind() == MATCH_ARM || parent.kind() == LAMBDA_EXPR { | ||
95 | return Some((node, true)); | ||
58 | } | 96 | } |
59 | } | 97 | } |
60 | false | 98 | |
99 | None | ||
61 | }) | 100 | }) |
62 | } | 101 | } |
63 | 102 | ||
64 | #[cfg(test)] | 103 | #[cfg(test)] |
65 | mod tests { | 104 | mod tests { |
66 | use super::*; | 105 | use super::*; |
67 | use crate::assists::check_assist_range; | 106 | use crate::assists::{ check_assist, check_assist_not_applicable, check_assist_range }; |
68 | 107 | ||
69 | #[test] | 108 | #[test] |
70 | fn test_introduce_var_simple() { | 109 | fn test_introduce_var_simple() { |
@@ -161,4 +200,232 @@ fn foo() { | |||
161 | }", | 200 | }", |
162 | ); | 201 | ); |
163 | } | 202 | } |
203 | |||
204 | #[test] | ||
205 | fn test_introduce_var_in_match_arm_no_block() { | ||
206 | check_assist_range( | ||
207 | introduce_variable, | ||
208 | " | ||
209 | fn main() { | ||
210 | let x = true; | ||
211 | let tuple = match x { | ||
212 | true => (<|>2 + 2<|>, true) | ||
213 | _ => (0, false) | ||
214 | }; | ||
215 | } | ||
216 | ", | ||
217 | " | ||
218 | fn main() { | ||
219 | let x = true; | ||
220 | let tuple = match x { | ||
221 | true => { let <|>var_name = 2 + 2; (var_name, true) } | ||
222 | _ => (0, false) | ||
223 | }; | ||
224 | } | ||
225 | ", | ||
226 | ); | ||
227 | } | ||
228 | |||
229 | #[test] | ||
230 | fn test_introduce_var_in_match_arm_with_block() { | ||
231 | check_assist_range( | ||
232 | introduce_variable, | ||
233 | " | ||
234 | fn main() { | ||
235 | let x = true; | ||
236 | let tuple = match x { | ||
237 | true => { | ||
238 | let y = 1; | ||
239 | (<|>2 + y<|>, true) | ||
240 | } | ||
241 | _ => (0, false) | ||
242 | }; | ||
243 | } | ||
244 | ", | ||
245 | " | ||
246 | fn main() { | ||
247 | let x = true; | ||
248 | let tuple = match x { | ||
249 | true => { | ||
250 | let y = 1; | ||
251 | let <|>var_name = 2 + y; | ||
252 | (var_name, true) | ||
253 | } | ||
254 | _ => (0, false) | ||
255 | }; | ||
256 | } | ||
257 | ", | ||
258 | ); | ||
259 | } | ||
260 | |||
261 | #[test] | ||
262 | fn test_introduce_var_in_closure_no_block() { | ||
263 | check_assist_range( | ||
264 | introduce_variable, | ||
265 | " | ||
266 | fn main() { | ||
267 | let lambda = |x: u32| <|>x * 2<|>; | ||
268 | } | ||
269 | ", | ||
270 | " | ||
271 | fn main() { | ||
272 | let lambda = |x: u32| { let <|>var_name = x * 2; var_name }; | ||
273 | } | ||
274 | ", | ||
275 | ); | ||
276 | } | ||
277 | |||
278 | #[test] | ||
279 | fn test_introduce_var_in_closure_with_block() { | ||
280 | check_assist_range( | ||
281 | introduce_variable, | ||
282 | " | ||
283 | fn main() { | ||
284 | let lambda = |x: u32| { <|>x * 2<|> }; | ||
285 | } | ||
286 | ", | ||
287 | " | ||
288 | fn main() { | ||
289 | let lambda = |x: u32| { let <|>var_name = x * 2; var_name }; | ||
290 | } | ||
291 | ", | ||
292 | ); | ||
293 | } | ||
294 | |||
295 | #[test] | ||
296 | fn test_introduce_var_path_simple() { | ||
297 | check_assist( | ||
298 | introduce_variable, | ||
299 | " | ||
300 | fn main() { | ||
301 | let o = S<|>ome(true); | ||
302 | } | ||
303 | ", | ||
304 | " | ||
305 | fn main() { | ||
306 | let <|>var_name = Some(true); | ||
307 | let o = var_name; | ||
308 | } | ||
309 | ", | ||
310 | ); | ||
311 | } | ||
312 | |||
313 | #[test] | ||
314 | fn test_introduce_var_path_method() { | ||
315 | check_assist( | ||
316 | introduce_variable, | ||
317 | " | ||
318 | fn main() { | ||
319 | let v = b<|>ar.foo(); | ||
320 | } | ||
321 | ", | ||
322 | " | ||
323 | fn main() { | ||
324 | let <|>var_name = bar.foo(); | ||
325 | let v = var_name; | ||
326 | } | ||
327 | ", | ||
328 | ); | ||
329 | } | ||
330 | |||
331 | #[test] | ||
332 | fn test_introduce_var_return() { | ||
333 | check_assist( | ||
334 | introduce_variable, | ||
335 | " | ||
336 | fn foo() -> u32 { | ||
337 | r<|>eturn 2 + 2; | ||
338 | } | ||
339 | ", | ||
340 | " | ||
341 | fn foo() -> u32 { | ||
342 | let <|>var_name = 2 + 2; | ||
343 | return var_name; | ||
344 | } | ||
345 | ", | ||
346 | ); | ||
347 | } | ||
348 | |||
349 | #[test] | ||
350 | fn test_introduce_var_break() { | ||
351 | check_assist( | ||
352 | introduce_variable, | ||
353 | " | ||
354 | fn main() { | ||
355 | let result = loop { | ||
356 | b<|>reak 2 + 2; | ||
357 | }; | ||
358 | } | ||
359 | ", | ||
360 | " | ||
361 | fn main() { | ||
362 | let result = loop { | ||
363 | let <|>var_name = 2 + 2; | ||
364 | break var_name; | ||
365 | }; | ||
366 | } | ||
367 | ", | ||
368 | ); | ||
369 | } | ||
370 | |||
371 | #[test] | ||
372 | fn test_introduce_var_for_cast() { | ||
373 | check_assist( | ||
374 | introduce_variable, | ||
375 | " | ||
376 | fn main() { | ||
377 | let v = 0f32 a<|>s u32; | ||
378 | } | ||
379 | ", | ||
380 | " | ||
381 | fn main() { | ||
382 | let <|>var_name = 0f32 as u32; | ||
383 | let v = var_name; | ||
384 | } | ||
385 | ", | ||
386 | ); | ||
387 | } | ||
388 | |||
389 | #[test] | ||
390 | fn test_introduce_var_for_return_not_applicable() { | ||
391 | check_assist_not_applicable( | ||
392 | introduce_variable, | ||
393 | " | ||
394 | fn foo() { | ||
395 | r<|>eturn; | ||
396 | } | ||
397 | ", | ||
398 | ); | ||
399 | } | ||
400 | |||
401 | #[test] | ||
402 | fn test_introduce_var_for_break_not_applicable() { | ||
403 | check_assist_not_applicable( | ||
404 | introduce_variable, | ||
405 | " | ||
406 | fn main() { | ||
407 | loop { | ||
408 | b<|>reak; | ||
409 | }; | ||
410 | } | ||
411 | ", | ||
412 | ); | ||
413 | } | ||
414 | |||
415 | #[test] | ||
416 | fn test_introduce_var_in_comment_not_applicable() { | ||
417 | check_assist_not_applicable( | ||
418 | introduce_variable, | ||
419 | " | ||
420 | fn main() { | ||
421 | let x = true; | ||
422 | let tuple = match x { | ||
423 | // c<|>omment | ||
424 | true => (2 + 2, true) | ||
425 | _ => (0, false) | ||
426 | }; | ||
427 | } | ||
428 | ", | ||
429 | ); | ||
430 | } | ||
164 | } | 431 | } |
diff --git a/crates/ra_ide_api_light/src/test_utils.rs b/crates/ra_ide_api_light/src/test_utils.rs index dc2470aa3..22ded2435 100644 --- a/crates/ra_ide_api_light/src/test_utils.rs +++ b/crates/ra_ide_api_light/src/test_utils.rs | |||
@@ -23,6 +23,18 @@ pub fn check_action<F: Fn(&SourceFile, TextUnit) -> Option<LocalEdit>>( | |||
23 | assert_eq_text!(after, &actual); | 23 | assert_eq_text!(after, &actual); |
24 | } | 24 | } |
25 | 25 | ||
26 | pub fn check_action_not_applicable<F: Fn(&SourceFile, TextUnit) -> Option<LocalEdit>>( | ||
27 | text: &str, | ||
28 | f: F, | ||
29 | ) { | ||
30 | let (text_cursor_pos, text) = extract_offset(text); | ||
31 | let file = SourceFile::parse(&text); | ||
32 | assert!( | ||
33 | f(&file, text_cursor_pos).is_none(), | ||
34 | "code action is applicable but it shouldn't" | ||
35 | ); | ||
36 | } | ||
37 | |||
26 | pub fn check_action_range<F: Fn(&SourceFile, TextRange) -> Option<LocalEdit>>( | 38 | pub fn check_action_range<F: Fn(&SourceFile, TextRange) -> Option<LocalEdit>>( |
27 | before: &str, | 39 | before: &str, |
28 | after: &str, | 40 | after: &str, |
diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index 4f5a96014..d0561c495 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs | |||
@@ -272,6 +272,7 @@ impl ToOwned for Block { | |||
272 | } | 272 | } |
273 | 273 | ||
274 | 274 | ||
275 | impl ast::AttrsOwner for Block {} | ||
275 | impl Block { | 276 | impl Block { |
276 | pub fn statements(&self) -> impl Iterator<Item = &Stmt> { | 277 | pub fn statements(&self) -> impl Iterator<Item = &Stmt> { |
277 | super::children(self) | 278 | super::children(self) |
diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index e4cad4eb3..d4c863705 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron | |||
@@ -571,6 +571,9 @@ Grammar( | |||
571 | options: [ "Expr" ], | 571 | options: [ "Expr" ], |
572 | collections: [ | 572 | collections: [ |
573 | ["statements", "Stmt"], | 573 | ["statements", "Stmt"], |
574 | ], | ||
575 | traits: [ | ||
576 | "AttrsOwner", | ||
574 | ] | 577 | ] |
575 | ), | 578 | ), |
576 | "ParamList": ( | 579 | "ParamList": ( |
diff --git a/crates/ra_syntax/src/grammar/expressions.rs b/crates/ra_syntax/src/grammar/expressions.rs index d27eb8b7e..6b88c5685 100644 --- a/crates/ra_syntax/src/grammar/expressions.rs +++ b/crates/ra_syntax/src/grammar/expressions.rs | |||
@@ -42,6 +42,8 @@ pub(crate) fn block(p: &mut Parser) { | |||
42 | } | 42 | } |
43 | let m = p.start(); | 43 | let m = p.start(); |
44 | p.bump(); | 44 | p.bump(); |
45 | // This is checked by a validator | ||
46 | attributes::inner_attributes(p); | ||
45 | 47 | ||
46 | while !p.at(EOF) && !p.at(R_CURLY) { | 48 | while !p.at(EOF) && !p.at(R_CURLY) { |
47 | match p.current() { | 49 | match p.current() { |
diff --git a/crates/ra_syntax/src/grammar/items/traits.rs b/crates/ra_syntax/src/grammar/items/traits.rs index 0a0621753..d5a8ccd98 100644 --- a/crates/ra_syntax/src/grammar/items/traits.rs +++ b/crates/ra_syntax/src/grammar/items/traits.rs | |||
@@ -78,6 +78,13 @@ pub(crate) fn impl_item_list(p: &mut Parser) { | |||
78 | assert!(p.at(L_CURLY)); | 78 | assert!(p.at(L_CURLY)); |
79 | let m = p.start(); | 79 | let m = p.start(); |
80 | p.bump(); | 80 | p.bump(); |
81 | // test impl_inner_attributes | ||
82 | // enum F{} | ||
83 | // impl F { | ||
84 | // //! This is a doc comment | ||
85 | // #![doc("This is also a doc comment")] | ||
86 | // } | ||
87 | attributes::inner_attributes(p); | ||
81 | 88 | ||
82 | while !p.at(EOF) && !p.at(R_CURLY) { | 89 | while !p.at(EOF) && !p.at(R_CURLY) { |
83 | if p.at(L_CURLY) { | 90 | if p.at(L_CURLY) { |
diff --git a/crates/ra_syntax/src/validation.rs b/crates/ra_syntax/src/validation.rs index 73e1d20b9..ac6cc3dd6 100644 --- a/crates/ra_syntax/src/validation.rs +++ b/crates/ra_syntax/src/validation.rs | |||
@@ -2,6 +2,7 @@ mod byte; | |||
2 | mod byte_string; | 2 | mod byte_string; |
3 | mod char; | 3 | mod char; |
4 | mod string; | 4 | mod string; |
5 | mod block; | ||
5 | 6 | ||
6 | use crate::{ | 7 | use crate::{ |
7 | SourceFile, yellow::SyntaxError, AstNode, | 8 | SourceFile, yellow::SyntaxError, AstNode, |
@@ -17,6 +18,7 @@ pub(crate) fn validate(file: &SourceFile) -> Vec<SyntaxError> { | |||
17 | .visit::<ast::ByteString, _>(self::byte_string::validate_byte_string_node) | 18 | .visit::<ast::ByteString, _>(self::byte_string::validate_byte_string_node) |
18 | .visit::<ast::Char, _>(self::char::validate_char_node) | 19 | .visit::<ast::Char, _>(self::char::validate_char_node) |
19 | .visit::<ast::String, _>(self::string::validate_string_node) | 20 | .visit::<ast::String, _>(self::string::validate_string_node) |
21 | .visit::<ast::Block, _>(self::block::validate_block_node) | ||
20 | .accept(node); | 22 | .accept(node); |
21 | } | 23 | } |
22 | errors | 24 | errors |
diff --git a/crates/ra_syntax/src/validation/block.rs b/crates/ra_syntax/src/validation/block.rs new file mode 100644 index 000000000..9e1949124 --- /dev/null +++ b/crates/ra_syntax/src/validation/block.rs | |||
@@ -0,0 +1,24 @@ | |||
1 | use crate::{SyntaxKind::*, | ||
2 | ast::{self, AttrsOwner, AstNode}, | ||
3 | yellow::{ | ||
4 | SyntaxError, | ||
5 | SyntaxErrorKind::*, | ||
6 | }, | ||
7 | }; | ||
8 | |||
9 | pub(crate) fn validate_block_node(node: &ast::Block, errors: &mut Vec<SyntaxError>) { | ||
10 | if let Some(parent) = node.syntax().parent() { | ||
11 | match parent.kind() { | ||
12 | FN_DEF => return, | ||
13 | BLOCK_EXPR => match parent.parent().map(|v| v.kind()) { | ||
14 | Some(EXPR_STMT) | Some(BLOCK) => return, | ||
15 | _ => {} | ||
16 | }, | ||
17 | _ => {} | ||
18 | } | ||
19 | } | ||
20 | errors.extend( | ||
21 | node.attrs() | ||
22 | .map(|attr| SyntaxError::new(InvalidBlockAttr, attr.syntax().range())), | ||
23 | ) | ||
24 | } | ||
diff --git a/crates/ra_syntax/src/yellow/syntax_error.rs b/crates/ra_syntax/src/yellow/syntax_error.rs index 534f3511e..c52c44cc3 100644 --- a/crates/ra_syntax/src/yellow/syntax_error.rs +++ b/crates/ra_syntax/src/yellow/syntax_error.rs | |||
@@ -94,6 +94,7 @@ pub enum SyntaxErrorKind { | |||
94 | UnicodeEscapeOutOfRange, | 94 | UnicodeEscapeOutOfRange, |
95 | UnclosedString, | 95 | UnclosedString, |
96 | InvalidSuffix, | 96 | InvalidSuffix, |
97 | InvalidBlockAttr, | ||
97 | } | 98 | } |
98 | 99 | ||
99 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 100 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
@@ -136,6 +137,9 @@ impl fmt::Display for SyntaxErrorKind { | |||
136 | UnicodeEscapeOutOfRange => write!(f, "Unicode escape code should be at most 0x10FFFF"), | 137 | UnicodeEscapeOutOfRange => write!(f, "Unicode escape code should be at most 0x10FFFF"), |
137 | UnclosedString => write!(f, "Unclosed string literal"), | 138 | UnclosedString => write!(f, "Unclosed string literal"), |
138 | InvalidSuffix => write!(f, "Invalid literal suffix"), | 139 | InvalidSuffix => write!(f, "Invalid literal suffix"), |
140 | InvalidBlockAttr => { | ||
141 | write!(f, "A block in this position cannot accept inner attributes") | ||
142 | } | ||
139 | ParseError(msg) => write!(f, "{}", msg.0), | 143 | ParseError(msg) => write!(f, "{}", msg.0), |
140 | } | 144 | } |
141 | } | 145 | } |
diff --git a/crates/ra_syntax/tests/data/parser/err/0031_block_inner_attrs.rs b/crates/ra_syntax/tests/data/parser/err/0031_block_inner_attrs.rs new file mode 100644 index 000000000..6a04f2d0a --- /dev/null +++ b/crates/ra_syntax/tests/data/parser/err/0031_block_inner_attrs.rs | |||
@@ -0,0 +1,15 @@ | |||
1 | fn block() { | ||
2 | let inner = { | ||
3 | #![doc("Inner attributes not allowed here")] | ||
4 | //! Nor are ModuleDoc comments | ||
5 | }; | ||
6 | if true { | ||
7 | #![doc("Nor here")] | ||
8 | #![doc("We error on each attr")] | ||
9 | //! Nor are ModuleDoc comments | ||
10 | } | ||
11 | while true { | ||
12 | #![doc("Nor here")] | ||
13 | //! Nor are ModuleDoc comments | ||
14 | } | ||
15 | } | ||
diff --git a/crates/ra_syntax/tests/data/parser/err/0031_block_inner_attrs.txt b/crates/ra_syntax/tests/data/parser/err/0031_block_inner_attrs.txt new file mode 100644 index 000000000..086aa79ac --- /dev/null +++ b/crates/ra_syntax/tests/data/parser/err/0031_block_inner_attrs.txt | |||
@@ -0,0 +1,114 @@ | |||
1 | SOURCE_FILE@[0; 350) | ||
2 | FN_DEF@[0; 349) | ||
3 | FN_KW@[0; 2) | ||
4 | WHITESPACE@[2; 3) | ||
5 | NAME@[3; 8) | ||
6 | IDENT@[3; 8) "block" | ||
7 | PARAM_LIST@[8; 10) | ||
8 | L_PAREN@[8; 9) | ||
9 | R_PAREN@[9; 10) | ||
10 | WHITESPACE@[10; 11) | ||
11 | BLOCK@[11; 349) | ||
12 | L_CURLY@[11; 12) | ||
13 | WHITESPACE@[12; 17) | ||
14 | LET_STMT@[17; 129) | ||
15 | LET_KW@[17; 20) | ||
16 | WHITESPACE@[20; 21) | ||
17 | BIND_PAT@[21; 26) | ||
18 | NAME@[21; 26) | ||
19 | IDENT@[21; 26) "inner" | ||
20 | WHITESPACE@[26; 27) | ||
21 | EQ@[27; 28) | ||
22 | WHITESPACE@[28; 29) | ||
23 | BLOCK_EXPR@[29; 128) | ||
24 | BLOCK@[29; 128) | ||
25 | L_CURLY@[29; 30) | ||
26 | WHITESPACE@[30; 39) | ||
27 | err: `A block in this position cannot accept inner attributes` | ||
28 | ATTR@[39; 83) | ||
29 | POUND@[39; 40) | ||
30 | EXCL@[40; 41) | ||
31 | TOKEN_TREE@[41; 83) | ||
32 | L_BRACK@[41; 42) | ||
33 | IDENT@[42; 45) "doc" | ||
34 | TOKEN_TREE@[45; 82) | ||
35 | L_PAREN@[45; 46) | ||
36 | STRING@[46; 81) | ||
37 | R_PAREN@[81; 82) | ||
38 | R_BRACK@[82; 83) | ||
39 | WHITESPACE@[83; 92) | ||
40 | COMMENT@[92; 122) | ||
41 | WHITESPACE@[122; 127) | ||
42 | R_CURLY@[127; 128) | ||
43 | SEMI@[128; 129) | ||
44 | WHITESPACE@[129; 134) | ||
45 | EXPR_STMT@[134; 257) | ||
46 | IF_EXPR@[134; 257) | ||
47 | IF_KW@[134; 136) | ||
48 | WHITESPACE@[136; 137) | ||
49 | CONDITION@[137; 141) | ||
50 | LITERAL@[137; 141) | ||
51 | TRUE_KW@[137; 141) | ||
52 | WHITESPACE@[141; 142) | ||
53 | BLOCK@[142; 257) | ||
54 | L_CURLY@[142; 143) | ||
55 | WHITESPACE@[143; 152) | ||
56 | err: `A block in this position cannot accept inner attributes` | ||
57 | ATTR@[152; 171) | ||
58 | POUND@[152; 153) | ||
59 | EXCL@[153; 154) | ||
60 | TOKEN_TREE@[154; 171) | ||
61 | L_BRACK@[154; 155) | ||
62 | IDENT@[155; 158) "doc" | ||
63 | TOKEN_TREE@[158; 170) | ||
64 | L_PAREN@[158; 159) | ||
65 | STRING@[159; 169) | ||
66 | R_PAREN@[169; 170) | ||
67 | R_BRACK@[170; 171) | ||
68 | WHITESPACE@[171; 180) | ||
69 | err: `A block in this position cannot accept inner attributes` | ||
70 | ATTR@[180; 212) | ||
71 | POUND@[180; 181) | ||
72 | EXCL@[181; 182) | ||
73 | TOKEN_TREE@[182; 212) | ||
74 | L_BRACK@[182; 183) | ||
75 | IDENT@[183; 186) "doc" | ||
76 | TOKEN_TREE@[186; 211) | ||
77 | L_PAREN@[186; 187) | ||
78 | STRING@[187; 210) | ||
79 | R_PAREN@[210; 211) | ||
80 | R_BRACK@[211; 212) | ||
81 | WHITESPACE@[212; 221) | ||
82 | COMMENT@[221; 251) | ||
83 | WHITESPACE@[251; 256) | ||
84 | R_CURLY@[256; 257) | ||
85 | WHITESPACE@[257; 262) | ||
86 | WHILE_EXPR@[262; 347) | ||
87 | WHILE_KW@[262; 267) | ||
88 | WHITESPACE@[267; 268) | ||
89 | CONDITION@[268; 272) | ||
90 | LITERAL@[268; 272) | ||
91 | TRUE_KW@[268; 272) | ||
92 | WHITESPACE@[272; 273) | ||
93 | BLOCK@[273; 347) | ||
94 | L_CURLY@[273; 274) | ||
95 | WHITESPACE@[274; 283) | ||
96 | err: `A block in this position cannot accept inner attributes` | ||
97 | ATTR@[283; 302) | ||
98 | POUND@[283; 284) | ||
99 | EXCL@[284; 285) | ||
100 | TOKEN_TREE@[285; 302) | ||
101 | L_BRACK@[285; 286) | ||
102 | IDENT@[286; 289) "doc" | ||
103 | TOKEN_TREE@[289; 301) | ||
104 | L_PAREN@[289; 290) | ||
105 | STRING@[290; 300) | ||
106 | R_PAREN@[300; 301) | ||
107 | R_BRACK@[301; 302) | ||
108 | WHITESPACE@[302; 311) | ||
109 | COMMENT@[311; 341) | ||
110 | WHITESPACE@[341; 346) | ||
111 | R_CURLY@[346; 347) | ||
112 | WHITESPACE@[347; 348) | ||
113 | R_CURLY@[348; 349) | ||
114 | WHITESPACE@[349; 350) | ||
diff --git a/crates/ra_syntax/tests/data/parser/inline/ok/0118_impl_inner_attributes.rs b/crates/ra_syntax/tests/data/parser/inline/ok/0118_impl_inner_attributes.rs new file mode 100644 index 000000000..4d68cceb7 --- /dev/null +++ b/crates/ra_syntax/tests/data/parser/inline/ok/0118_impl_inner_attributes.rs | |||
@@ -0,0 +1,5 @@ | |||
1 | enum F{} | ||
2 | impl F { | ||
3 | //! This is a doc comment | ||
4 | #![doc("This is also a doc comment")] | ||
5 | } | ||
diff --git a/crates/ra_syntax/tests/data/parser/inline/ok/0118_impl_inner_attributes.txt b/crates/ra_syntax/tests/data/parser/inline/ok/0118_impl_inner_attributes.txt new file mode 100644 index 000000000..3b761b7bb --- /dev/null +++ b/crates/ra_syntax/tests/data/parser/inline/ok/0118_impl_inner_attributes.txt | |||
@@ -0,0 +1,38 @@ | |||
1 | SOURCE_FILE@[0; 94) | ||
2 | ENUM_DEF@[0; 8) | ||
3 | ENUM_KW@[0; 4) | ||
4 | WHITESPACE@[4; 5) | ||
5 | NAME@[5; 6) | ||
6 | IDENT@[5; 6) "F" | ||
7 | ENUM_VARIANT_LIST@[6; 8) | ||
8 | L_CURLY@[6; 7) | ||
9 | R_CURLY@[7; 8) | ||
10 | WHITESPACE@[8; 9) | ||
11 | IMPL_BLOCK@[9; 93) | ||
12 | IMPL_KW@[9; 13) | ||
13 | WHITESPACE@[13; 14) | ||
14 | PATH_TYPE@[14; 15) | ||
15 | PATH@[14; 15) | ||
16 | PATH_SEGMENT@[14; 15) | ||
17 | NAME_REF@[14; 15) | ||
18 | IDENT@[14; 15) "F" | ||
19 | WHITESPACE@[15; 16) | ||
20 | ITEM_LIST@[16; 93) | ||
21 | L_CURLY@[16; 17) | ||
22 | WHITESPACE@[17; 23) | ||
23 | COMMENT@[23; 48) | ||
24 | WHITESPACE@[48; 54) | ||
25 | ATTR@[54; 91) | ||
26 | POUND@[54; 55) | ||
27 | EXCL@[55; 56) | ||
28 | TOKEN_TREE@[56; 91) | ||
29 | L_BRACK@[56; 57) | ||
30 | IDENT@[57; 60) "doc" | ||
31 | TOKEN_TREE@[60; 90) | ||
32 | L_PAREN@[60; 61) | ||
33 | STRING@[61; 89) | ||
34 | R_PAREN@[89; 90) | ||
35 | R_BRACK@[90; 91) | ||
36 | WHITESPACE@[91; 92) | ||
37 | R_CURLY@[92; 93) | ||
38 | WHITESPACE@[93; 94) | ||
diff --git a/crates/ra_syntax/tests/data/parser/ok/0045_block_inner_attrs.rs b/crates/ra_syntax/tests/data/parser/ok/0045_block_inner_attrs.rs new file mode 100644 index 000000000..88df8138e --- /dev/null +++ b/crates/ra_syntax/tests/data/parser/ok/0045_block_inner_attrs.rs | |||
@@ -0,0 +1,20 @@ | |||
1 | fn block() { | ||
2 | #![doc("Inner attributes allowed here")] | ||
3 | //! As are ModuleDoc style comments | ||
4 | { | ||
5 | #![doc("Inner attributes are allowed in blocks used as statements")] | ||
6 | #![doc("Being validated is not affected by duplcates")] | ||
7 | //! As are ModuleDoc style comments | ||
8 | }; | ||
9 | { | ||
10 | #![doc("Inner attributes are allowed in blocks when they are the last statement of another block")] | ||
11 | //! As are ModuleDoc style comments | ||
12 | } | ||
13 | } | ||
14 | |||
15 | // https://github.com/rust-analyzer/rust-analyzer/issues/689 | ||
16 | impl Whatever { | ||
17 | fn salsa_event(&self, event_fn: impl Fn() -> Event<Self>) { | ||
18 | #![allow(unused_variables)] // this is `inner_attr` of the block | ||
19 | } | ||
20 | } | ||
diff --git a/crates/ra_syntax/tests/data/parser/ok/0045_block_inner_attrs.txt b/crates/ra_syntax/tests/data/parser/ok/0045_block_inner_attrs.txt new file mode 100644 index 000000000..a1ba645ef --- /dev/null +++ b/crates/ra_syntax/tests/data/parser/ok/0045_block_inner_attrs.txt | |||
@@ -0,0 +1,167 @@ | |||
1 | SOURCE_FILE@[0; 686) | ||
2 | FN_DEF@[0; 461) | ||
3 | FN_KW@[0; 2) | ||
4 | WHITESPACE@[2; 3) | ||
5 | NAME@[3; 8) | ||
6 | IDENT@[3; 8) "block" | ||
7 | PARAM_LIST@[8; 10) | ||
8 | L_PAREN@[8; 9) | ||
9 | R_PAREN@[9; 10) | ||
10 | WHITESPACE@[10; 11) | ||
11 | BLOCK@[11; 461) | ||
12 | L_CURLY@[11; 12) | ||
13 | WHITESPACE@[12; 17) | ||
14 | ATTR@[17; 57) | ||
15 | POUND@[17; 18) | ||
16 | EXCL@[18; 19) | ||
17 | TOKEN_TREE@[19; 57) | ||
18 | L_BRACK@[19; 20) | ||
19 | IDENT@[20; 23) "doc" | ||
20 | TOKEN_TREE@[23; 56) | ||
21 | L_PAREN@[23; 24) | ||
22 | STRING@[24; 55) | ||
23 | R_PAREN@[55; 56) | ||
24 | R_BRACK@[56; 57) | ||
25 | WHITESPACE@[57; 62) | ||
26 | COMMENT@[62; 97) | ||
27 | WHITESPACE@[97; 102) | ||
28 | EXPR_STMT@[102; 295) | ||
29 | BLOCK_EXPR@[102; 294) | ||
30 | BLOCK@[102; 294) | ||
31 | L_CURLY@[102; 103) | ||
32 | WHITESPACE@[103; 112) | ||
33 | ATTR@[112; 180) | ||
34 | POUND@[112; 113) | ||
35 | EXCL@[113; 114) | ||
36 | TOKEN_TREE@[114; 180) | ||
37 | L_BRACK@[114; 115) | ||
38 | IDENT@[115; 118) "doc" | ||
39 | TOKEN_TREE@[118; 179) | ||
40 | L_PAREN@[118; 119) | ||
41 | STRING@[119; 178) | ||
42 | R_PAREN@[178; 179) | ||
43 | R_BRACK@[179; 180) | ||
44 | WHITESPACE@[180; 189) | ||
45 | ATTR@[189; 244) | ||
46 | POUND@[189; 190) | ||
47 | EXCL@[190; 191) | ||
48 | TOKEN_TREE@[191; 244) | ||
49 | L_BRACK@[191; 192) | ||
50 | IDENT@[192; 195) "doc" | ||
51 | TOKEN_TREE@[195; 243) | ||
52 | L_PAREN@[195; 196) | ||
53 | STRING@[196; 242) | ||
54 | R_PAREN@[242; 243) | ||
55 | R_BRACK@[243; 244) | ||
56 | WHITESPACE@[244; 253) | ||
57 | COMMENT@[253; 288) | ||
58 | WHITESPACE@[288; 293) | ||
59 | R_CURLY@[293; 294) | ||
60 | SEMI@[294; 295) | ||
61 | WHITESPACE@[295; 300) | ||
62 | BLOCK_EXPR@[300; 459) | ||
63 | BLOCK@[300; 459) | ||
64 | L_CURLY@[300; 301) | ||
65 | WHITESPACE@[301; 310) | ||
66 | ATTR@[310; 409) | ||
67 | POUND@[310; 311) | ||
68 | EXCL@[311; 312) | ||
69 | TOKEN_TREE@[312; 409) | ||
70 | L_BRACK@[312; 313) | ||
71 | IDENT@[313; 316) "doc" | ||
72 | TOKEN_TREE@[316; 408) | ||
73 | L_PAREN@[316; 317) | ||
74 | STRING@[317; 407) | ||
75 | R_PAREN@[407; 408) | ||
76 | R_BRACK@[408; 409) | ||
77 | WHITESPACE@[409; 418) | ||
78 | COMMENT@[418; 453) | ||
79 | WHITESPACE@[453; 458) | ||
80 | R_CURLY@[458; 459) | ||
81 | WHITESPACE@[459; 460) | ||
82 | R_CURLY@[460; 461) | ||
83 | WHITESPACE@[461; 463) | ||
84 | COMMENT@[463; 523) | ||
85 | WHITESPACE@[523; 524) | ||
86 | IMPL_BLOCK@[524; 685) | ||
87 | IMPL_KW@[524; 528) | ||
88 | WHITESPACE@[528; 529) | ||
89 | PATH_TYPE@[529; 537) | ||
90 | PATH@[529; 537) | ||
91 | PATH_SEGMENT@[529; 537) | ||
92 | NAME_REF@[529; 537) | ||
93 | IDENT@[529; 537) "Whatever" | ||
94 | WHITESPACE@[537; 538) | ||
95 | ITEM_LIST@[538; 685) | ||
96 | L_CURLY@[538; 539) | ||
97 | WHITESPACE@[539; 544) | ||
98 | FN_DEF@[544; 683) | ||
99 | FN_KW@[544; 546) | ||
100 | WHITESPACE@[546; 547) | ||
101 | NAME@[547; 558) | ||
102 | IDENT@[547; 558) "salsa_event" | ||
103 | PARAM_LIST@[558; 601) | ||
104 | L_PAREN@[558; 559) | ||
105 | SELF_PARAM@[559; 564) | ||
106 | AMP@[559; 560) | ||
107 | SELF_KW@[560; 564) | ||
108 | COMMA@[564; 565) | ||
109 | WHITESPACE@[565; 566) | ||
110 | PARAM@[566; 600) | ||
111 | BIND_PAT@[566; 574) | ||
112 | NAME@[566; 574) | ||
113 | IDENT@[566; 574) "event_fn" | ||
114 | COLON@[574; 575) | ||
115 | WHITESPACE@[575; 576) | ||
116 | IMPL_TRAIT_TYPE@[576; 600) | ||
117 | IMPL_KW@[576; 580) | ||
118 | WHITESPACE@[580; 581) | ||
119 | PATH_TYPE@[581; 600) | ||
120 | PATH@[581; 600) | ||
121 | PATH_SEGMENT@[581; 600) | ||
122 | NAME_REF@[581; 583) | ||
123 | IDENT@[581; 583) "Fn" | ||
124 | PARAM_LIST@[583; 585) | ||
125 | L_PAREN@[583; 584) | ||
126 | R_PAREN@[584; 585) | ||
127 | WHITESPACE@[585; 586) | ||
128 | RET_TYPE@[586; 600) | ||
129 | THIN_ARROW@[586; 588) | ||
130 | WHITESPACE@[588; 589) | ||
131 | PATH_TYPE@[589; 600) | ||
132 | PATH@[589; 600) | ||
133 | PATH_SEGMENT@[589; 600) | ||
134 | NAME_REF@[589; 594) | ||
135 | IDENT@[589; 594) "Event" | ||
136 | TYPE_ARG_LIST@[594; 600) | ||
137 | L_ANGLE@[594; 595) | ||
138 | TYPE_ARG@[595; 599) | ||
139 | PATH_TYPE@[595; 599) | ||
140 | PATH@[595; 599) | ||
141 | PATH_SEGMENT@[595; 599) | ||
142 | NAME_REF@[595; 599) | ||
143 | IDENT@[595; 599) "Self" | ||
144 | R_ANGLE@[599; 600) | ||
145 | R_PAREN@[600; 601) | ||
146 | WHITESPACE@[601; 602) | ||
147 | BLOCK@[602; 683) | ||
148 | L_CURLY@[602; 603) | ||
149 | WHITESPACE@[603; 612) | ||
150 | ATTR@[612; 639) | ||
151 | POUND@[612; 613) | ||
152 | EXCL@[613; 614) | ||
153 | TOKEN_TREE@[614; 639) | ||
154 | L_BRACK@[614; 615) | ||
155 | IDENT@[615; 620) "allow" | ||
156 | TOKEN_TREE@[620; 638) | ||
157 | L_PAREN@[620; 621) | ||
158 | IDENT@[621; 637) "unused_variables" | ||
159 | R_PAREN@[637; 638) | ||
160 | R_BRACK@[638; 639) | ||
161 | WHITESPACE@[639; 640) | ||
162 | COMMENT@[640; 677) | ||
163 | WHITESPACE@[677; 682) | ||
164 | R_CURLY@[682; 683) | ||
165 | WHITESPACE@[683; 684) | ||
166 | R_CURLY@[684; 685) | ||
167 | WHITESPACE@[685; 686) | ||
diff --git a/crates/ra_syntax/tests/data/parser/ok/0045_extern_inner_attributes.rs b/crates/ra_syntax/tests/data/parser/ok/0045_extern_inner_attributes.rs new file mode 100644 index 000000000..fe67e2df4 --- /dev/null +++ b/crates/ra_syntax/tests/data/parser/ok/0045_extern_inner_attributes.rs | |||
@@ -0,0 +1,4 @@ | |||
1 | extern "C" { | ||
2 | //! This is a doc comment | ||
3 | #![doc("This is also a doc comment")] | ||
4 | } | ||
diff --git a/crates/ra_syntax/tests/data/parser/ok/0045_extern_inner_attributes.txt b/crates/ra_syntax/tests/data/parser/ok/0045_extern_inner_attributes.txt new file mode 100644 index 000000000..c68e1b271 --- /dev/null +++ b/crates/ra_syntax/tests/data/parser/ok/0045_extern_inner_attributes.txt | |||
@@ -0,0 +1,26 @@ | |||
1 | SOURCE_FILE@[0; 87) | ||
2 | EXTERN_BLOCK@[0; 86) | ||
3 | ABI@[0; 10) | ||
4 | EXTERN_KW@[0; 6) | ||
5 | WHITESPACE@[6; 7) | ||
6 | STRING@[7; 10) | ||
7 | WHITESPACE@[10; 11) | ||
8 | EXTERN_ITEM_LIST@[11; 86) | ||
9 | L_CURLY@[11; 12) | ||
10 | WHITESPACE@[12; 17) | ||
11 | COMMENT@[17; 42) | ||
12 | WHITESPACE@[42; 47) | ||
13 | ATTR@[47; 84) | ||
14 | POUND@[47; 48) | ||
15 | EXCL@[48; 49) | ||
16 | TOKEN_TREE@[49; 84) | ||
17 | L_BRACK@[49; 50) | ||
18 | IDENT@[50; 53) "doc" | ||
19 | TOKEN_TREE@[53; 83) | ||
20 | L_PAREN@[53; 54) | ||
21 | STRING@[54; 82) | ||
22 | R_PAREN@[82; 83) | ||
23 | R_BRACK@[83; 84) | ||
24 | WHITESPACE@[84; 85) | ||
25 | R_CURLY@[85; 86) | ||
26 | WHITESPACE@[86; 87) | ||