aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_syntax
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_syntax')
-rw-r--r--crates/ra_syntax/Cargo.toml3
-rw-r--r--crates/ra_syntax/src/ast.rs2
-rw-r--r--crates/ra_syntax/src/ast/expr_extensions.rs46
-rw-r--r--crates/ra_syntax/src/ast/tokens.rs95
-rw-r--r--crates/ra_syntax/src/lib.rs6
-rw-r--r--crates/ra_syntax/src/syntax_error.rs4
-rw-r--r--crates/ra_syntax/src/validation.rs10
-rw-r--r--crates/ra_syntax/test_data/parser/err/0038_endless_inclusive_range.rs4
-rw-r--r--crates/ra_syntax/test_data/parser/err/0038_endless_inclusive_range.txt30
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0060_as_range.rs4
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0060_as_range.txt56
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0061_match_full_range.rs4
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0061_match_full_range.txt27
13 files changed, 278 insertions, 13 deletions
diff --git a/crates/ra_syntax/Cargo.toml b/crates/ra_syntax/Cargo.toml
index 9c0e856e8..45a18a73f 100644
--- a/crates/ra_syntax/Cargo.toml
+++ b/crates/ra_syntax/Cargo.toml
@@ -7,6 +7,9 @@ license = "MIT OR Apache-2.0"
7description = "Comment and whitespace preserving parser for the Rust langauge" 7description = "Comment and whitespace preserving parser for the Rust langauge"
8repository = "https://github.com/rust-analyzer/rust-analyzer" 8repository = "https://github.com/rust-analyzer/rust-analyzer"
9 9
10[lib]
11doctest = false
12
10[dependencies] 13[dependencies]
11itertools = "0.8.0" 14itertools = "0.8.0"
12rowan = "0.6.1" 15rowan = "0.6.1"
diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs
index 1ec9881b9..277532a8c 100644
--- a/crates/ra_syntax/src/ast.rs
+++ b/crates/ra_syntax/src/ast.rs
@@ -16,7 +16,7 @@ use crate::{
16}; 16};
17 17
18pub use self::{ 18pub use self::{
19 expr_extensions::{ArrayExprKind, BinOp, ElseBranch, LiteralKind, PrefixOp}, 19 expr_extensions::{ArrayExprKind, BinOp, ElseBranch, LiteralKind, PrefixOp, RangeOp},
20 extensions::{FieldKind, PathSegmentKind, SelfParamKind, StructKind, TypeBoundKind}, 20 extensions::{FieldKind, PathSegmentKind, SelfParamKind, StructKind, TypeBoundKind},
21 generated::*, 21 generated::*,
22 tokens::*, 22 tokens::*,
diff --git a/crates/ra_syntax/src/ast/expr_extensions.rs b/crates/ra_syntax/src/ast/expr_extensions.rs
index 25dbd0bed..7c53aa934 100644
--- a/crates/ra_syntax/src/ast/expr_extensions.rs
+++ b/crates/ra_syntax/src/ast/expr_extensions.rs
@@ -189,6 +189,52 @@ impl ast::BinExpr {
189 } 189 }
190} 190}
191 191
192#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
193pub enum RangeOp {
194 /// `..`
195 Exclusive,
196 /// `..=`
197 Inclusive,
198}
199
200impl ast::RangeExpr {
201 fn op_details(&self) -> Option<(usize, SyntaxToken, RangeOp)> {
202 self.syntax().children_with_tokens().enumerate().find_map(|(ix, child)| {
203 let token = child.into_token()?;
204 let bin_op = match token.kind() {
205 T![..] => RangeOp::Exclusive,
206 T![..=] => RangeOp::Inclusive,
207 _ => return None,
208 };
209 Some((ix, token, bin_op))
210 })
211 }
212
213 pub fn op_kind(&self) -> Option<RangeOp> {
214 self.op_details().map(|t| t.2)
215 }
216
217 pub fn op_token(&self) -> Option<SyntaxToken> {
218 self.op_details().map(|t| t.1)
219 }
220
221 pub fn start(&self) -> Option<ast::Expr> {
222 let op_ix = self.op_details()?.0;
223 self.syntax()
224 .children_with_tokens()
225 .take(op_ix)
226 .find_map(|it| ast::Expr::cast(it.into_node()?))
227 }
228
229 pub fn end(&self) -> Option<ast::Expr> {
230 let op_ix = self.op_details()?.0;
231 self.syntax()
232 .children_with_tokens()
233 .skip(op_ix + 1)
234 .find_map(|it| ast::Expr::cast(it.into_node()?))
235 }
236}
237
192impl ast::IndexExpr { 238impl ast::IndexExpr {
193 pub fn base(&self) -> Option<ast::Expr> { 239 pub fn base(&self) -> Option<ast::Expr> {
194 children(self).nth(0) 240 children(self).nth(0)
diff --git a/crates/ra_syntax/src/ast/tokens.rs b/crates/ra_syntax/src/ast/tokens.rs
index 87cca325d..ed8661faf 100644
--- a/crates/ra_syntax/src/ast/tokens.rs
+++ b/crates/ra_syntax/src/ast/tokens.rs
@@ -2,8 +2,8 @@
2 2
3use crate::{ 3use crate::{
4 ast::AstToken, 4 ast::AstToken,
5 SyntaxKind::{COMMENT, WHITESPACE}, 5 SyntaxKind::{COMMENT, RAW_STRING, STRING, WHITESPACE},
6 SyntaxToken, 6 SyntaxToken, TextRange, TextUnit,
7}; 7};
8 8
9#[derive(Debug, Clone, PartialEq, Eq, Hash)] 9#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -11,10 +11,9 @@ pub struct Comment(SyntaxToken);
11 11
12impl AstToken for Comment { 12impl AstToken for Comment {
13 fn cast(token: SyntaxToken) -> Option<Self> { 13 fn cast(token: SyntaxToken) -> Option<Self> {
14 if token.kind() == COMMENT { 14 match token.kind() {
15 Some(Comment(token)) 15 COMMENT => Some(Comment(token)),
16 } else { 16 _ => None,
17 None
18 } 17 }
19 } 18 }
20 fn syntax(&self) -> &SyntaxToken { 19 fn syntax(&self) -> &SyntaxToken {
@@ -94,10 +93,9 @@ pub struct Whitespace(SyntaxToken);
94 93
95impl AstToken for Whitespace { 94impl AstToken for Whitespace {
96 fn cast(token: SyntaxToken) -> Option<Self> { 95 fn cast(token: SyntaxToken) -> Option<Self> {
97 if token.kind() == WHITESPACE { 96 match token.kind() {
98 Some(Whitespace(token)) 97 WHITESPACE => Some(Whitespace(token)),
99 } else { 98 _ => None,
100 None
101 } 99 }
102 } 100 }
103 fn syntax(&self) -> &SyntaxToken { 101 fn syntax(&self) -> &SyntaxToken {
@@ -111,3 +109,80 @@ impl Whitespace {
111 text.find('\n').map_or(false, |idx| text[idx + 1..].contains('\n')) 109 text.find('\n').map_or(false, |idx| text[idx + 1..].contains('\n'))
112 } 110 }
113} 111}
112
113pub struct String(SyntaxToken);
114
115impl AstToken for String {
116 fn cast(token: SyntaxToken) -> Option<Self> {
117 match token.kind() {
118 STRING => Some(String(token)),
119 _ => None,
120 }
121 }
122 fn syntax(&self) -> &SyntaxToken {
123 &self.0
124 }
125}
126
127impl String {
128 pub fn value(&self) -> Option<std::string::String> {
129 let text = self.text().as_str();
130 let usual_string_range = find_usual_string_range(text)?;
131 let start_of_inside = usual_string_range.start().to_usize() + 1;
132 let end_of_inside = usual_string_range.end().to_usize();
133 let inside_str = &text[start_of_inside..end_of_inside];
134
135 let mut buf = std::string::String::with_capacity(inside_str.len());
136 let mut has_error = false;
137 rustc_lexer::unescape::unescape_str(inside_str, &mut |_, unescaped_char| {
138 match unescaped_char {
139 Ok(c) => buf.push(c),
140 Err(_) => has_error = true,
141 }
142 });
143
144 if has_error {
145 return None;
146 }
147 Some(buf)
148 }
149}
150
151pub struct RawString(SyntaxToken);
152
153impl AstToken for RawString {
154 fn cast(token: SyntaxToken) -> Option<Self> {
155 match token.kind() {
156 RAW_STRING => Some(RawString(token)),
157 _ => None,
158 }
159 }
160 fn syntax(&self) -> &SyntaxToken {
161 &self.0
162 }
163}
164
165impl RawString {
166 pub fn value(&self) -> Option<std::string::String> {
167 let text = self.text().as_str();
168 let usual_string_range = find_usual_string_range(text)?;
169 let start_of_inside = usual_string_range.start().to_usize() + 1;
170 let end_of_inside = usual_string_range.end().to_usize();
171 let inside_str = &text[start_of_inside..end_of_inside];
172 Some(inside_str.to_string())
173 }
174}
175
176fn find_usual_string_range(s: &str) -> Option<TextRange> {
177 let left_quote = s.find('"')?;
178 let right_quote = s.rfind('"')?;
179 if left_quote == right_quote {
180 // `s` only contains one quote
181 None
182 } else {
183 Some(TextRange::from_to(
184 TextUnit::from(left_quote as u32),
185 TextUnit::from(right_quote as u32),
186 ))
187 }
188}
diff --git a/crates/ra_syntax/src/lib.rs b/crates/ra_syntax/src/lib.rs
index 5dcb6a95a..9931fec84 100644
--- a/crates/ra_syntax/src/lib.rs
+++ b/crates/ra_syntax/src/lib.rs
@@ -176,9 +176,11 @@ impl SourceFile {
176/// ``` 176/// ```
177#[macro_export] 177#[macro_export]
178macro_rules! match_ast { 178macro_rules! match_ast {
179 (match $node:ident { 179 (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) };
180
181 (match ($node:expr) {
180 $( ast::$ast:ident($it:ident) => $res:block, )* 182 $( ast::$ast:ident($it:ident) => $res:block, )*
181 _ => $catch_all:expr, 183 _ => $catch_all:expr $(,)?
182 }) => {{ 184 }) => {{
183 $( if let Some($it) = ast::$ast::cast($node.clone()) $res else )* 185 $( if let Some($it) = ast::$ast::cast($node.clone()) $res else )*
184 { $catch_all } 186 { $catch_all }
diff --git a/crates/ra_syntax/src/syntax_error.rs b/crates/ra_syntax/src/syntax_error.rs
index 1f60a7aab..6c171df8d 100644
--- a/crates/ra_syntax/src/syntax_error.rs
+++ b/crates/ra_syntax/src/syntax_error.rs
@@ -83,6 +83,7 @@ pub enum SyntaxErrorKind {
83 InvalidMatchInnerAttr, 83 InvalidMatchInnerAttr,
84 InvalidTupleIndexFormat, 84 InvalidTupleIndexFormat,
85 VisibilityNotAllowed, 85 VisibilityNotAllowed,
86 InclusiveRangeMissingEnd,
86} 87}
87 88
88impl fmt::Display for SyntaxErrorKind { 89impl fmt::Display for SyntaxErrorKind {
@@ -103,6 +104,9 @@ impl fmt::Display for SyntaxErrorKind {
103 VisibilityNotAllowed => { 104 VisibilityNotAllowed => {
104 write!(f, "unnecessary visibility qualifier") 105 write!(f, "unnecessary visibility qualifier")
105 } 106 }
107 InclusiveRangeMissingEnd => {
108 write!(f, "An inclusive range must have an end expression")
109 }
106 } 110 }
107 } 111 }
108} 112}
diff --git a/crates/ra_syntax/src/validation.rs b/crates/ra_syntax/src/validation.rs
index 2d596763e..222ac15f8 100644
--- a/crates/ra_syntax/src/validation.rs
+++ b/crates/ra_syntax/src/validation.rs
@@ -103,6 +103,7 @@ pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> {
103 ast::FieldExpr(it) => { validate_numeric_name(it.name_ref(), &mut errors) }, 103 ast::FieldExpr(it) => { validate_numeric_name(it.name_ref(), &mut errors) },
104 ast::RecordField(it) => { validate_numeric_name(it.name_ref(), &mut errors) }, 104 ast::RecordField(it) => { validate_numeric_name(it.name_ref(), &mut errors) },
105 ast::Visibility(it) => { validate_visibility(it, &mut errors) }, 105 ast::Visibility(it) => { validate_visibility(it, &mut errors) },
106 ast::RangeExpr(it) => { validate_range_expr(it, &mut errors) },
106 _ => (), 107 _ => (),
107 } 108 }
108 } 109 }
@@ -227,3 +228,12 @@ fn validate_visibility(vis: ast::Visibility, errors: &mut Vec<SyntaxError>) {
227 .push(SyntaxError::new(SyntaxErrorKind::VisibilityNotAllowed, vis.syntax.text_range())) 228 .push(SyntaxError::new(SyntaxErrorKind::VisibilityNotAllowed, vis.syntax.text_range()))
228 } 229 }
229} 230}
231
232fn validate_range_expr(expr: ast::RangeExpr, errors: &mut Vec<SyntaxError>) {
233 if expr.op_kind() == Some(ast::RangeOp::Inclusive) && expr.end().is_none() {
234 errors.push(SyntaxError::new(
235 SyntaxErrorKind::InclusiveRangeMissingEnd,
236 expr.syntax().text_range(),
237 ));
238 }
239}
diff --git a/crates/ra_syntax/test_data/parser/err/0038_endless_inclusive_range.rs b/crates/ra_syntax/test_data/parser/err/0038_endless_inclusive_range.rs
new file mode 100644
index 000000000..0b4ed7a2b
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/err/0038_endless_inclusive_range.rs
@@ -0,0 +1,4 @@
1fn main() {
2 0..=;
3 ..=;
4}
diff --git a/crates/ra_syntax/test_data/parser/err/0038_endless_inclusive_range.txt b/crates/ra_syntax/test_data/parser/err/0038_endless_inclusive_range.txt
new file mode 100644
index 000000000..3810b9680
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/err/0038_endless_inclusive_range.txt
@@ -0,0 +1,30 @@
1SOURCE_FILE@[0; 33)
2 FN_DEF@[0; 32)
3 FN_KW@[0; 2) "fn"
4 WHITESPACE@[2; 3) " "
5 NAME@[3; 7)
6 IDENT@[3; 7) "main"
7 PARAM_LIST@[7; 9)
8 L_PAREN@[7; 8) "("
9 R_PAREN@[8; 9) ")"
10 WHITESPACE@[9; 10) " "
11 BLOCK_EXPR@[10; 32)
12 BLOCK@[10; 32)
13 L_CURLY@[10; 11) "{"
14 WHITESPACE@[11; 16) "\n "
15 EXPR_STMT@[16; 21)
16 RANGE_EXPR@[16; 20)
17 LITERAL@[16; 17)
18 INT_NUMBER@[16; 17) "0"
19 DOTDOTEQ@[17; 20) "..="
20 SEMI@[20; 21) ";"
21 WHITESPACE@[21; 26) "\n "
22 EXPR_STMT@[26; 30)
23 RANGE_EXPR@[26; 29)
24 DOTDOTEQ@[26; 29) "..="
25 SEMI@[29; 30) ";"
26 WHITESPACE@[30; 31) "\n"
27 R_CURLY@[31; 32) "}"
28 WHITESPACE@[32; 33) "\n"
29error [16; 20): An inclusive range must have an end expression
30error [26; 29): An inclusive range must have an end expression
diff --git a/crates/ra_syntax/test_data/parser/ok/0060_as_range.rs b/crates/ra_syntax/test_data/parser/ok/0060_as_range.rs
new file mode 100644
index 000000000..f063ffadb
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/ok/0060_as_range.rs
@@ -0,0 +1,4 @@
1fn main() {
2 0 as usize ..;
3 1 + 2 as usize ..;
4}
diff --git a/crates/ra_syntax/test_data/parser/ok/0060_as_range.txt b/crates/ra_syntax/test_data/parser/ok/0060_as_range.txt
new file mode 100644
index 000000000..ad0c4a3fe
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/ok/0060_as_range.txt
@@ -0,0 +1,56 @@
1SOURCE_FILE@[0; 56)
2 FN_DEF@[0; 55)
3 FN_KW@[0; 2) "fn"
4 WHITESPACE@[2; 3) " "
5 NAME@[3; 7)
6 IDENT@[3; 7) "main"
7 PARAM_LIST@[7; 9)
8 L_PAREN@[7; 8) "("
9 R_PAREN@[8; 9) ")"
10 WHITESPACE@[9; 10) " "
11 BLOCK_EXPR@[10; 55)
12 BLOCK@[10; 55)
13 L_CURLY@[10; 11) "{"
14 WHITESPACE@[11; 16) "\n "
15 EXPR_STMT@[16; 30)
16 RANGE_EXPR@[16; 29)
17 CAST_EXPR@[16; 26)
18 LITERAL@[16; 17)
19 INT_NUMBER@[16; 17) "0"
20 WHITESPACE@[17; 18) " "
21 AS_KW@[18; 20) "as"
22 WHITESPACE@[20; 21) " "
23 PATH_TYPE@[21; 26)
24 PATH@[21; 26)
25 PATH_SEGMENT@[21; 26)
26 NAME_REF@[21; 26)
27 IDENT@[21; 26) "usize"
28 WHITESPACE@[26; 27) " "
29 DOTDOT@[27; 29) ".."
30 SEMI@[29; 30) ";"
31 WHITESPACE@[30; 35) "\n "
32 EXPR_STMT@[35; 53)
33 RANGE_EXPR@[35; 52)
34 BIN_EXPR@[35; 49)
35 LITERAL@[35; 36)
36 INT_NUMBER@[35; 36) "1"
37 WHITESPACE@[36; 37) " "
38 PLUS@[37; 38) "+"
39 WHITESPACE@[38; 39) " "
40 CAST_EXPR@[39; 49)
41 LITERAL@[39; 40)
42 INT_NUMBER@[39; 40) "2"
43 WHITESPACE@[40; 41) " "
44 AS_KW@[41; 43) "as"
45 WHITESPACE@[43; 44) " "
46 PATH_TYPE@[44; 49)
47 PATH@[44; 49)
48 PATH_SEGMENT@[44; 49)
49 NAME_REF@[44; 49)
50 IDENT@[44; 49) "usize"
51 WHITESPACE@[49; 50) " "
52 DOTDOT@[50; 52) ".."
53 SEMI@[52; 53) ";"
54 WHITESPACE@[53; 54) "\n"
55 R_CURLY@[54; 55) "}"
56 WHITESPACE@[55; 56) "\n"
diff --git a/crates/ra_syntax/test_data/parser/ok/0061_match_full_range.rs b/crates/ra_syntax/test_data/parser/ok/0061_match_full_range.rs
new file mode 100644
index 000000000..2c4ed11e1
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/ok/0061_match_full_range.rs
@@ -0,0 +1,4 @@
1fn main() {
2 match .. {
3 }
4}
diff --git a/crates/ra_syntax/test_data/parser/ok/0061_match_full_range.txt b/crates/ra_syntax/test_data/parser/ok/0061_match_full_range.txt
new file mode 100644
index 000000000..bdfac9b76
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/ok/0061_match_full_range.txt
@@ -0,0 +1,27 @@
1SOURCE_FILE@[0; 35)
2 FN_DEF@[0; 34)
3 FN_KW@[0; 2) "fn"
4 WHITESPACE@[2; 3) " "
5 NAME@[3; 7)
6 IDENT@[3; 7) "main"
7 PARAM_LIST@[7; 9)
8 L_PAREN@[7; 8) "("
9 R_PAREN@[8; 9) ")"
10 WHITESPACE@[9; 10) " "
11 BLOCK_EXPR@[10; 34)
12 BLOCK@[10; 34)
13 L_CURLY@[10; 11) "{"
14 WHITESPACE@[11; 16) "\n "
15 MATCH_EXPR@[16; 32)
16 MATCH_KW@[16; 21) "match"
17 WHITESPACE@[21; 22) " "
18 RANGE_EXPR@[22; 24)
19 DOTDOT@[22; 24) ".."
20 WHITESPACE@[24; 25) " "
21 MATCH_ARM_LIST@[25; 32)
22 L_CURLY@[25; 26) "{"
23 WHITESPACE@[26; 31) "\n "
24 R_CURLY@[31; 32) "}"
25 WHITESPACE@[32; 33) "\n"
26 R_CURLY@[33; 34) "}"
27 WHITESPACE@[34; 35) "\n"