From 73023c0299d4adeada026648c3684621f129e038 Mon Sep 17 00:00:00 2001 From: Jade Date: Wed, 12 May 2021 05:44:01 -0700 Subject: [PATCH] Support length for ByteStrings I am not confident that my added byte string parsing is right. --- crates/hir_def/src/body/lower.rs | 5 +- crates/hir_ty/src/infer/expr.rs | 20 +++-- crates/hir_ty/src/tests/simple.rs | 4 +- crates/ide/src/join_lines.rs | 2 +- crates/ide/src/syntax_highlighting/inject.rs | 2 +- crates/ide_assists/src/handlers/raw_string.rs | 2 +- .../src/handlers/replace_string_with_char.rs | 2 +- crates/syntax/src/ast/token_ext.rs | 81 ++++++++++++++----- 8 files changed, 85 insertions(+), 33 deletions(-) diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs index 9f278d35b73..b00dcbdf040 100644 --- a/crates/hir_def/src/body/lower.rs +++ b/crates/hir_def/src/body/lower.rs @@ -1022,7 +1022,10 @@ fn from(ast_lit_kind: ast::LiteralKind) -> Self { let ty = lit.suffix().and_then(|it| BuiltinFloat::from_suffix(&it)); Literal::Float(Default::default(), ty) } - LiteralKind::ByteString(_) => Literal::ByteString(Default::default()), + LiteralKind::ByteString(bs) => { + let text = bs.value().map(Vec::from).unwrap_or_else(Default::default); + Literal::ByteString(text) + } LiteralKind::String(_) => Literal::String(Default::default()), LiteralKind::Byte => Literal::Uint(Default::default(), Some(BuiltinUint::U8)), LiteralKind::Bool(val) => Literal::Bool(val), diff --git a/crates/hir_ty/src/infer/expr.rs b/crates/hir_ty/src/infer/expr.rs index 5e942075249..46e4777a40a 100644 --- a/crates/hir_ty/src/infer/expr.rs +++ b/crates/hir_ty/src/infer/expr.rs @@ -16,7 +16,7 @@ use syntax::ast::RangeOp; use crate::{ - autoderef, dummy_usize_const, + autoderef, lower::lower_to_chalk_mutability, mapping::from_chalk, method_resolution, op, @@ -24,8 +24,9 @@ static_lifetime, to_chalk_trait_id, traits::FnTrait, utils::{generics, Generics}, - AdtId, Binders, CallableDefId, ConstValue, FnPointer, FnSig, FnSubst, InEnvironment, Interner, - ProjectionTyExt, Rawness, Scalar, Substitution, TraitRef, Ty, TyBuilder, TyExt, TyKind, + AdtId, Binders, CallableDefId, ConcreteConst, ConstValue, FnPointer, FnSig, FnSubst, + InEnvironment, Interner, ProjectionTyExt, Rawness, Scalar, Substitution, TraitRef, Ty, + TyBuilder, TyExt, TyKind, }; use super::{ @@ -758,11 +759,18 @@ fn infer_expr_inner(&mut self, tgt_expr: ExprId, expected: &Expectation) -> Ty { TyKind::Ref(Mutability::Not, static_lifetime(), TyKind::Str.intern(&Interner)) .intern(&Interner) } - Literal::ByteString(..) => { + Literal::ByteString(bs) => { let byte_type = TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(&Interner); - let array_type = - TyKind::Array(byte_type, dummy_usize_const()).intern(&Interner); + let len = ConstData { + ty: TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(&Interner), + value: ConstValue::Concrete(ConcreteConst { + interned: ConstScalar::Usize(bs.len() as u64), + }), + } + .intern(&Interner); + + let array_type = TyKind::Array(byte_type, len).intern(&Interner); TyKind::Ref(Mutability::Not, static_lifetime(), array_type).intern(&Interner) } Literal::Char(..) => TyKind::Scalar(Scalar::Char).intern(&Interner), diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs index 19775a4ece4..79445a12d9e 100644 --- a/crates/hir_ty/src/tests/simple.rs +++ b/crates/hir_ty/src/tests/simple.rs @@ -496,7 +496,7 @@ mod foo {} 26..30 '5f32': f32 36..40 '5f64': f64 46..53 '"hello"': &str - 59..67 'b"bytes"': &[u8; _] + 59..67 'b"bytes"': &[u8; 5] 73..76 ''c'': char 82..86 'b'b'': u8 92..96 '3.14': f64 @@ -504,7 +504,7 @@ mod foo {} 112..117 'false': bool 123..127 'true': bool 133..197 'r#" ... "#': &str - 203..213 'br#"yolo"#': &[u8; _] + 203..213 'br#"yolo"#': &[u8; 4] "##]], ); } diff --git a/crates/ide/src/join_lines.rs b/crates/ide/src/join_lines.rs index 61dcbb399dd..c67ccd1a9f7 100644 --- a/crates/ide/src/join_lines.rs +++ b/crates/ide/src/join_lines.rs @@ -4,7 +4,7 @@ use itertools::Itertools; use syntax::{ algo::non_trivia_sibling, - ast::{self, AstNode, AstToken}, + ast::{self, AstNode, AstToken, IsString}, Direction, NodeOrToken, SourceFile, SyntaxKind::{self, USE_TREE, WHITESPACE}, SyntaxNode, SyntaxToken, TextRange, TextSize, T, diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs index bc221d59966..4269d339ebf 100644 --- a/crates/ide/src/syntax_highlighting/inject.rs +++ b/crates/ide/src/syntax_highlighting/inject.rs @@ -6,7 +6,7 @@ use hir::{InFile, Semantics}; use ide_db::{call_info::ActiveParameter, helpers::rust_doc::is_rust_fence, SymbolKind}; use syntax::{ - ast::{self, AstNode}, + ast::{self, AstNode, IsString}, AstToken, NodeOrToken, SyntaxNode, SyntaxToken, TextRange, TextSize, }; diff --git a/crates/ide_assists/src/handlers/raw_string.rs b/crates/ide_assists/src/handlers/raw_string.rs index d0f1613f37b..d98a55ae4ab 100644 --- a/crates/ide_assists/src/handlers/raw_string.rs +++ b/crates/ide_assists/src/handlers/raw_string.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; -use syntax::{ast, AstToken, TextRange, TextSize}; +use syntax::{ast, ast::IsString, AstToken, TextRange, TextSize}; use crate::{AssistContext, AssistId, AssistKind, Assists}; diff --git a/crates/ide_assists/src/handlers/replace_string_with_char.rs b/crates/ide_assists/src/handlers/replace_string_with_char.rs index 634b9c0b791..0800d291e44 100644 --- a/crates/ide_assists/src/handlers/replace_string_with_char.rs +++ b/crates/ide_assists/src/handlers/replace_string_with_char.rs @@ -1,4 +1,4 @@ -use syntax::{ast, AstToken, SyntaxKind::STRING}; +use syntax::{ast, ast::IsString, AstToken, SyntaxKind::STRING}; use crate::{AssistContext, AssistId, AssistKind, Assists}; diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs index 29d25a58a31..4b1e1ccee29 100644 --- a/crates/syntax/src/ast/token_ext.rs +++ b/crates/syntax/src/ast/token_ext.rs @@ -143,6 +143,30 @@ fn new(literal: &str) -> Option { } } +pub trait IsString: AstToken { + fn quote_offsets(&self) -> Option { + let text = self.text(); + let offsets = QuoteOffsets::new(text)?; + let o = self.syntax().text_range().start(); + let offsets = QuoteOffsets { + quotes: (offsets.quotes.0 + o, offsets.quotes.1 + o), + contents: offsets.contents + o, + }; + Some(offsets) + } + fn text_range_between_quotes(&self) -> Option { + self.quote_offsets().map(|it| it.contents) + } + fn open_quote_text_range(&self) -> Option { + self.quote_offsets().map(|it| it.quotes.0) + } + fn close_quote_text_range(&self) -> Option { + self.quote_offsets().map(|it| it.quotes.1) + } +} + +impl IsString for ast::String {} + impl ast::String { pub fn is_raw(&self) -> bool { self.text().starts_with('r') @@ -187,32 +211,49 @@ pub fn value(&self) -> Option> { (false, false) => Some(Cow::Owned(buf)), } } - - pub fn quote_offsets(&self) -> Option { - let text = self.text(); - let offsets = QuoteOffsets::new(text)?; - let o = self.syntax().text_range().start(); - let offsets = QuoteOffsets { - quotes: (offsets.quotes.0 + o, offsets.quotes.1 + o), - contents: offsets.contents + o, - }; - Some(offsets) - } - pub fn text_range_between_quotes(&self) -> Option { - self.quote_offsets().map(|it| it.contents) - } - pub fn open_quote_text_range(&self) -> Option { - self.quote_offsets().map(|it| it.quotes.0) - } - pub fn close_quote_text_range(&self) -> Option { - self.quote_offsets().map(|it| it.quotes.1) - } } +impl IsString for ast::ByteString {} + impl ast::ByteString { pub fn is_raw(&self) -> bool { self.text().starts_with("br") } + + pub fn value(&self) -> Option> { + if self.is_raw() { + let text = self.text(); + let text = + &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; + return Some(Cow::Borrowed(text.as_bytes())); + } + + let text = self.text(); + let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; + + let mut buf: Vec = Vec::new(); + let mut text_iter = text.chars(); + let mut has_error = false; + unescape_literal(text, Mode::ByteStr, &mut |char_range, unescaped_char| match ( + unescaped_char, + buf.capacity() == 0, + ) { + (Ok(c), false) => buf.push(c as u8), + (Ok(c), true) if char_range.len() == 1 && Some(c) == text_iter.next() => (), + (Ok(c), true) => { + buf.reserve_exact(text.len()); + buf.extend_from_slice(&text[..char_range.start].as_bytes()); + buf.push(c as u8); + } + (Err(_), _) => has_error = true, + }); + + match (has_error, buf.capacity() == 0) { + (true, _) => None, + (false, true) => Some(Cow::Borrowed(text.as_bytes())), + (false, false) => Some(Cow::Owned(buf)), + } + } } #[derive(Debug)] -- 2.44.0