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.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
6 files changed, 150 insertions, 13 deletions
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}