Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion crates/assists/src/handlers/convert_integer_literal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ use crate::{AssistContext, AssistId, AssistKind, Assists, GroupLabel};
// const _: i32 = 0b1010;
// ```
pub(crate) fn convert_integer_literal(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
let literal = ctx.find_node_at_offset::<ast::Literal>()?.as_int_number()?;
let literal = ctx.find_node_at_offset::<ast::Literal>()?;
let literal = match literal.kind() {
ast::LiteralKind::IntNumber(it) => it,
_ => return None,
};
let radix = literal.radix();
let value = literal.value()?;
let suffix = literal.suffix();
Expand Down
21 changes: 11 additions & 10 deletions crates/hir_def/src/body/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -953,18 +953,19 @@ impl From<ast::BinOp> for BinaryOp {
impl From<ast::LiteralKind> for Literal {
fn from(ast_lit_kind: ast::LiteralKind) -> Self {
match ast_lit_kind {
LiteralKind::IntNumber { suffix } => {
let known_name = suffix.and_then(|it| BuiltinInt::from_suffix(&it));

Literal::Int(Default::default(), known_name)
LiteralKind::IntNumber(lit) => {
if let Some(float_suffix) = lit.suffix().and_then(BuiltinFloat::from_suffix) {
return Literal::Float(Default::default(), Some(float_suffix));
}
let ty = lit.suffix().and_then(|it| BuiltinInt::from_suffix(&it));
Literal::Int(Default::default(), ty)
}
LiteralKind::FloatNumber { suffix } => {
let known_name = suffix.and_then(|it| BuiltinFloat::from_suffix(&it));

Literal::Float(Default::default(), known_name)
LiteralKind::FloatNumber(lit) => {
let ty = lit.suffix().and_then(|it| BuiltinFloat::from_suffix(&it));
Literal::Float(Default::default(), ty)
}
LiteralKind::ByteString => Literal::ByteString(Default::default()),
LiteralKind::String => Literal::String(Default::default()),
LiteralKind::ByteString(_) => Literal::ByteString(Default::default()),
LiteralKind::String(_) => Literal::String(Default::default()),
LiteralKind::Byte => Literal::Int(Default::default(), Some(BuiltinInt::U8)),
LiteralKind::Bool(val) => Literal::Bool(val),
LiteralKind::Char => Literal::Char(Default::default()),
Expand Down
64 changes: 18 additions & 46 deletions crates/syntax/src/ast/expr_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use crate::{
ast::{self, support, AstChildren, AstNode},
AstToken, SmolStr,
AstToken,
SyntaxKind::*,
SyntaxToken, T,
};
Expand Down Expand Up @@ -298,12 +298,12 @@ impl ast::ArrayExpr {

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum LiteralKind {
String,
ByteString,
String(ast::String),
ByteString(ast::ByteString),
IntNumber(ast::IntNumber),
FloatNumber(ast::FloatNumber),
Char,
Byte,
IntNumber { suffix: Option<SmolStr> },
FloatNumber { suffix: Option<SmolStr> },
Bool(bool),
}

Expand All @@ -315,53 +315,25 @@ impl ast::Literal {
.and_then(|e| e.into_token())
.unwrap()
}

pub fn as_int_number(&self) -> Option<ast::IntNumber> {
ast::IntNumber::cast(self.token())
}

pub fn as_string(&self) -> Option<ast::String> {
ast::String::cast(self.token())
}
pub fn as_byte_string(&self) -> Option<ast::ByteString> {
ast::ByteString::cast(self.token())
}

fn find_suffix(text: &str, possible_suffixes: &[&str]) -> Option<SmolStr> {
possible_suffixes
.iter()
.find(|&suffix| text.ends_with(suffix))
.map(|&suffix| SmolStr::new(suffix))
}

pub fn kind(&self) -> LiteralKind {
let token = self.token();

if let Some(t) = ast::IntNumber::cast(token.clone()) {
return LiteralKind::IntNumber(t);
}
if let Some(t) = ast::FloatNumber::cast(token.clone()) {
return LiteralKind::FloatNumber(t);
}
if let Some(t) = ast::String::cast(token.clone()) {
return LiteralKind::String(t);
}
if let Some(t) = ast::ByteString::cast(token.clone()) {
return LiteralKind::ByteString(t);
}

match token.kind() {
INT_NUMBER => {
// FYI: there was a bug here previously, thus the if statement below is necessary.
// The lexer treats e.g. `1f64` as an integer literal. See
// https://github.com/rust-analyzer/rust-analyzer/issues/1592
// and the comments on the linked PR.
let text = token.text();
if let suffix @ Some(_) = Self::find_suffix(&text, &ast::FloatNumber::SUFFIXES) {
LiteralKind::FloatNumber { suffix }
} else {
LiteralKind::IntNumber {
suffix: Self::find_suffix(&text, &ast::IntNumber::SUFFIXES),
}
}
}
FLOAT_NUMBER => {
let text = token.text();
LiteralKind::FloatNumber {
suffix: Self::find_suffix(&text, &ast::FloatNumber::SUFFIXES),
}
}
STRING => LiteralKind::String,
T![true] => LiteralKind::Bool(true),
T![false] => LiteralKind::Bool(false),
BYTE_STRING => LiteralKind::ByteString,
CHAR => LiteralKind::Char,
BYTE => LiteralKind::Byte,
_ => unreachable!(),
Expand Down
32 changes: 21 additions & 11 deletions crates/syntax/src/ast/token_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -517,10 +517,9 @@ impl HasFormatSpecifier for ast::String {
}

impl ast::IntNumber {
#[rustfmt::skip]
pub(crate) const SUFFIXES: &'static [&'static str] = &[
"u8", "u16", "u32", "u64", "u128", "usize",
"i8", "i16", "i32", "i64", "i128", "isize",
const SUFFIXES: &'static [&'static str] = &[
"u8", "u16", "u32", "u64", "u128", "usize", // Unsigned.
"i8", "i16", "i32", "i64", "i128", "isize", // Signed.
];

pub fn radix(&self) -> Radix {
Expand Down Expand Up @@ -555,9 +554,24 @@ impl ast::IntNumber {

pub fn suffix(&self) -> Option<&str> {
let text = self.text();
// FIXME: don't check a fixed set of suffixes, `1_0_1___lol` is valid
// syntax, suffix is `lol`.
ast::IntNumber::SUFFIXES.iter().find_map(|suffix| {
// FIXME: don't check a fixed set of suffixes, `1_0_1_l_o_l` is valid
// syntax, suffix is `l_o_l`.
ast::IntNumber::SUFFIXES.iter().chain(ast::FloatNumber::SUFFIXES.iter()).find_map(
|suffix| {
if text.ends_with(suffix) {
return Some(&text[text.len() - suffix.len()..]);
}
None
},
)
}
}

impl ast::FloatNumber {
const SUFFIXES: &'static [&'static str] = &["f32", "f64"];
pub fn suffix(&self) -> Option<&str> {
let text = self.text();
ast::FloatNumber::SUFFIXES.iter().find_map(|suffix| {
if text.ends_with(suffix) {
return Some(&text[text.len() - suffix.len()..]);
}
Expand All @@ -566,10 +580,6 @@ impl ast::IntNumber {
}
}

impl ast::FloatNumber {
pub(crate) const SUFFIXES: &'static [&'static str] = &["f32", "f64"];
}

#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum Radix {
Binary = 2,
Expand Down
57 changes: 29 additions & 28 deletions crates/syntax/src/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ mod block;

use crate::{
algo, ast, match_ast, AstNode, SyntaxError,
SyntaxKind::{BYTE, CHAR, CONST, FN, INT_NUMBER, TYPE_ALIAS},
SyntaxKind::{CONST, FN, INT_NUMBER, TYPE_ALIAS},
SyntaxNode, SyntaxToken, TextSize, T,
};
use rowan::Direction;
Expand Down Expand Up @@ -121,41 +121,42 @@ fn validate_literal(literal: ast::Literal, acc: &mut Vec<SyntaxError>) {
acc.push(SyntaxError::new_at_offset(rustc_unescape_error_to_string(err), off));
};

if let Some(s) = literal.as_string() {
if !s.is_raw() {
if let Some(without_quotes) = unquote(text, 1, '"') {
unescape_literal(without_quotes, Mode::Str, &mut |range, char| {
if let Err(err) = char {
push_err(1, (range.start, err));
}
})
}
}
}
if let Some(s) = literal.as_byte_string() {
if !s.is_raw() {
if let Some(without_quotes) = unquote(text, 2, '"') {
unescape_byte_literal(without_quotes, Mode::ByteStr, &mut |range, char| {
if let Err(err) = char {
push_err(2, (range.start, err));
}
})
match literal.kind() {
ast::LiteralKind::String(s) => {
if !s.is_raw() {
if let Some(without_quotes) = unquote(text, 1, '"') {
unescape_literal(without_quotes, Mode::Str, &mut |range, char| {
if let Err(err) = char {
push_err(1, (range.start, err));
}
})
}
}
}
}

match token.kind() {
BYTE => {
if let Some(Err(e)) = unquote(text, 2, '\'').map(unescape_byte) {
push_err(2, e);
ast::LiteralKind::ByteString(s) => {
if !s.is_raw() {
if let Some(without_quotes) = unquote(text, 2, '"') {
unescape_byte_literal(without_quotes, Mode::ByteStr, &mut |range, char| {
if let Err(err) = char {
push_err(2, (range.start, err));
}
})
}
}
}
CHAR => {
ast::LiteralKind::Char => {
if let Some(Err(e)) = unquote(text, 1, '\'').map(unescape_char) {
push_err(1, e);
}
}
_ => (),
ast::LiteralKind::Byte => {
if let Some(Err(e)) = unquote(text, 2, '\'').map(unescape_byte) {
push_err(2, e);
}
}
ast::LiteralKind::IntNumber(_)
| ast::LiteralKind::FloatNumber(_)
| ast::LiteralKind::Bool(_) => {}
}
}

Expand Down