diff options
Diffstat (limited to 'crates/ra_syntax')
-rw-r--r-- | crates/ra_syntax/Cargo.toml | 2 | ||||
-rw-r--r-- | crates/ra_syntax/src/algo.rs | 36 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/edit.rs | 44 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/make.rs | 36 | ||||
-rw-r--r-- | crates/ra_syntax/src/ast/tokens.rs | 5 | ||||
-rw-r--r-- | crates/ra_syntax/src/validation.rs | 20 |
6 files changed, 94 insertions, 49 deletions
diff --git a/crates/ra_syntax/Cargo.toml b/crates/ra_syntax/Cargo.toml index c07ff488e..a9a5cc7bc 100644 --- a/crates/ra_syntax/Cargo.toml +++ b/crates/ra_syntax/Cargo.toml | |||
@@ -13,7 +13,7 @@ doctest = false | |||
13 | [dependencies] | 13 | [dependencies] |
14 | itertools = "0.9.0" | 14 | itertools = "0.9.0" |
15 | rowan = "0.10.0" | 15 | rowan = "0.10.0" |
16 | rustc_lexer = { version = "656.0.0", package = "rustc-ap-rustc_lexer" } | 16 | rustc_lexer = { version = "660.0.0", package = "rustc-ap-rustc_lexer" } |
17 | rustc-hash = "1.1.0" | 17 | rustc-hash = "1.1.0" |
18 | arrayvec = "0.5.1" | 18 | arrayvec = "0.5.1" |
19 | once_cell = "1.3.1" | 19 | once_cell = "1.3.1" |
diff --git a/crates/ra_syntax/src/algo.rs b/crates/ra_syntax/src/algo.rs index 2a8dac757..664894d1f 100644 --- a/crates/ra_syntax/src/algo.rs +++ b/crates/ra_syntax/src/algo.rs | |||
@@ -266,6 +266,15 @@ impl<'a> SyntaxRewriter<'a> { | |||
266 | let replacement = Replacement::Single(with.clone().into()); | 266 | let replacement = Replacement::Single(with.clone().into()); |
267 | self.replacements.insert(what, replacement); | 267 | self.replacements.insert(what, replacement); |
268 | } | 268 | } |
269 | pub fn replace_with_many<T: Clone + Into<SyntaxElement>>( | ||
270 | &mut self, | ||
271 | what: &T, | ||
272 | with: Vec<SyntaxElement>, | ||
273 | ) { | ||
274 | let what = what.clone().into(); | ||
275 | let replacement = Replacement::Many(with); | ||
276 | self.replacements.insert(what, replacement); | ||
277 | } | ||
269 | pub fn replace_ast<T: AstNode>(&mut self, what: &T, with: &T) { | 278 | pub fn replace_ast<T: AstNode>(&mut self, what: &T, with: &T) { |
270 | self.replace(what.syntax(), with.syntax()) | 279 | self.replace(what.syntax(), with.syntax()) |
271 | } | 280 | } |
@@ -302,31 +311,41 @@ impl<'a> SyntaxRewriter<'a> { | |||
302 | 311 | ||
303 | fn rewrite_children(&self, node: &SyntaxNode) -> SyntaxNode { | 312 | fn rewrite_children(&self, node: &SyntaxNode) -> SyntaxNode { |
304 | // FIXME: this could be made much faster. | 313 | // FIXME: this could be made much faster. |
305 | let new_children = | 314 | let mut new_children = Vec::new(); |
306 | node.children_with_tokens().flat_map(|it| self.rewrite_self(&it)).collect::<Vec<_>>(); | 315 | for child in node.children_with_tokens() { |
316 | self.rewrite_self(&mut new_children, &child); | ||
317 | } | ||
307 | with_children(node, new_children) | 318 | with_children(node, new_children) |
308 | } | 319 | } |
309 | 320 | ||
310 | fn rewrite_self( | 321 | fn rewrite_self( |
311 | &self, | 322 | &self, |
323 | acc: &mut Vec<NodeOrToken<rowan::GreenNode, rowan::GreenToken>>, | ||
312 | element: &SyntaxElement, | 324 | element: &SyntaxElement, |
313 | ) -> Option<NodeOrToken<rowan::GreenNode, rowan::GreenToken>> { | 325 | ) { |
314 | if let Some(replacement) = self.replacement(&element) { | 326 | if let Some(replacement) = self.replacement(&element) { |
315 | return match replacement { | 327 | match replacement { |
316 | Replacement::Single(NodeOrToken::Node(it)) => { | 328 | Replacement::Single(NodeOrToken::Node(it)) => { |
317 | Some(NodeOrToken::Node(it.green().clone())) | 329 | acc.push(NodeOrToken::Node(it.green().clone())) |
318 | } | 330 | } |
319 | Replacement::Single(NodeOrToken::Token(it)) => { | 331 | Replacement::Single(NodeOrToken::Token(it)) => { |
320 | Some(NodeOrToken::Token(it.green().clone())) | 332 | acc.push(NodeOrToken::Token(it.green().clone())) |
321 | } | 333 | } |
322 | Replacement::Delete => None, | 334 | Replacement::Many(replacements) => { |
335 | acc.extend(replacements.iter().map(|it| match it { | ||
336 | NodeOrToken::Node(it) => NodeOrToken::Node(it.green().clone()), | ||
337 | NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()), | ||
338 | })) | ||
339 | } | ||
340 | Replacement::Delete => (), | ||
323 | }; | 341 | }; |
342 | return; | ||
324 | } | 343 | } |
325 | let res = match element { | 344 | let res = match element { |
326 | NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()), | 345 | NodeOrToken::Token(it) => NodeOrToken::Token(it.green().clone()), |
327 | NodeOrToken::Node(it) => NodeOrToken::Node(self.rewrite_children(it).green().clone()), | 346 | NodeOrToken::Node(it) => NodeOrToken::Node(self.rewrite_children(it).green().clone()), |
328 | }; | 347 | }; |
329 | Some(res) | 348 | acc.push(res) |
330 | } | 349 | } |
331 | } | 350 | } |
332 | 351 | ||
@@ -341,6 +360,7 @@ impl ops::AddAssign for SyntaxRewriter<'_> { | |||
341 | enum Replacement { | 360 | enum Replacement { |
342 | Delete, | 361 | Delete, |
343 | Single(SyntaxElement), | 362 | Single(SyntaxElement), |
363 | Many(Vec<SyntaxElement>), | ||
344 | } | 364 | } |
345 | 365 | ||
346 | fn with_children( | 366 | fn with_children( |
diff --git a/crates/ra_syntax/src/ast/edit.rs b/crates/ra_syntax/src/ast/edit.rs index 24a1e1d91..29eb3fcb9 100644 --- a/crates/ra_syntax/src/ast/edit.rs +++ b/crates/ra_syntax/src/ast/edit.rs | |||
@@ -1,7 +1,10 @@ | |||
1 | //! This module contains functions for editing syntax trees. As the trees are | 1 | //! This module contains functions for editing syntax trees. As the trees are |
2 | //! immutable, all function here return a fresh copy of the tree, instead of | 2 | //! immutable, all function here return a fresh copy of the tree, instead of |
3 | //! doing an in-place modification. | 3 | //! doing an in-place modification. |
4 | use std::{iter, ops::RangeInclusive}; | 4 | use std::{ |
5 | fmt, iter, | ||
6 | ops::{self, RangeInclusive}, | ||
7 | }; | ||
5 | 8 | ||
6 | use arrayvec::ArrayVec; | 9 | use arrayvec::ArrayVec; |
7 | 10 | ||
@@ -437,6 +440,28 @@ impl From<u8> for IndentLevel { | |||
437 | } | 440 | } |
438 | } | 441 | } |
439 | 442 | ||
443 | impl fmt::Display for IndentLevel { | ||
444 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
445 | let spaces = " "; | ||
446 | let buf; | ||
447 | let len = self.0 as usize * 4; | ||
448 | let indent = if len <= spaces.len() { | ||
449 | &spaces[..len] | ||
450 | } else { | ||
451 | buf = iter::repeat(' ').take(len).collect::<String>(); | ||
452 | &buf | ||
453 | }; | ||
454 | fmt::Display::fmt(indent, f) | ||
455 | } | ||
456 | } | ||
457 | |||
458 | impl ops::Add<u8> for IndentLevel { | ||
459 | type Output = IndentLevel; | ||
460 | fn add(self, rhs: u8) -> IndentLevel { | ||
461 | IndentLevel(self.0 + rhs) | ||
462 | } | ||
463 | } | ||
464 | |||
440 | impl IndentLevel { | 465 | impl IndentLevel { |
441 | pub fn from_node(node: &SyntaxNode) -> IndentLevel { | 466 | pub fn from_node(node: &SyntaxNode) -> IndentLevel { |
442 | let first_token = match node.first_token() { | 467 | let first_token = match node.first_token() { |
@@ -453,6 +478,14 @@ impl IndentLevel { | |||
453 | IndentLevel(0) | 478 | IndentLevel(0) |
454 | } | 479 | } |
455 | 480 | ||
481 | /// XXX: this intentionally doesn't change the indent of the very first token. | ||
482 | /// Ie, in something like | ||
483 | /// ``` | ||
484 | /// fn foo() { | ||
485 | /// 92 | ||
486 | /// } | ||
487 | /// ``` | ||
488 | /// if you indent the block, the `{` token would stay put. | ||
456 | fn increase_indent(self, node: SyntaxNode) -> SyntaxNode { | 489 | fn increase_indent(self, node: SyntaxNode) -> SyntaxNode { |
457 | let mut rewriter = SyntaxRewriter::default(); | 490 | let mut rewriter = SyntaxRewriter::default(); |
458 | node.descendants_with_tokens() | 491 | node.descendants_with_tokens() |
@@ -463,12 +496,7 @@ impl IndentLevel { | |||
463 | text.contains('\n') | 496 | text.contains('\n') |
464 | }) | 497 | }) |
465 | .for_each(|ws| { | 498 | .for_each(|ws| { |
466 | let new_ws = make::tokens::whitespace(&format!( | 499 | let new_ws = make::tokens::whitespace(&format!("{}{}", ws.syntax(), self,)); |
467 | "{}{:width$}", | ||
468 | ws.syntax().text(), | ||
469 | "", | ||
470 | width = self.0 as usize * 4 | ||
471 | )); | ||
472 | rewriter.replace(ws.syntax(), &new_ws) | 500 | rewriter.replace(ws.syntax(), &new_ws) |
473 | }); | 501 | }); |
474 | rewriter.rewrite(&node) | 502 | rewriter.rewrite(&node) |
@@ -485,7 +513,7 @@ impl IndentLevel { | |||
485 | }) | 513 | }) |
486 | .for_each(|ws| { | 514 | .for_each(|ws| { |
487 | let new_ws = make::tokens::whitespace( | 515 | let new_ws = make::tokens::whitespace( |
488 | &ws.syntax().text().replace(&format!("\n{:1$}", "", self.0 as usize * 4), "\n"), | 516 | &ws.syntax().text().replace(&format!("\n{}", self), "\n"), |
489 | ); | 517 | ); |
490 | rewriter.replace(ws.syntax(), &new_ws) | 518 | rewriter.replace(ws.syntax(), &new_ws) |
491 | }); | 519 | }); |
diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs index d0e960fb4..da0eb0926 100644 --- a/crates/ra_syntax/src/ast/make.rs +++ b/crates/ra_syntax/src/ast/make.rs | |||
@@ -1,5 +1,9 @@ | |||
1 | //! This module contains free-standing functions for creating AST fragments out | 1 | //! This module contains free-standing functions for creating AST fragments out |
2 | //! of smaller pieces. | 2 | //! of smaller pieces. |
3 | //! | ||
4 | //! Note that all functions here intended to be stupid constructors, which just | ||
5 | //! assemble a finish node from immediate children. If you want to do something | ||
6 | //! smarter than that, it probably doesn't belong in this module. | ||
3 | use itertools::Itertools; | 7 | use itertools::Itertools; |
4 | use stdx::format_to; | 8 | use stdx::format_to; |
5 | 9 | ||
@@ -95,6 +99,9 @@ pub fn expr_empty_block() -> ast::Expr { | |||
95 | pub fn expr_unimplemented() -> ast::Expr { | 99 | pub fn expr_unimplemented() -> ast::Expr { |
96 | expr_from_text("unimplemented!()") | 100 | expr_from_text("unimplemented!()") |
97 | } | 101 | } |
102 | pub fn expr_unreachable() -> ast::Expr { | ||
103 | expr_from_text("unreachable!()") | ||
104 | } | ||
98 | pub fn expr_todo() -> ast::Expr { | 105 | pub fn expr_todo() -> ast::Expr { |
99 | expr_from_text("todo!()") | 106 | expr_from_text("todo!()") |
100 | } | 107 | } |
@@ -264,10 +271,6 @@ pub fn token(kind: SyntaxKind) -> SyntaxToken { | |||
264 | .unwrap_or_else(|| panic!("unhandled token: {:?}", kind)) | 271 | .unwrap_or_else(|| panic!("unhandled token: {:?}", kind)) |
265 | } | 272 | } |
266 | 273 | ||
267 | pub fn unreachable_macro_call() -> ast::MacroCall { | ||
268 | ast_from_text(&format!("unreachable!()")) | ||
269 | } | ||
270 | |||
271 | pub fn param(name: String, ty: String) -> ast::Param { | 274 | pub fn param(name: String, ty: String) -> ast::Param { |
272 | ast_from_text(&format!("fn f({}: {}) {{ }}", name, ty)) | 275 | ast_from_text(&format!("fn f({}: {}) {{ }}", name, ty)) |
273 | } | 276 | } |
@@ -277,7 +280,12 @@ pub fn param_list(pats: impl IntoIterator<Item = ast::Param>) -> ast::ParamList | |||
277 | ast_from_text(&format!("fn f({}) {{ }}", args)) | 280 | ast_from_text(&format!("fn f({}) {{ }}", args)) |
278 | } | 281 | } |
279 | 282 | ||
283 | pub fn visibility_pub_crate() -> ast::Visibility { | ||
284 | ast_from_text("pub(crate) struct S") | ||
285 | } | ||
286 | |||
280 | pub fn fn_def( | 287 | pub fn fn_def( |
288 | visibility: Option<ast::Visibility>, | ||
281 | fn_name: ast::Name, | 289 | fn_name: ast::Name, |
282 | type_params: Option<ast::TypeParamList>, | 290 | type_params: Option<ast::TypeParamList>, |
283 | params: ast::ParamList, | 291 | params: ast::ParamList, |
@@ -285,21 +293,11 @@ pub fn fn_def( | |||
285 | ) -> ast::FnDef { | 293 | ) -> ast::FnDef { |
286 | let type_params = | 294 | let type_params = |
287 | if let Some(type_params) = type_params { format!("<{}>", type_params) } else { "".into() }; | 295 | if let Some(type_params) = type_params { format!("<{}>", type_params) } else { "".into() }; |
288 | ast_from_text(&format!("fn {}{}{} {}", fn_name, type_params, params, body)) | 296 | let visibility = match visibility { |
289 | } | 297 | None => String::new(), |
290 | 298 | Some(it) => format!("{} ", it), | |
291 | pub fn add_leading_newlines(amount_of_newlines: usize, t: impl AstNode) -> ast::SourceFile { | 299 | }; |
292 | let newlines = "\n".repeat(amount_of_newlines); | 300 | ast_from_text(&format!("{}fn {}{}{} {}", visibility, fn_name, type_params, params, body)) |
293 | ast_from_text(&format!("{}{}", newlines, t.syntax())) | ||
294 | } | ||
295 | |||
296 | pub fn add_trailing_newlines(amount_of_newlines: usize, t: impl AstNode) -> ast::SourceFile { | ||
297 | let newlines = "\n".repeat(amount_of_newlines); | ||
298 | ast_from_text(&format!("{}{}", t.syntax(), newlines)) | ||
299 | } | ||
300 | |||
301 | pub fn add_pub_crate_modifier(fn_def: ast::FnDef) -> ast::FnDef { | ||
302 | ast_from_text(&format!("pub(crate) {}", fn_def)) | ||
303 | } | 301 | } |
304 | 302 | ||
305 | fn ast_from_text<N: AstNode>(text: &str) -> N { | 303 | fn ast_from_text<N: AstNode>(text: &str) -> N { |
diff --git a/crates/ra_syntax/src/ast/tokens.rs b/crates/ra_syntax/src/ast/tokens.rs index 74906d8a6..3cd6d99c3 100644 --- a/crates/ra_syntax/src/ast/tokens.rs +++ b/crates/ra_syntax/src/ast/tokens.rs | |||
@@ -6,6 +6,7 @@ use crate::{ | |||
6 | ast::{AstToken, Comment, RawString, String, Whitespace}, | 6 | ast::{AstToken, Comment, RawString, String, Whitespace}, |
7 | TextRange, TextSize, | 7 | TextRange, TextSize, |
8 | }; | 8 | }; |
9 | use rustc_lexer::unescape::{unescape_literal, Mode}; | ||
9 | 10 | ||
10 | impl Comment { | 11 | impl Comment { |
11 | pub fn kind(&self) -> CommentKind { | 12 | pub fn kind(&self) -> CommentKind { |
@@ -147,7 +148,7 @@ impl HasStringValue for String { | |||
147 | 148 | ||
148 | let mut buf = std::string::String::with_capacity(text.len()); | 149 | let mut buf = std::string::String::with_capacity(text.len()); |
149 | let mut has_error = false; | 150 | let mut has_error = false; |
150 | rustc_lexer::unescape::unescape_str(text, &mut |_, unescaped_char| match unescaped_char { | 151 | unescape_literal(text, Mode::Str, &mut |_, unescaped_char| match unescaped_char { |
151 | Ok(c) => buf.push(c), | 152 | Ok(c) => buf.push(c), |
152 | Err(_) => has_error = true, | 153 | Err(_) => has_error = true, |
153 | }); | 154 | }); |
@@ -498,7 +499,7 @@ impl HasFormatSpecifier for String { | |||
498 | let offset = self.text_range_between_quotes()?.start() - self.syntax().text_range().start(); | 499 | let offset = self.text_range_between_quotes()?.start() - self.syntax().text_range().start(); |
499 | 500 | ||
500 | let mut res = Vec::with_capacity(text.len()); | 501 | let mut res = Vec::with_capacity(text.len()); |
501 | rustc_lexer::unescape::unescape_str(text, &mut |range, unescaped_char| { | 502 | unescape_literal(text, Mode::Str, &mut |range, unescaped_char| { |
502 | res.push(( | 503 | res.push(( |
503 | TextRange::new(range.start.try_into().unwrap(), range.end.try_into().unwrap()) | 504 | TextRange::new(range.start.try_into().unwrap(), range.end.try_into().unwrap()) |
504 | + offset, | 505 | + offset, |
diff --git a/crates/ra_syntax/src/validation.rs b/crates/ra_syntax/src/validation.rs index d68cf0a82..fdec48fb0 100644 --- a/crates/ra_syntax/src/validation.rs +++ b/crates/ra_syntax/src/validation.rs | |||
@@ -2,15 +2,15 @@ | |||
2 | 2 | ||
3 | mod block; | 3 | mod block; |
4 | 4 | ||
5 | use std::convert::TryFrom; | ||
6 | |||
7 | use rustc_lexer::unescape; | ||
8 | |||
9 | use crate::{ | 5 | use crate::{ |
10 | ast, match_ast, AstNode, SyntaxError, | 6 | ast, match_ast, AstNode, SyntaxError, |
11 | SyntaxKind::{BYTE, BYTE_STRING, CHAR, CONST_DEF, FN_DEF, INT_NUMBER, STRING, TYPE_ALIAS_DEF}, | 7 | SyntaxKind::{BYTE, BYTE_STRING, CHAR, CONST_DEF, FN_DEF, INT_NUMBER, STRING, TYPE_ALIAS_DEF}, |
12 | SyntaxNode, SyntaxToken, TextSize, T, | 8 | SyntaxNode, SyntaxToken, TextSize, T, |
13 | }; | 9 | }; |
10 | use rustc_lexer::unescape::{ | ||
11 | self, unescape_byte, unescape_byte_literal, unescape_char, unescape_literal, Mode, | ||
12 | }; | ||
13 | use std::convert::TryFrom; | ||
14 | 14 | ||
15 | fn rustc_unescape_error_to_string(err: unescape::EscapeError) -> &'static str { | 15 | fn rustc_unescape_error_to_string(err: unescape::EscapeError) -> &'static str { |
16 | use unescape::EscapeError as EE; | 16 | use unescape::EscapeError as EE; |
@@ -81,10 +81,8 @@ fn rustc_unescape_error_to_string(err: unescape::EscapeError) -> &'static str { | |||
81 | 81 | ||
82 | pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> { | 82 | pub(crate) fn validate(root: &SyntaxNode) -> Vec<SyntaxError> { |
83 | // FIXME: | 83 | // FIXME: |
84 | // * Add validation of character literal containing only a single char | 84 | // * Add unescape validation of raw string literals and raw byte string literals |
85 | // * Add validation of `crate` keyword not appearing in the middle of the symbol path | ||
86 | // * Add validation of doc comments are being attached to nodes | 85 | // * Add validation of doc comments are being attached to nodes |
87 | // * Remove validation of unterminated literals (it is already implemented in `tokenize()`) | ||
88 | 86 | ||
89 | let mut errors = Vec::new(); | 87 | let mut errors = Vec::new(); |
90 | for node in root.descendants() { | 88 | for node in root.descendants() { |
@@ -121,18 +119,18 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) { | |||
121 | 119 | ||
122 | match token.kind() { | 120 | match token.kind() { |
123 | BYTE => { | 121 | BYTE => { |
124 | if let Some(Err(e)) = unquote(text, 2, '\'').map(unescape::unescape_byte) { | 122 | if let Some(Err(e)) = unquote(text, 2, '\'').map(unescape_byte) { |
125 | push_err(2, e); | 123 | push_err(2, e); |
126 | } | 124 | } |
127 | } | 125 | } |
128 | CHAR => { | 126 | CHAR => { |
129 | if let Some(Err(e)) = unquote(text, 1, '\'').map(unescape::unescape_char) { | 127 | if let Some(Err(e)) = unquote(text, 1, '\'').map(unescape_char) { |
130 | push_err(1, e); | 128 | push_err(1, e); |
131 | } | 129 | } |
132 | } | 130 | } |
133 | BYTE_STRING => { | 131 | BYTE_STRING => { |
134 | if let Some(without_quotes) = unquote(text, 2, '"') { | 132 | if let Some(without_quotes) = unquote(text, 2, '"') { |
135 | unescape::unescape_byte_str(without_quotes, &mut |range, char| { | 133 | unescape_byte_literal(without_quotes, Mode::ByteStr, &mut |range, char| { |
136 | if let Err(err) = char { | 134 | if let Err(err) = char { |
137 | push_err(2, (range.start, err)); | 135 | push_err(2, (range.start, err)); |
138 | } | 136 | } |
@@ -141,7 +139,7 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) { | |||
141 | } | 139 | } |
142 | STRING => { | 140 | STRING => { |
143 | if let Some(without_quotes) = unquote(text, 1, '"') { | 141 | if let Some(without_quotes) = unquote(text, 1, '"') { |
144 | unescape::unescape_str(without_quotes, &mut |range, char| { | 142 | unescape_literal(without_quotes, Mode::Str, &mut |range, char| { |
145 | if let Err(err) = char { | 143 | if let Err(err) = char { |
146 | push_err(1, (range.start, err)); | 144 | push_err(1, (range.start, err)); |
147 | } | 145 | } |