diff options
Diffstat (limited to 'crates/ra_syntax/src')
-rw-r--r-- | crates/ra_syntax/src/ast.rs | 46 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/extensions.rs | 10 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/generated.rs | 2 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/traits.rs | 12 | ||||
-rw-r--r-- | crates/ra_syntax/src/grammar.ron | 2 | ||||
-rw-r--r-- | crates/ra_syntax/src/parsing/lexer.rs | 35 | ||||
-rw-r--r-- | crates/ra_syntax/src/validation.rs | 25 | ||||
-rw-r--r-- | crates/ra_syntax/src/validation/field_expr.rs | 13 |
8 files changed, 104 insertions, 41 deletions
diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs index c5746d98d..6f0489617 100644 --- a/crates/ra_syntax/src/ast.rs +++ b/crates/ra_syntax/src/ast.rs | |||
@@ -139,6 +139,52 @@ fn test_doc_comment_preserves_newlines() { | |||
139 | } | 139 | } |
140 | 140 | ||
141 | #[test] | 141 | #[test] |
142 | fn test_doc_comment_single_line_block_strips_suffix() { | ||
143 | let file = SourceFile::parse( | ||
144 | r#" | ||
145 | /** this is mod foo*/ | ||
146 | mod foo {} | ||
147 | "#, | ||
148 | ) | ||
149 | .ok() | ||
150 | .unwrap(); | ||
151 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); | ||
152 | assert_eq!("this is mod foo", module.doc_comment_text().unwrap()); | ||
153 | } | ||
154 | |||
155 | #[test] | ||
156 | fn test_doc_comment_single_line_block_strips_suffix_whitespace() { | ||
157 | let file = SourceFile::parse( | ||
158 | r#" | ||
159 | /** this is mod foo */ | ||
160 | mod foo {} | ||
161 | "#, | ||
162 | ) | ||
163 | .ok() | ||
164 | .unwrap(); | ||
165 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); | ||
166 | assert_eq!("this is mod foo", module.doc_comment_text().unwrap()); | ||
167 | } | ||
168 | |||
169 | #[test] | ||
170 | fn test_doc_comment_multi_line_block_strips_suffix() { | ||
171 | let file = SourceFile::parse( | ||
172 | r#" | ||
173 | /** | ||
174 | this | ||
175 | is | ||
176 | mod foo | ||
177 | */ | ||
178 | mod foo {} | ||
179 | "#, | ||
180 | ) | ||
181 | .ok() | ||
182 | .unwrap(); | ||
183 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); | ||
184 | assert_eq!(" this\n is\n mod foo", module.doc_comment_text().unwrap()); | ||
185 | } | ||
186 | |||
187 | #[test] | ||
142 | fn test_where_predicates() { | 188 | fn test_where_predicates() { |
143 | fn assert_bound(text: &str, bound: Option<TypeBound>) { | 189 | fn assert_bound(text: &str, bound: Option<TypeBound>) { |
144 | assert_eq!(text, bound.unwrap().syntax().text().to_string()); | 190 | assert_eq!(text, bound.unwrap().syntax().text().to_string()); |
diff --git a/crates/ra_syntax/src/ast/extensions.rs b/crates/ra_syntax/src/ast/extensions.rs index d4873b39a..2a59cf653 100644 --- a/crates/ra_syntax/src/ast/extensions.rs +++ b/crates/ra_syntax/src/ast/extensions.rs | |||
@@ -91,6 +91,7 @@ impl ast::Attr { | |||
91 | #[derive(Debug, Clone, PartialEq, Eq)] | 91 | #[derive(Debug, Clone, PartialEq, Eq)] |
92 | pub enum PathSegmentKind { | 92 | pub enum PathSegmentKind { |
93 | Name(ast::NameRef), | 93 | Name(ast::NameRef), |
94 | Type { type_ref: Option<ast::TypeRef>, trait_ref: Option<ast::PathType> }, | ||
94 | SelfKw, | 95 | SelfKw, |
95 | SuperKw, | 96 | SuperKw, |
96 | CrateKw, | 97 | CrateKw, |
@@ -112,6 +113,15 @@ impl ast::PathSegment { | |||
112 | T![self] => PathSegmentKind::SelfKw, | 113 | T![self] => PathSegmentKind::SelfKw, |
113 | T![super] => PathSegmentKind::SuperKw, | 114 | T![super] => PathSegmentKind::SuperKw, |
114 | T![crate] => PathSegmentKind::CrateKw, | 115 | T![crate] => PathSegmentKind::CrateKw, |
116 | T![<] => { | ||
117 | // <T> or <T as Trait> | ||
118 | // T is any TypeRef, Trait has to be a PathType | ||
119 | let mut type_refs = | ||
120 | self.syntax().children().filter(|node| ast::TypeRef::can_cast(node.kind())); | ||
121 | let type_ref = type_refs.next().and_then(ast::TypeRef::cast); | ||
122 | let trait_ref = type_refs.next().and_then(ast::PathType::cast); | ||
123 | PathSegmentKind::Type { type_ref, trait_ref } | ||
124 | } | ||
115 | _ => return None, | 125 | _ => return None, |
116 | } | 126 | } |
117 | }; | 127 | }; |
diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs index da8cf4ae8..f322e1d84 100644 --- a/crates/ra_syntax/src/ast/generated.rs +++ b/crates/ra_syntax/src/ast/generated.rs | |||
@@ -2013,6 +2013,7 @@ impl AstNode for Param { | |||
2013 | 2013 | ||
2014 | 2014 | ||
2015 | impl ast::TypeAscriptionOwner for Param {} | 2015 | impl ast::TypeAscriptionOwner for Param {} |
2016 | impl ast::AttrsOwner for Param {} | ||
2016 | impl Param { | 2017 | impl Param { |
2017 | pub fn pat(&self) -> Option<Pat> { | 2018 | pub fn pat(&self) -> Option<Pat> { |
2018 | super::child_opt(self) | 2019 | super::child_opt(self) |
@@ -2667,6 +2668,7 @@ impl AstNode for SelfParam { | |||
2667 | 2668 | ||
2668 | 2669 | ||
2669 | impl ast::TypeAscriptionOwner for SelfParam {} | 2670 | impl ast::TypeAscriptionOwner for SelfParam {} |
2671 | impl ast::AttrsOwner for SelfParam {} | ||
2670 | impl SelfParam {} | 2672 | impl SelfParam {} |
2671 | 2673 | ||
2672 | // SlicePat | 2674 | // SlicePat |
diff --git a/crates/ra_syntax/src/ast/traits.rs b/crates/ra_syntax/src/ast/traits.rs index 6ed1b5213..1b9a2b20c 100644 --- a/crates/ra_syntax/src/ast/traits.rs +++ b/crates/ra_syntax/src/ast/traits.rs | |||
@@ -115,8 +115,8 @@ pub trait DocCommentsOwner: AstNode { | |||
115 | } | 115 | } |
116 | 116 | ||
117 | /// Returns the textual content of a doc comment block as a single string. | 117 | /// Returns the textual content of a doc comment block as a single string. |
118 | /// That is, strips leading `///` (+ optional 1 character of whitespace) | 118 | /// That is, strips leading `///` (+ optional 1 character of whitespace), |
119 | /// and joins lines. | 119 | /// trailing `*/`, trailing whitespace and then joins the lines. |
120 | fn doc_comment_text(&self) -> Option<String> { | 120 | fn doc_comment_text(&self) -> Option<String> { |
121 | let mut has_comments = false; | 121 | let mut has_comments = false; |
122 | let docs = self | 122 | let docs = self |
@@ -136,7 +136,13 @@ pub trait DocCommentsOwner: AstNode { | |||
136 | prefix_len | 136 | prefix_len |
137 | }; | 137 | }; |
138 | 138 | ||
139 | line[pos..].to_owned() | 139 | let end = if comment.kind().shape.is_block() && line.ends_with("*/") { |
140 | line.len() - 2 | ||
141 | } else { | ||
142 | line.len() | ||
143 | }; | ||
144 | |||
145 | line[pos..end].trim_end().to_owned() | ||
140 | }) | 146 | }) |
141 | .join("\n"); | 147 | .join("\n"); |
142 | 148 | ||
diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index 817dedfbf..f2c20573e 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron | |||
@@ -642,12 +642,14 @@ Grammar( | |||
642 | "SelfParam": ( | 642 | "SelfParam": ( |
643 | traits: [ | 643 | traits: [ |
644 | "TypeAscriptionOwner", | 644 | "TypeAscriptionOwner", |
645 | "AttrsOwner", | ||
645 | ] | 646 | ] |
646 | ), | 647 | ), |
647 | "Param": ( | 648 | "Param": ( |
648 | options: [ "Pat" ], | 649 | options: [ "Pat" ], |
649 | traits: [ | 650 | traits: [ |
650 | "TypeAscriptionOwner", | 651 | "TypeAscriptionOwner", |
652 | "AttrsOwner", | ||
651 | ] | 653 | ] |
652 | ), | 654 | ), |
653 | "UseItem": ( | 655 | "UseItem": ( |
diff --git a/crates/ra_syntax/src/parsing/lexer.rs b/crates/ra_syntax/src/parsing/lexer.rs index 2a4343b0a..06822ea91 100644 --- a/crates/ra_syntax/src/parsing/lexer.rs +++ b/crates/ra_syntax/src/parsing/lexer.rs | |||
@@ -12,6 +12,19 @@ pub struct Token { | |||
12 | pub len: TextUnit, | 12 | pub len: TextUnit, |
13 | } | 13 | } |
14 | 14 | ||
15 | fn match_literal_kind(kind: ra_rustc_lexer::LiteralKind) -> SyntaxKind { | ||
16 | match kind { | ||
17 | ra_rustc_lexer::LiteralKind::Int { .. } => INT_NUMBER, | ||
18 | ra_rustc_lexer::LiteralKind::Float { .. } => FLOAT_NUMBER, | ||
19 | ra_rustc_lexer::LiteralKind::Char { .. } => CHAR, | ||
20 | ra_rustc_lexer::LiteralKind::Byte { .. } => BYTE, | ||
21 | ra_rustc_lexer::LiteralKind::Str { .. } => STRING, | ||
22 | ra_rustc_lexer::LiteralKind::ByteStr { .. } => BYTE_STRING, | ||
23 | ra_rustc_lexer::LiteralKind::RawStr { .. } => RAW_STRING, | ||
24 | ra_rustc_lexer::LiteralKind::RawByteStr { .. } => RAW_BYTE_STRING, | ||
25 | } | ||
26 | } | ||
27 | |||
15 | /// Break a string up into its component tokens | 28 | /// Break a string up into its component tokens |
16 | pub fn tokenize(text: &str) -> Vec<Token> { | 29 | pub fn tokenize(text: &str) -> Vec<Token> { |
17 | if text.is_empty() { | 30 | if text.is_empty() { |
@@ -53,16 +66,7 @@ pub fn tokenize(text: &str) -> Vec<Token> { | |||
53 | } | 66 | } |
54 | } | 67 | } |
55 | ra_rustc_lexer::TokenKind::RawIdent => IDENT, | 68 | ra_rustc_lexer::TokenKind::RawIdent => IDENT, |
56 | ra_rustc_lexer::TokenKind::Literal { kind, .. } => match kind { | 69 | ra_rustc_lexer::TokenKind::Literal { kind, .. } => match_literal_kind(kind), |
57 | ra_rustc_lexer::LiteralKind::Int { .. } => INT_NUMBER, | ||
58 | ra_rustc_lexer::LiteralKind::Float { .. } => FLOAT_NUMBER, | ||
59 | ra_rustc_lexer::LiteralKind::Char { .. } => CHAR, | ||
60 | ra_rustc_lexer::LiteralKind::Byte { .. } => BYTE, | ||
61 | ra_rustc_lexer::LiteralKind::Str { .. } => STRING, | ||
62 | ra_rustc_lexer::LiteralKind::ByteStr { .. } => BYTE_STRING, | ||
63 | ra_rustc_lexer::LiteralKind::RawStr { .. } => RAW_STRING, | ||
64 | ra_rustc_lexer::LiteralKind::RawByteStr { .. } => RAW_BYTE_STRING, | ||
65 | }, | ||
66 | ra_rustc_lexer::TokenKind::Lifetime { .. } => LIFETIME, | 70 | ra_rustc_lexer::TokenKind::Lifetime { .. } => LIFETIME, |
67 | ra_rustc_lexer::TokenKind::Semi => SEMI, | 71 | ra_rustc_lexer::TokenKind::Semi => SEMI, |
68 | ra_rustc_lexer::TokenKind::Comma => COMMA, | 72 | ra_rustc_lexer::TokenKind::Comma => COMMA, |
@@ -131,16 +135,7 @@ pub fn classify_literal(text: &str) -> Option<Token> { | |||
131 | return None; | 135 | return None; |
132 | } | 136 | } |
133 | let kind = match t.kind { | 137 | let kind = match t.kind { |
134 | ra_rustc_lexer::TokenKind::Literal { kind, .. } => match kind { | 138 | ra_rustc_lexer::TokenKind::Literal { kind, .. } => match_literal_kind(kind), |
135 | ra_rustc_lexer::LiteralKind::Int { .. } => INT_NUMBER, | ||
136 | ra_rustc_lexer::LiteralKind::Float { .. } => FLOAT_NUMBER, | ||
137 | ra_rustc_lexer::LiteralKind::Char { .. } => CHAR, | ||
138 | ra_rustc_lexer::LiteralKind::Byte { .. } => BYTE, | ||
139 | ra_rustc_lexer::LiteralKind::Str { .. } => STRING, | ||
140 | ra_rustc_lexer::LiteralKind::ByteStr { .. } => BYTE_STRING, | ||
141 | ra_rustc_lexer::LiteralKind::RawStr { .. } => RAW_STRING, | ||
142 | ra_rustc_lexer::LiteralKind::RawByteStr { .. } => RAW_BYTE_STRING, | ||
143 | }, | ||
144 | _ => return None, | 139 | _ => return None, |
145 | }; | 140 | }; |
146 | Some(Token { kind, len: TextUnit::from_usize(t.len) }) | 141 | Some(Token { kind, len: TextUnit::from_usize(t.len) }) |
diff --git a/crates/ra_syntax/src/validation.rs b/crates/ra_syntax/src/validation.rs index 1f904434e..2bb3c0a03 100644 --- a/crates/ra_syntax/src/validation.rs +++ b/crates/ra_syntax/src/validation.rs | |||
@@ -1,13 +1,12 @@ | |||
1 | mod block; | 1 | mod block; |
2 | mod field_expr; | ||
3 | 2 | ||
4 | use ra_rustc_lexer::unescape; | 3 | use ra_rustc_lexer::unescape; |
5 | 4 | ||
6 | use crate::{ | 5 | use crate::{ |
7 | algo::visit::{visitor_ctx, VisitorCtx}, | 6 | algo::visit::{visitor_ctx, VisitorCtx}, |
8 | ast, SyntaxError, SyntaxErrorKind, | 7 | ast, AstNode, SyntaxError, SyntaxErrorKind, |
9 | SyntaxKind::{BYTE, BYTE_STRING, CHAR, STRING}, | 8 | SyntaxKind::{BYTE, BYTE_STRING, CHAR, INT_NUMBER, STRING}, |
10 | SyntaxNode, TextUnit, T, | 9 | SyntaxNode, SyntaxToken, TextUnit, T, |
11 | }; | 10 | }; |
12 | 11 | ||
13 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] | 12 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] |
@@ -101,7 +100,8 @@ pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> { | |||
101 | let _ = visitor_ctx(&mut errors) | 100 | let _ = visitor_ctx(&mut errors) |
102 | .visit::<ast::Literal, _>(validate_literal) | 101 | .visit::<ast::Literal, _>(validate_literal) |
103 | .visit::<ast::Block, _>(block::validate_block_node) | 102 | .visit::<ast::Block, _>(block::validate_block_node) |
104 | .visit::<ast::FieldExpr, _>(field_expr::validate_field_expr_node) | 103 | .visit::<ast::FieldExpr, _>(|it, errors| validate_numeric_name(it.name_ref(), errors)) |
104 | .visit::<ast::NamedField, _>(|it, errors| validate_numeric_name(it.name_ref(), errors)) | ||
105 | .accept(&node); | 105 | .accept(&node); |
106 | } | 106 | } |
107 | errors | 107 | errors |
@@ -189,3 +189,18 @@ pub(crate) fn validate_block_structure(root: &SyntaxNode) { | |||
189 | } | 189 | } |
190 | } | 190 | } |
191 | } | 191 | } |
192 | |||
193 | fn validate_numeric_name(name_ref: Option<ast::NameRef>, errors: &mut Vec<SyntaxError>) { | ||
194 | if let Some(int_token) = int_token(name_ref) { | ||
195 | if int_token.text().chars().any(|c| !c.is_digit(10)) { | ||
196 | errors.push(SyntaxError::new( | ||
197 | SyntaxErrorKind::InvalidTupleIndexFormat, | ||
198 | int_token.text_range(), | ||
199 | )); | ||
200 | } | ||
201 | } | ||
202 | |||
203 | fn int_token(name_ref: Option<ast::NameRef>) -> Option<SyntaxToken> { | ||
204 | name_ref?.syntax().first_child_or_token()?.into_token().filter(|it| it.kind() == INT_NUMBER) | ||
205 | } | ||
206 | } | ||
diff --git a/crates/ra_syntax/src/validation/field_expr.rs b/crates/ra_syntax/src/validation/field_expr.rs deleted file mode 100644 index 004f199fd..000000000 --- a/crates/ra_syntax/src/validation/field_expr.rs +++ /dev/null | |||
@@ -1,13 +0,0 @@ | |||
1 | use crate::{ | ||
2 | ast::{self, FieldKind}, | ||
3 | SyntaxError, | ||
4 | SyntaxErrorKind::*, | ||
5 | }; | ||
6 | |||
7 | pub(crate) fn validate_field_expr_node(node: ast::FieldExpr, errors: &mut Vec<SyntaxError>) { | ||
8 | if let Some(FieldKind::Index(idx)) = node.field_access() { | ||
9 | if idx.text().chars().any(|c| c < '0' || c > '9') { | ||
10 | errors.push(SyntaxError::new(InvalidTupleIndexFormat, idx.text_range())); | ||
11 | } | ||
12 | } | ||
13 | } | ||