aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_syntax/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_syntax/src')
-rw-r--r--crates/ra_syntax/src/ast.rs46
-rw-r--r--crates/ra_syntax/src/ast/extensions.rs10
-rw-r--r--crates/ra_syntax/src/ast/generated.rs2
-rw-r--r--crates/ra_syntax/src/ast/traits.rs12
-rw-r--r--crates/ra_syntax/src/grammar.ron2
-rw-r--r--crates/ra_syntax/src/parsing/lexer.rs35
-rw-r--r--crates/ra_syntax/src/validation.rs25
-rw-r--r--crates/ra_syntax/src/validation/field_expr.rs13
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]
142fn 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]
156fn 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]
170fn 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]
142fn test_where_predicates() { 188fn 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)]
92pub enum PathSegmentKind { 92pub 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
2015impl ast::TypeAscriptionOwner for Param {} 2015impl ast::TypeAscriptionOwner for Param {}
2016impl ast::AttrsOwner for Param {}
2016impl Param { 2017impl 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
2669impl ast::TypeAscriptionOwner for SelfParam {} 2670impl ast::TypeAscriptionOwner for SelfParam {}
2671impl ast::AttrsOwner for SelfParam {}
2670impl SelfParam {} 2672impl 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
15fn 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
16pub fn tokenize(text: &str) -> Vec<Token> { 29pub 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 @@
1mod block; 1mod block;
2mod field_expr;
3 2
4use ra_rustc_lexer::unescape; 3use ra_rustc_lexer::unescape;
5 4
6use crate::{ 5use 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
193fn 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 @@
1use crate::{
2 ast::{self, FieldKind},
3 SyntaxError,
4 SyntaxErrorKind::*,
5};
6
7pub(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}