aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_ide_api_light/src/assists.rs8
-rw-r--r--crates/ra_ide_api_light/src/assists/introduce_variable.rs297
-rw-r--r--crates/ra_ide_api_light/src/test_utils.rs12
-rw-r--r--crates/ra_syntax/src/ast/generated.rs1
-rw-r--r--crates/ra_syntax/src/grammar.ron3
-rw-r--r--crates/ra_syntax/src/grammar/expressions.rs2
-rw-r--r--crates/ra_syntax/src/grammar/items/traits.rs7
-rw-r--r--crates/ra_syntax/src/validation.rs2
-rw-r--r--crates/ra_syntax/src/validation/block.rs24
-rw-r--r--crates/ra_syntax/src/yellow/syntax_error.rs4
-rw-r--r--crates/ra_syntax/tests/data/parser/err/0031_block_inner_attrs.rs15
-rw-r--r--crates/ra_syntax/tests/data/parser/err/0031_block_inner_attrs.txt114
-rw-r--r--crates/ra_syntax/tests/data/parser/inline/ok/0118_impl_inner_attributes.rs5
-rw-r--r--crates/ra_syntax/tests/data/parser/inline/ok/0118_impl_inner_attributes.txt38
-rw-r--r--crates/ra_syntax/tests/data/parser/ok/0045_block_inner_attrs.rs20
-rw-r--r--crates/ra_syntax/tests/data/parser/ok/0045_block_inner_attrs.txt167
-rw-r--r--crates/ra_syntax/tests/data/parser/ok/0045_extern_inner_attributes.rs4
-rw-r--r--crates/ra_syntax/tests/data/parser/ok/0045_extern_inner_attributes.txt26
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)]
200fn 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)]
200fn check_assist_range(assist: fn(AssistCtx) -> Option<Assist>, before: &str, after: &str) { 208fn 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 @@
1use ra_syntax::{ 1use 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
7use crate::assists::{AssistCtx, Assist}; 8use crate::assists::{AssistCtx, Assist};
8 9
9pub fn introduce_variable<'a>(ctx: AssistCtx) -> Option<Assist> { 10pub 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 57fn valid_covering_node(node: &SyntaxNode) -> bool {
45/// the freshly introduced var. 58 node.kind() != COMMENT
46fn 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.
62fn 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.
77fn 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)]
65mod tests { 104mod 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 "
209fn main() {
210 let x = true;
211 let tuple = match x {
212 true => (<|>2 + 2<|>, true)
213 _ => (0, false)
214 };
215}
216",
217 "
218fn 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 "
234fn 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 "
246fn 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 "
266fn main() {
267 let lambda = |x: u32| <|>x * 2<|>;
268}
269",
270 "
271fn 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 "
283fn main() {
284 let lambda = |x: u32| { <|>x * 2<|> };
285}
286",
287 "
288fn 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 "
300fn main() {
301 let o = S<|>ome(true);
302}
303",
304 "
305fn 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 "
318fn main() {
319 let v = b<|>ar.foo();
320}
321",
322 "
323fn 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 "
336fn foo() -> u32 {
337 r<|>eturn 2 + 2;
338}
339",
340 "
341fn 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 "
354fn main() {
355 let result = loop {
356 b<|>reak 2 + 2;
357 };
358}
359",
360 "
361fn 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 "
376fn main() {
377 let v = 0f32 a<|>s u32;
378}
379",
380 "
381fn 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 "
394fn 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 "
406fn 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 "
420fn 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
26pub 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
26pub fn check_action_range<F: Fn(&SourceFile, TextRange) -> Option<LocalEdit>>( 38pub 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
275impl ast::AttrsOwner for Block {}
275impl Block { 276impl 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;
2mod byte_string; 2mod byte_string;
3mod char; 3mod char;
4mod string; 4mod string;
5mod block;
5 6
6use crate::{ 7use 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 @@
1use crate::{SyntaxKind::*,
2 ast::{self, AttrsOwner, AstNode},
3 yellow::{
4 SyntaxError,
5 SyntaxErrorKind::*,
6 },
7};
8
9pub(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 @@
1fn 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 @@
1SOURCE_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 @@
1enum F{}
2impl 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 @@
1SOURCE_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 @@
1fn 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
16impl 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 @@
1SOURCE_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 @@
1extern "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 @@
1SOURCE_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)