diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/assists/src/handlers/change_return_type_to_result.rs | 121 | ||||
-rw-r--r-- | crates/assists/src/handlers/convert_integer_literal.rs | 23 | ||||
-rw-r--r-- | crates/project_model/src/sysroot.rs | 8 | ||||
-rw-r--r-- | crates/syntax/src/ast.rs | 2 | ||||
-rw-r--r-- | crates/syntax/src/ast/expr_ext.rs | 87 | ||||
-rw-r--r-- | crates/syntax/src/ast/generated/tokens.rs | 42 | ||||
-rw-r--r-- | crates/syntax/src/ast/token_ext.rs | 97 | ||||
-rw-r--r-- | crates/syntax/src/parsing/lexer.rs | 114 |
8 files changed, 326 insertions, 168 deletions
diff --git a/crates/assists/src/handlers/change_return_type_to_result.rs b/crates/assists/src/handlers/change_return_type_to_result.rs index be480943c..76f33a5b6 100644 --- a/crates/assists/src/handlers/change_return_type_to_result.rs +++ b/crates/assists/src/handlers/change_return_type_to_result.rs | |||
@@ -2,7 +2,7 @@ use std::iter; | |||
2 | 2 | ||
3 | use syntax::{ | 3 | use syntax::{ |
4 | ast::{self, make, BlockExpr, Expr, LoopBodyOwner}, | 4 | ast::{self, make, BlockExpr, Expr, LoopBodyOwner}, |
5 | AstNode, SyntaxNode, | 5 | match_ast, AstNode, SyntaxNode, |
6 | }; | 6 | }; |
7 | use test_utils::mark; | 7 | use test_utils::mark; |
8 | 8 | ||
@@ -21,8 +21,18 @@ use crate::{AssistContext, AssistId, AssistKind, Assists}; | |||
21 | // ``` | 21 | // ``` |
22 | pub(crate) fn change_return_type_to_result(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 22 | pub(crate) fn change_return_type_to_result(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
23 | let ret_type = ctx.find_node_at_offset::<ast::RetType>()?; | 23 | let ret_type = ctx.find_node_at_offset::<ast::RetType>()?; |
24 | // FIXME: extend to lambdas as well | 24 | let parent = ret_type.syntax().parent()?; |
25 | let fn_def = ret_type.syntax().parent().and_then(ast::Fn::cast)?; | 25 | let block_expr = match_ast! { |
26 | match parent { | ||
27 | ast::Fn(func) => func.body()?, | ||
28 | ast::ClosureExpr(closure) => match closure.body()? { | ||
29 | Expr::BlockExpr(block) => block, | ||
30 | // closures require a block when a return type is specified | ||
31 | _ => return None, | ||
32 | }, | ||
33 | _ => return None, | ||
34 | } | ||
35 | }; | ||
26 | 36 | ||
27 | let type_ref = &ret_type.ty()?; | 37 | let type_ref = &ret_type.ty()?; |
28 | let ret_type_str = type_ref.syntax().text().to_string(); | 38 | let ret_type_str = type_ref.syntax().text().to_string(); |
@@ -34,16 +44,14 @@ pub(crate) fn change_return_type_to_result(acc: &mut Assists, ctx: &AssistContex | |||
34 | } | 44 | } |
35 | } | 45 | } |
36 | 46 | ||
37 | let block_expr = &fn_def.body()?; | ||
38 | |||
39 | acc.add( | 47 | acc.add( |
40 | AssistId("change_return_type_to_result", AssistKind::RefactorRewrite), | 48 | AssistId("change_return_type_to_result", AssistKind::RefactorRewrite), |
41 | "Wrap return type in Result", | 49 | "Wrap return type in Result", |
42 | type_ref.syntax().text_range(), | 50 | type_ref.syntax().text_range(), |
43 | |builder| { | 51 | |builder| { |
44 | let mut tail_return_expr_collector = TailReturnCollector::new(); | 52 | let mut tail_return_expr_collector = TailReturnCollector::new(); |
45 | tail_return_expr_collector.collect_jump_exprs(block_expr, false); | 53 | tail_return_expr_collector.collect_jump_exprs(&block_expr, false); |
46 | tail_return_expr_collector.collect_tail_exprs(block_expr); | 54 | tail_return_expr_collector.collect_tail_exprs(&block_expr); |
47 | 55 | ||
48 | for ret_expr_arg in tail_return_expr_collector.exprs_to_wrap { | 56 | for ret_expr_arg in tail_return_expr_collector.exprs_to_wrap { |
49 | let ok_wrapped = make::expr_call( | 57 | let ok_wrapped = make::expr_call( |
@@ -285,16 +293,20 @@ mod tests { | |||
285 | } | 293 | } |
286 | 294 | ||
287 | #[test] | 295 | #[test] |
288 | fn change_return_type_to_result_simple_return_type() { | 296 | fn change_return_type_to_result_simple_closure() { |
289 | check_assist( | 297 | check_assist( |
290 | change_return_type_to_result, | 298 | change_return_type_to_result, |
291 | r#"fn foo() -> i32<|> { | 299 | r#"fn foo() { |
292 | let test = "test"; | 300 | || -> i32<|> { |
293 | return 42i32; | 301 | let test = "test"; |
302 | return 42i32; | ||
303 | }; | ||
294 | }"#, | 304 | }"#, |
295 | r#"fn foo() -> Result<i32, ${0:_}> { | 305 | r#"fn foo() { |
296 | let test = "test"; | 306 | || -> Result<i32, ${0:_}> { |
297 | return Ok(42i32); | 307 | let test = "test"; |
308 | return Ok(42i32); | ||
309 | }; | ||
298 | }"#, | 310 | }"#, |
299 | ); | 311 | ); |
300 | } | 312 | } |
@@ -311,6 +323,29 @@ mod tests { | |||
311 | } | 323 | } |
312 | 324 | ||
313 | #[test] | 325 | #[test] |
326 | fn change_return_type_to_result_simple_return_type_bad_cursor_closure() { | ||
327 | check_assist_not_applicable( | ||
328 | change_return_type_to_result, | ||
329 | r#"fn foo() { | ||
330 | || -> i32 { | ||
331 | let test = "test";<|> | ||
332 | return 42i32; | ||
333 | }; | ||
334 | }"#, | ||
335 | ); | ||
336 | } | ||
337 | |||
338 | #[test] | ||
339 | fn change_return_type_to_result_closure_non_block() { | ||
340 | check_assist_not_applicable( | ||
341 | change_return_type_to_result, | ||
342 | r#"fn foo() { | ||
343 | || -> i<|>32 3; | ||
344 | }"#, | ||
345 | ); | ||
346 | } | ||
347 | |||
348 | #[test] | ||
314 | fn change_return_type_to_result_simple_return_type_already_result_std() { | 349 | fn change_return_type_to_result_simple_return_type_already_result_std() { |
315 | check_assist_not_applicable( | 350 | check_assist_not_applicable( |
316 | change_return_type_to_result, | 351 | change_return_type_to_result, |
@@ -334,6 +369,19 @@ mod tests { | |||
334 | } | 369 | } |
335 | 370 | ||
336 | #[test] | 371 | #[test] |
372 | fn change_return_type_to_result_simple_return_type_already_result_closure() { | ||
373 | check_assist_not_applicable( | ||
374 | change_return_type_to_result, | ||
375 | r#"fn foo() { | ||
376 | || -> Result<i32<|>, String> { | ||
377 | let test = "test"; | ||
378 | return 42i32; | ||
379 | }; | ||
380 | }"#, | ||
381 | ); | ||
382 | } | ||
383 | |||
384 | #[test] | ||
337 | fn change_return_type_to_result_simple_with_cursor() { | 385 | fn change_return_type_to_result_simple_with_cursor() { |
338 | check_assist( | 386 | check_assist( |
339 | change_return_type_to_result, | 387 | change_return_type_to_result, |
@@ -364,6 +412,25 @@ mod tests { | |||
364 | } | 412 | } |
365 | 413 | ||
366 | #[test] | 414 | #[test] |
415 | fn change_return_type_to_result_simple_with_tail_closure() { | ||
416 | check_assist( | ||
417 | change_return_type_to_result, | ||
418 | r#"fn foo() { | ||
419 | || -><|> i32 { | ||
420 | let test = "test"; | ||
421 | 42i32 | ||
422 | }; | ||
423 | }"#, | ||
424 | r#"fn foo() { | ||
425 | || -> Result<i32, ${0:_}> { | ||
426 | let test = "test"; | ||
427 | Ok(42i32) | ||
428 | }; | ||
429 | }"#, | ||
430 | ); | ||
431 | } | ||
432 | |||
433 | #[test] | ||
367 | fn change_return_type_to_result_simple_with_tail_only() { | 434 | fn change_return_type_to_result_simple_with_tail_only() { |
368 | check_assist( | 435 | check_assist( |
369 | change_return_type_to_result, | 436 | change_return_type_to_result, |
@@ -375,6 +442,7 @@ mod tests { | |||
375 | }"#, | 442 | }"#, |
376 | ); | 443 | ); |
377 | } | 444 | } |
445 | |||
378 | #[test] | 446 | #[test] |
379 | fn change_return_type_to_result_simple_with_tail_block_like() { | 447 | fn change_return_type_to_result_simple_with_tail_block_like() { |
380 | check_assist( | 448 | check_assist( |
@@ -397,6 +465,31 @@ mod tests { | |||
397 | } | 465 | } |
398 | 466 | ||
399 | #[test] | 467 | #[test] |
468 | fn change_return_type_to_result_simple_without_block_closure() { | ||
469 | check_assist( | ||
470 | change_return_type_to_result, | ||
471 | r#"fn foo() { | ||
472 | || -> i32<|> { | ||
473 | if true { | ||
474 | 42i32 | ||
475 | } else { | ||
476 | 24i32 | ||
477 | } | ||
478 | }; | ||
479 | }"#, | ||
480 | r#"fn foo() { | ||
481 | || -> Result<i32, ${0:_}> { | ||
482 | if true { | ||
483 | Ok(42i32) | ||
484 | } else { | ||
485 | Ok(24i32) | ||
486 | } | ||
487 | }; | ||
488 | }"#, | ||
489 | ); | ||
490 | } | ||
491 | |||
492 | #[test] | ||
400 | fn change_return_type_to_result_simple_with_nested_if() { | 493 | fn change_return_type_to_result_simple_with_nested_if() { |
401 | check_assist( | 494 | check_assist( |
402 | change_return_type_to_result, | 495 | change_return_type_to_result, |
diff --git a/crates/assists/src/handlers/convert_integer_literal.rs b/crates/assists/src/handlers/convert_integer_literal.rs index c8af80701..5957834d3 100644 --- a/crates/assists/src/handlers/convert_integer_literal.rs +++ b/crates/assists/src/handlers/convert_integer_literal.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use syntax::{ast, ast::Radix, AstNode}; | 1 | use syntax::{ast, ast::Radix, AstToken}; |
2 | 2 | ||
3 | use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel}; | 3 | use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel}; |
4 | 4 | ||
@@ -14,15 +14,13 @@ use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel}; | |||
14 | // const _: i32 = 0b1010; | 14 | // const _: i32 = 0b1010; |
15 | // ``` | 15 | // ``` |
16 | pub(crate) fn convert_integer_literal(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 16 | pub(crate) fn convert_integer_literal(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { |
17 | let literal = ctx.find_node_at_offset::<ast::Literal>()?; | 17 | let literal = ctx.find_node_at_offset::<ast::Literal>()?.as_int_number()?; |
18 | let (radix, value) = literal.int_value()?; | 18 | let radix = literal.radix(); |
19 | let value = literal.value()?; | ||
20 | let suffix = literal.suffix(); | ||
19 | 21 | ||
20 | let range = literal.syntax().text_range(); | 22 | let range = literal.syntax().text_range(); |
21 | let group_id = GroupLabel("Convert integer base".into()); | 23 | let group_id = GroupLabel("Convert integer base".into()); |
22 | let suffix = match literal.kind() { | ||
23 | ast::LiteralKind::IntNumber { suffix } => suffix, | ||
24 | _ => return None, | ||
25 | }; | ||
26 | 24 | ||
27 | for &target_radix in Radix::ALL { | 25 | for &target_radix in Radix::ALL { |
28 | if target_radix == radix { | 26 | if target_radix == radix { |
@@ -36,16 +34,11 @@ pub(crate) fn convert_integer_literal(acc: &mut Assists, ctx: &AssistContext) -> | |||
36 | Radix::Hexadecimal => format!("0x{:X}", value), | 34 | Radix::Hexadecimal => format!("0x{:X}", value), |
37 | }; | 35 | }; |
38 | 36 | ||
39 | let label = format!( | 37 | let label = format!("Convert {} to {}{}", literal, converted, suffix.unwrap_or_default()); |
40 | "Convert {} to {}{}", | ||
41 | literal, | ||
42 | converted, | ||
43 | suffix.as_deref().unwrap_or_default() | ||
44 | ); | ||
45 | 38 | ||
46 | // Appends the type suffix back into the new literal if it exists. | 39 | // Appends the type suffix back into the new literal if it exists. |
47 | if let Some(suffix) = &suffix { | 40 | if let Some(suffix) = suffix { |
48 | converted.push_str(&suffix); | 41 | converted.push_str(suffix); |
49 | } | 42 | } |
50 | 43 | ||
51 | acc.add_group( | 44 | acc.add_group( |
diff --git a/crates/project_model/src/sysroot.rs b/crates/project_model/src/sysroot.rs index 3fe494729..b0e8863f6 100644 --- a/crates/project_model/src/sysroot.rs +++ b/crates/project_model/src/sysroot.rs | |||
@@ -114,8 +114,12 @@ fn discover_sysroot_src_dir(current_dir: &AbsPath) -> Result<AbsPathBuf> { | |||
114 | if let Ok(path) = env::var("RUST_SRC_PATH") { | 114 | if let Ok(path) = env::var("RUST_SRC_PATH") { |
115 | let path = AbsPathBuf::try_from(path.as_str()) | 115 | let path = AbsPathBuf::try_from(path.as_str()) |
116 | .map_err(|path| format_err!("RUST_SRC_PATH must be absolute: {}", path.display()))?; | 116 | .map_err(|path| format_err!("RUST_SRC_PATH must be absolute: {}", path.display()))?; |
117 | log::debug!("Discovered sysroot by RUST_SRC_PATH: {}", path.display()); | 117 | let core = path.join("core"); |
118 | return Ok(path); | 118 | if core.exists() { |
119 | log::debug!("Discovered sysroot by RUST_SRC_PATH: {}", path.display()); | ||
120 | return Ok(path); | ||
121 | } | ||
122 | log::debug!("RUST_SRC_PATH is set, but is invalid (no core: {:?}), ignoring", core); | ||
119 | } | 123 | } |
120 | 124 | ||
121 | let sysroot_path = { | 125 | let sysroot_path = { |
diff --git a/crates/syntax/src/ast.rs b/crates/syntax/src/ast.rs index a16ac6a7c..8a0e3d27b 100644 --- a/crates/syntax/src/ast.rs +++ b/crates/syntax/src/ast.rs | |||
@@ -16,7 +16,7 @@ use crate::{ | |||
16 | }; | 16 | }; |
17 | 17 | ||
18 | pub use self::{ | 18 | pub use self::{ |
19 | expr_ext::{ArrayExprKind, BinOp, Effect, ElseBranch, LiteralKind, PrefixOp, Radix, RangeOp}, | 19 | expr_ext::{ArrayExprKind, BinOp, Effect, ElseBranch, LiteralKind, PrefixOp, RangeOp}, |
20 | generated::{nodes::*, tokens::*}, | 20 | generated::{nodes::*, tokens::*}, |
21 | node_ext::{ | 21 | node_ext::{ |
22 | AttrKind, FieldKind, NameOrNameRef, PathSegmentKind, SelfParamKind, SlicePatComponents, | 22 | AttrKind, FieldKind, NameOrNameRef, PathSegmentKind, SelfParamKind, SlicePatComponents, |
diff --git a/crates/syntax/src/ast/expr_ext.rs b/crates/syntax/src/ast/expr_ext.rs index 3aff01e83..3d33cd1cf 100644 --- a/crates/syntax/src/ast/expr_ext.rs +++ b/crates/syntax/src/ast/expr_ext.rs | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | use crate::{ | 3 | use crate::{ |
4 | ast::{self, support, AstChildren, AstNode}, | 4 | ast::{self, support, AstChildren, AstNode}, |
5 | SmolStr, | 5 | AstToken, SmolStr, |
6 | SyntaxKind::*, | 6 | SyntaxKind::*, |
7 | SyntaxToken, T, | 7 | SyntaxToken, T, |
8 | }; | 8 | }; |
@@ -316,6 +316,10 @@ impl ast::Literal { | |||
316 | .unwrap() | 316 | .unwrap() |
317 | } | 317 | } |
318 | 318 | ||
319 | pub fn as_int_number(&self) -> Option<ast::IntNumber> { | ||
320 | ast::IntNumber::cast(self.token()) | ||
321 | } | ||
322 | |||
319 | fn find_suffix(text: &str, possible_suffixes: &[&str]) -> Option<SmolStr> { | 323 | fn find_suffix(text: &str, possible_suffixes: &[&str]) -> Option<SmolStr> { |
320 | possible_suffixes | 324 | possible_suffixes |
321 | .iter() | 325 | .iter() |
@@ -324,11 +328,6 @@ impl ast::Literal { | |||
324 | } | 328 | } |
325 | 329 | ||
326 | pub fn kind(&self) -> LiteralKind { | 330 | pub fn kind(&self) -> LiteralKind { |
327 | const INT_SUFFIXES: [&str; 12] = [ | ||
328 | "u64", "u32", "u16", "u8", "usize", "isize", "i64", "i32", "i16", "i8", "u128", "i128", | ||
329 | ]; | ||
330 | const FLOAT_SUFFIXES: [&str; 2] = ["f32", "f64"]; | ||
331 | |||
332 | let token = self.token(); | 331 | let token = self.token(); |
333 | 332 | ||
334 | match token.kind() { | 333 | match token.kind() { |
@@ -337,17 +336,20 @@ impl ast::Literal { | |||
337 | // The lexer treats e.g. `1f64` as an integer literal. See | 336 | // The lexer treats e.g. `1f64` as an integer literal. See |
338 | // https://github.com/rust-analyzer/rust-analyzer/issues/1592 | 337 | // https://github.com/rust-analyzer/rust-analyzer/issues/1592 |
339 | // and the comments on the linked PR. | 338 | // and the comments on the linked PR. |
340 | |||
341 | let text = token.text(); | 339 | let text = token.text(); |
342 | if let suffix @ Some(_) = Self::find_suffix(&text, &FLOAT_SUFFIXES) { | 340 | if let suffix @ Some(_) = Self::find_suffix(&text, &ast::FloatNumber::SUFFIXES) { |
343 | LiteralKind::FloatNumber { suffix } | 341 | LiteralKind::FloatNumber { suffix } |
344 | } else { | 342 | } else { |
345 | LiteralKind::IntNumber { suffix: Self::find_suffix(&text, &INT_SUFFIXES) } | 343 | LiteralKind::IntNumber { |
344 | suffix: Self::find_suffix(&text, &ast::IntNumber::SUFFIXES), | ||
345 | } | ||
346 | } | 346 | } |
347 | } | 347 | } |
348 | FLOAT_NUMBER => { | 348 | FLOAT_NUMBER => { |
349 | let text = token.text(); | 349 | let text = token.text(); |
350 | LiteralKind::FloatNumber { suffix: Self::find_suffix(&text, &FLOAT_SUFFIXES) } | 350 | LiteralKind::FloatNumber { |
351 | suffix: Self::find_suffix(&text, &ast::FloatNumber::SUFFIXES), | ||
352 | } | ||
351 | } | 353 | } |
352 | STRING | RAW_STRING => LiteralKind::String, | 354 | STRING | RAW_STRING => LiteralKind::String, |
353 | T![true] => LiteralKind::Bool(true), | 355 | T![true] => LiteralKind::Bool(true), |
@@ -358,71 +360,6 @@ impl ast::Literal { | |||
358 | _ => unreachable!(), | 360 | _ => unreachable!(), |
359 | } | 361 | } |
360 | } | 362 | } |
361 | |||
362 | // FIXME: should probably introduce string token type? | ||
363 | // https://github.com/rust-analyzer/rust-analyzer/issues/6308 | ||
364 | pub fn int_value(&self) -> Option<(Radix, u128)> { | ||
365 | let suffix = match self.kind() { | ||
366 | LiteralKind::IntNumber { suffix } => suffix, | ||
367 | _ => return None, | ||
368 | }; | ||
369 | |||
370 | let token = self.token(); | ||
371 | let mut text = token.text().as_str(); | ||
372 | text = &text[..text.len() - suffix.map_or(0, |it| it.len())]; | ||
373 | |||
374 | let buf; | ||
375 | if text.contains("_") { | ||
376 | buf = text.replace('_', ""); | ||
377 | text = buf.as_str(); | ||
378 | }; | ||
379 | |||
380 | let radix = Radix::identify(text)?; | ||
381 | let digits = &text[radix.prefix_len()..]; | ||
382 | let value = u128::from_str_radix(digits, radix as u32).ok()?; | ||
383 | Some((radix, value)) | ||
384 | } | ||
385 | } | ||
386 | |||
387 | #[derive(Debug, PartialEq, Eq, Copy, Clone)] | ||
388 | pub enum Radix { | ||
389 | Binary = 2, | ||
390 | Octal = 8, | ||
391 | Decimal = 10, | ||
392 | Hexadecimal = 16, | ||
393 | } | ||
394 | |||
395 | impl Radix { | ||
396 | pub const ALL: &'static [Radix] = | ||
397 | &[Radix::Binary, Radix::Octal, Radix::Decimal, Radix::Hexadecimal]; | ||
398 | |||
399 | fn identify(literal_text: &str) -> Option<Self> { | ||
400 | // We cannot express a literal in anything other than decimal in under 3 characters, so we return here if possible. | ||
401 | if literal_text.len() < 3 && literal_text.chars().all(|c| c.is_digit(10)) { | ||
402 | return Some(Self::Decimal); | ||
403 | } | ||
404 | |||
405 | let res = match &literal_text[..2] { | ||
406 | "0b" => Radix::Binary, | ||
407 | "0o" => Radix::Octal, | ||
408 | "0x" => Radix::Hexadecimal, | ||
409 | _ => Radix::Decimal, | ||
410 | }; | ||
411 | |||
412 | // Checks that all characters after the base prefix are all valid digits for that base. | ||
413 | if literal_text[res.prefix_len()..].chars().all(|c| c.is_digit(res as u32)) { | ||
414 | Some(res) | ||
415 | } else { | ||
416 | None | ||
417 | } | ||
418 | } | ||
419 | |||
420 | const fn prefix_len(&self) -> usize { | ||
421 | match self { | ||
422 | Self::Decimal => 0, | ||
423 | _ => 2, | ||
424 | } | ||
425 | } | ||
426 | } | 363 | } |
427 | 364 | ||
428 | #[derive(Debug, Clone, PartialEq, Eq)] | 365 | #[derive(Debug, Clone, PartialEq, Eq)] |
diff --git a/crates/syntax/src/ast/generated/tokens.rs b/crates/syntax/src/ast/generated/tokens.rs index abadd0b61..1b8449221 100644 --- a/crates/syntax/src/ast/generated/tokens.rs +++ b/crates/syntax/src/ast/generated/tokens.rs | |||
@@ -89,3 +89,45 @@ impl AstToken for RawString { | |||
89 | } | 89 | } |
90 | fn syntax(&self) -> &SyntaxToken { &self.syntax } | 90 | fn syntax(&self) -> &SyntaxToken { &self.syntax } |
91 | } | 91 | } |
92 | |||
93 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
94 | pub struct IntNumber { | ||
95 | pub(crate) syntax: SyntaxToken, | ||
96 | } | ||
97 | impl std::fmt::Display for IntNumber { | ||
98 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
99 | std::fmt::Display::fmt(&self.syntax, f) | ||
100 | } | ||
101 | } | ||
102 | impl AstToken for IntNumber { | ||
103 | fn can_cast(kind: SyntaxKind) -> bool { kind == INT_NUMBER } | ||
104 | fn cast(syntax: SyntaxToken) -> Option<Self> { | ||
105 | if Self::can_cast(syntax.kind()) { | ||
106 | Some(Self { syntax }) | ||
107 | } else { | ||
108 | None | ||
109 | } | ||
110 | } | ||
111 | fn syntax(&self) -> &SyntaxToken { &self.syntax } | ||
112 | } | ||
113 | |||
114 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
115 | pub struct FloatNumber { | ||
116 | pub(crate) syntax: SyntaxToken, | ||
117 | } | ||
118 | impl std::fmt::Display for FloatNumber { | ||
119 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
120 | std::fmt::Display::fmt(&self.syntax, f) | ||
121 | } | ||
122 | } | ||
123 | impl AstToken for FloatNumber { | ||
124 | fn can_cast(kind: SyntaxKind) -> bool { kind == FLOAT_NUMBER } | ||
125 | fn cast(syntax: SyntaxToken) -> Option<Self> { | ||
126 | if Self::can_cast(syntax.kind()) { | ||
127 | Some(Self { syntax }) | ||
128 | } else { | ||
129 | None | ||
130 | } | ||
131 | } | ||
132 | fn syntax(&self) -> &SyntaxToken { &self.syntax } | ||
133 | } | ||
diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs index c5ef92733..8d3fad5a6 100644 --- a/crates/syntax/src/ast/token_ext.rs +++ b/crates/syntax/src/ast/token_ext.rs | |||
@@ -8,11 +8,11 @@ use std::{ | |||
8 | use rustc_lexer::unescape::{unescape_literal, Mode}; | 8 | use rustc_lexer::unescape::{unescape_literal, Mode}; |
9 | 9 | ||
10 | use crate::{ | 10 | use crate::{ |
11 | ast::{AstToken, Comment, RawString, String, Whitespace}, | 11 | ast::{self, AstToken}, |
12 | TextRange, TextSize, | 12 | TextRange, TextSize, |
13 | }; | 13 | }; |
14 | 14 | ||
15 | impl Comment { | 15 | impl ast::Comment { |
16 | pub fn kind(&self) -> CommentKind { | 16 | pub fn kind(&self) -> CommentKind { |
17 | kind_by_prefix(self.text()) | 17 | kind_by_prefix(self.text()) |
18 | } | 18 | } |
@@ -80,7 +80,7 @@ fn kind_by_prefix(text: &str) -> CommentKind { | |||
80 | panic!("bad comment text: {:?}", text) | 80 | panic!("bad comment text: {:?}", text) |
81 | } | 81 | } |
82 | 82 | ||
83 | impl Whitespace { | 83 | impl ast::Whitespace { |
84 | pub fn spans_multiple_lines(&self) -> bool { | 84 | pub fn spans_multiple_lines(&self) -> bool { |
85 | let text = self.text(); | 85 | let text = self.text(); |
86 | text.find('\n').map_or(false, |idx| text[idx + 1..].contains('\n')) | 86 | text.find('\n').map_or(false, |idx| text[idx + 1..].contains('\n')) |
@@ -138,19 +138,19 @@ pub trait HasQuotes: AstToken { | |||
138 | } | 138 | } |
139 | } | 139 | } |
140 | 140 | ||
141 | impl HasQuotes for String {} | 141 | impl HasQuotes for ast::String {} |
142 | impl HasQuotes for RawString {} | 142 | impl HasQuotes for ast::RawString {} |
143 | 143 | ||
144 | pub trait HasStringValue: HasQuotes { | 144 | pub trait HasStringValue: HasQuotes { |
145 | fn value(&self) -> Option<Cow<'_, str>>; | 145 | fn value(&self) -> Option<Cow<'_, str>>; |
146 | } | 146 | } |
147 | 147 | ||
148 | impl HasStringValue for String { | 148 | impl HasStringValue for ast::String { |
149 | fn value(&self) -> Option<Cow<'_, str>> { | 149 | fn value(&self) -> Option<Cow<'_, str>> { |
150 | let text = self.text().as_str(); | 150 | let text = self.text().as_str(); |
151 | let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; | 151 | let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; |
152 | 152 | ||
153 | let mut buf = std::string::String::with_capacity(text.len()); | 153 | let mut buf = String::with_capacity(text.len()); |
154 | let mut has_error = false; | 154 | let mut has_error = false; |
155 | unescape_literal(text, Mode::Str, &mut |_, unescaped_char| match unescaped_char { | 155 | unescape_literal(text, Mode::Str, &mut |_, unescaped_char| match unescaped_char { |
156 | Ok(c) => buf.push(c), | 156 | Ok(c) => buf.push(c), |
@@ -166,7 +166,8 @@ impl HasStringValue for String { | |||
166 | } | 166 | } |
167 | } | 167 | } |
168 | 168 | ||
169 | impl HasStringValue for RawString { | 169 | // FIXME: merge `ast::RawString` and `ast::String`. |
170 | impl HasStringValue for ast::RawString { | ||
170 | fn value(&self) -> Option<Cow<'_, str>> { | 171 | fn value(&self) -> Option<Cow<'_, str>> { |
171 | let text = self.text().as_str(); | 172 | let text = self.text().as_str(); |
172 | let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; | 173 | let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; |
@@ -174,7 +175,7 @@ impl HasStringValue for RawString { | |||
174 | } | 175 | } |
175 | } | 176 | } |
176 | 177 | ||
177 | impl RawString { | 178 | impl ast::RawString { |
178 | pub fn map_range_up(&self, range: TextRange) -> Option<TextRange> { | 179 | pub fn map_range_up(&self, range: TextRange) -> Option<TextRange> { |
179 | let contents_range = self.text_range_between_quotes()?; | 180 | let contents_range = self.text_range_between_quotes()?; |
180 | assert!(TextRange::up_to(contents_range.len()).contains_range(range)); | 181 | assert!(TextRange::up_to(contents_range.len()).contains_range(range)); |
@@ -500,7 +501,7 @@ pub trait HasFormatSpecifier: AstToken { | |||
500 | } | 501 | } |
501 | } | 502 | } |
502 | 503 | ||
503 | impl HasFormatSpecifier for String { | 504 | impl HasFormatSpecifier for ast::String { |
504 | fn char_ranges( | 505 | fn char_ranges( |
505 | &self, | 506 | &self, |
506 | ) -> Option<Vec<(TextRange, Result<char, rustc_lexer::unescape::EscapeError>)>> { | 507 | ) -> Option<Vec<(TextRange, Result<char, rustc_lexer::unescape::EscapeError>)>> { |
@@ -521,7 +522,7 @@ impl HasFormatSpecifier for String { | |||
521 | } | 522 | } |
522 | } | 523 | } |
523 | 524 | ||
524 | impl HasFormatSpecifier for RawString { | 525 | impl HasFormatSpecifier for ast::RawString { |
525 | fn char_ranges( | 526 | fn char_ranges( |
526 | &self, | 527 | &self, |
527 | ) -> Option<Vec<(TextRange, Result<char, rustc_lexer::unescape::EscapeError>)>> { | 528 | ) -> Option<Vec<(TextRange, Result<char, rustc_lexer::unescape::EscapeError>)>> { |
@@ -536,3 +537,77 @@ impl HasFormatSpecifier for RawString { | |||
536 | Some(res) | 537 | Some(res) |
537 | } | 538 | } |
538 | } | 539 | } |
540 | |||
541 | impl ast::IntNumber { | ||
542 | #[rustfmt::skip] | ||
543 | pub(crate) const SUFFIXES: &'static [&'static str] = &[ | ||
544 | "u8", "u16", "u32", "u64", "u128", "usize", | ||
545 | "i8", "i16", "i32", "i64", "i128", "isize", | ||
546 | ]; | ||
547 | |||
548 | pub fn radix(&self) -> Radix { | ||
549 | match self.text().get(..2).unwrap_or_default() { | ||
550 | "0b" => Radix::Binary, | ||
551 | "0o" => Radix::Octal, | ||
552 | "0x" => Radix::Hexadecimal, | ||
553 | _ => Radix::Decimal, | ||
554 | } | ||
555 | } | ||
556 | |||
557 | pub fn value(&self) -> Option<u128> { | ||
558 | let token = self.syntax(); | ||
559 | |||
560 | let mut text = token.text().as_str(); | ||
561 | if let Some(suffix) = self.suffix() { | ||
562 | text = &text[..text.len() - suffix.len()] | ||
563 | } | ||
564 | |||
565 | let radix = self.radix(); | ||
566 | text = &text[radix.prefix_len()..]; | ||
567 | |||
568 | let buf; | ||
569 | if text.contains("_") { | ||
570 | buf = text.replace('_', ""); | ||
571 | text = buf.as_str(); | ||
572 | }; | ||
573 | |||
574 | let value = u128::from_str_radix(text, radix as u32).ok()?; | ||
575 | Some(value) | ||
576 | } | ||
577 | |||
578 | pub fn suffix(&self) -> Option<&str> { | ||
579 | let text = self.text(); | ||
580 | // FIXME: don't check a fixed set of suffixes, `1_0_1___lol` is valid | ||
581 | // syntax, suffix is `lol`. | ||
582 | ast::IntNumber::SUFFIXES.iter().find_map(|suffix| { | ||
583 | if text.ends_with(suffix) { | ||
584 | return Some(&text[text.len() - suffix.len()..]); | ||
585 | } | ||
586 | None | ||
587 | }) | ||
588 | } | ||
589 | } | ||
590 | |||
591 | impl ast::FloatNumber { | ||
592 | pub(crate) const SUFFIXES: &'static [&'static str] = &["f32", "f64"]; | ||
593 | } | ||
594 | |||
595 | #[derive(Debug, PartialEq, Eq, Copy, Clone)] | ||
596 | pub enum Radix { | ||
597 | Binary = 2, | ||
598 | Octal = 8, | ||
599 | Decimal = 10, | ||
600 | Hexadecimal = 16, | ||
601 | } | ||
602 | |||
603 | impl Radix { | ||
604 | pub const ALL: &'static [Radix] = | ||
605 | &[Radix::Binary, Radix::Octal, Radix::Decimal, Radix::Hexadecimal]; | ||
606 | |||
607 | const fn prefix_len(&self) -> usize { | ||
608 | match self { | ||
609 | Self::Decimal => 0, | ||
610 | _ => 2, | ||
611 | } | ||
612 | } | ||
613 | } | ||
diff --git a/crates/syntax/src/parsing/lexer.rs b/crates/syntax/src/parsing/lexer.rs index 7e38c32cc..5674ecb84 100644 --- a/crates/syntax/src/parsing/lexer.rs +++ b/crates/syntax/src/parsing/lexer.rs | |||
@@ -3,7 +3,7 @@ | |||
3 | 3 | ||
4 | use std::convert::TryInto; | 4 | use std::convert::TryInto; |
5 | 5 | ||
6 | use rustc_lexer::{LiteralKind as LK, RawStrError}; | 6 | use rustc_lexer::RawStrError; |
7 | 7 | ||
8 | use crate::{ | 8 | use crate::{ |
9 | SyntaxError, | 9 | SyntaxError, |
@@ -185,63 +185,77 @@ fn rustc_token_kind_to_syntax_kind( | |||
185 | return (syntax_kind, None); | 185 | return (syntax_kind, None); |
186 | 186 | ||
187 | fn match_literal_kind(kind: &rustc_lexer::LiteralKind) -> (SyntaxKind, Option<&'static str>) { | 187 | fn match_literal_kind(kind: &rustc_lexer::LiteralKind) -> (SyntaxKind, Option<&'static str>) { |
188 | #[rustfmt::skip] | 188 | let mut err = ""; |
189 | let syntax_kind = match *kind { | 189 | let syntax_kind = match *kind { |
190 | LK::Int { empty_int: false, .. } => INT_NUMBER, | 190 | rustc_lexer::LiteralKind::Int { empty_int, base: _ } => { |
191 | LK::Int { empty_int: true, .. } => { | 191 | if empty_int { |
192 | return (INT_NUMBER, Some("Missing digits after the integer base prefix")) | 192 | err = "Missing digits after the integer base prefix"; |
193 | } | ||
194 | INT_NUMBER | ||
193 | } | 195 | } |
194 | 196 | rustc_lexer::LiteralKind::Float { empty_exponent, base: _ } => { | |
195 | LK::Float { empty_exponent: false, .. } => FLOAT_NUMBER, | 197 | if empty_exponent { |
196 | LK::Float { empty_exponent: true, .. } => { | 198 | err = "Missing digits after the exponent symbol"; |
197 | return (FLOAT_NUMBER, Some("Missing digits after the exponent symbol")) | 199 | } |
200 | FLOAT_NUMBER | ||
198 | } | 201 | } |
199 | 202 | rustc_lexer::LiteralKind::Char { terminated } => { | |
200 | LK::Char { terminated: true } => CHAR, | 203 | if !terminated { |
201 | LK::Char { terminated: false } => { | 204 | err = "Missing trailing `'` symbol to terminate the character literal"; |
202 | return (CHAR, Some("Missing trailing `'` symbol to terminate the character literal")) | 205 | } |
206 | CHAR | ||
203 | } | 207 | } |
204 | 208 | rustc_lexer::LiteralKind::Byte { terminated } => { | |
205 | LK::Byte { terminated: true } => BYTE, | 209 | if !terminated { |
206 | LK::Byte { terminated: false } => { | 210 | err = "Missing trailing `'` symbol to terminate the byte literal"; |
207 | return (BYTE, Some("Missing trailing `'` symbol to terminate the byte literal")) | 211 | } |
212 | BYTE | ||
208 | } | 213 | } |
209 | 214 | rustc_lexer::LiteralKind::Str { terminated } => { | |
210 | LK::Str { terminated: true } => STRING, | 215 | if !terminated { |
211 | LK::Str { terminated: false } => { | 216 | err = "Missing trailing `\"` symbol to terminate the string literal"; |
212 | return (STRING, Some("Missing trailing `\"` symbol to terminate the string literal")) | 217 | } |
218 | STRING | ||
213 | } | 219 | } |
214 | 220 | rustc_lexer::LiteralKind::ByteStr { terminated } => { | |
215 | 221 | if !terminated { | |
216 | LK::ByteStr { terminated: true } => BYTE_STRING, | 222 | err = "Missing trailing `\"` symbol to terminate the byte string literal"; |
217 | LK::ByteStr { terminated: false } => { | 223 | } |
218 | return (BYTE_STRING, Some("Missing trailing `\"` symbol to terminate the byte string literal")) | 224 | BYTE_STRING |
225 | } | ||
226 | rustc_lexer::LiteralKind::RawStr { err: raw_str_err, .. } => { | ||
227 | if let Some(raw_str_err) = raw_str_err { | ||
228 | err = match raw_str_err { | ||
229 | RawStrError::InvalidStarter { .. } => "Missing `\"` symbol after `#` symbols to begin the raw string literal", | ||
230 | RawStrError::NoTerminator { expected, found, .. } => if expected == found { | ||
231 | "Missing trailing `\"` to terminate the raw string literal" | ||
232 | } else { | ||
233 | "Missing trailing `\"` with `#` symbols to terminate the raw string literal" | ||
234 | }, | ||
235 | RawStrError::TooManyDelimiters { .. } => "Too many `#` symbols: raw strings may be delimited by up to 65535 `#` symbols", | ||
236 | }; | ||
237 | }; | ||
238 | RAW_STRING | ||
239 | } | ||
240 | rustc_lexer::LiteralKind::RawByteStr { err: raw_str_err, .. } => { | ||
241 | if let Some(raw_str_err) = raw_str_err { | ||
242 | err = match raw_str_err { | ||
243 | RawStrError::InvalidStarter { .. } => "Missing `\"` symbol after `#` symbols to begin the raw byte string literal", | ||
244 | RawStrError::NoTerminator { expected, found, .. } => if expected == found { | ||
245 | "Missing trailing `\"` to terminate the raw byte string literal" | ||
246 | } else { | ||
247 | "Missing trailing `\"` with `#` symbols to terminate the raw byte string literal" | ||
248 | }, | ||
249 | RawStrError::TooManyDelimiters { .. } => "Too many `#` symbols: raw byte strings may be delimited by up to 65535 `#` symbols", | ||
250 | }; | ||
251 | }; | ||
252 | |||
253 | RAW_BYTE_STRING | ||
219 | } | 254 | } |
220 | |||
221 | LK::RawStr { err, .. } => match err { | ||
222 | None => RAW_STRING, | ||
223 | Some(RawStrError::InvalidStarter { .. }) => return (RAW_STRING, Some("Missing `\"` symbol after `#` symbols to begin the raw string literal")), | ||
224 | Some(RawStrError::NoTerminator { expected, found, .. }) => if expected == found { | ||
225 | return (RAW_STRING, Some("Missing trailing `\"` to terminate the raw string literal")) | ||
226 | } else { | ||
227 | return (RAW_STRING, Some("Missing trailing `\"` with `#` symbols to terminate the raw string literal")) | ||
228 | |||
229 | }, | ||
230 | Some(RawStrError::TooManyDelimiters { .. }) => return (RAW_STRING, Some("Too many `#` symbols: raw strings may be delimited by up to 65535 `#` symbols")), | ||
231 | }, | ||
232 | LK::RawByteStr { err, .. } => match err { | ||
233 | None => RAW_BYTE_STRING, | ||
234 | Some(RawStrError::InvalidStarter { .. }) => return (RAW_BYTE_STRING, Some("Missing `\"` symbol after `#` symbols to begin the raw byte string literal")), | ||
235 | Some(RawStrError::NoTerminator { expected, found, .. }) => if expected == found { | ||
236 | return (RAW_BYTE_STRING, Some("Missing trailing `\"` to terminate the raw byte string literal")) | ||
237 | } else { | ||
238 | return (RAW_BYTE_STRING, Some("Missing trailing `\"` with `#` symbols to terminate the raw byte string literal")) | ||
239 | |||
240 | }, | ||
241 | Some(RawStrError::TooManyDelimiters { .. }) => return (RAW_BYTE_STRING, Some("Too many `#` symbols: raw byte strings may be delimited by up to 65535 `#` symbols")), | ||
242 | }, | ||
243 | }; | 255 | }; |
244 | 256 | ||
245 | (syntax_kind, None) | 257 | let err = if err.is_empty() { None } else { Some(err) }; |
258 | |||
259 | (syntax_kind, err) | ||
246 | } | 260 | } |
247 | } | 261 | } |