mirror of
https://github.com/rust-lang/rust.git
synced 2024-12-02 03:33:59 +00:00
Implement type inference for literals (WIP)
This commit is contained in:
parent
8caff4e034
commit
a6146d35b1
@ -5,7 +5,10 @@ use rustc_hash::FxHashMap;
|
||||
|
||||
use ra_arena::{Arena, RawId, impl_arena_id, map::ArenaMap};
|
||||
use ra_db::{LocalSyntaxPtr, Cancelable};
|
||||
use ra_syntax::ast::{self, AstNode, LoopBodyOwner, ArgListOwner, NameOwner};
|
||||
use ra_syntax::{
|
||||
SyntaxKind,
|
||||
ast::{self, AstNode, LoopBodyOwner, ArgListOwner, NameOwner}
|
||||
};
|
||||
|
||||
use crate::{Path, type_ref::{Mutability, TypeRef}, Name, HirDatabase, DefId, Def, name::AsName};
|
||||
|
||||
@ -103,6 +106,19 @@ impl BodySyntaxMapping {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum Literal {
|
||||
String(String),
|
||||
ByteString(Vec<u8>),
|
||||
Char(char),
|
||||
Bool(bool),
|
||||
Byte(u8),
|
||||
Int, // this and float need additional information
|
||||
Float,
|
||||
Tuple { values: Vec<ExprId> },
|
||||
Array { values: Vec<ExprId> },
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum Expr {
|
||||
/// This is produced if syntax tree does not have a required expression piece.
|
||||
@ -186,6 +202,7 @@ pub enum Expr {
|
||||
Tuple {
|
||||
exprs: Vec<ExprId>,
|
||||
},
|
||||
Literal(Literal),
|
||||
}
|
||||
|
||||
pub use ra_syntax::ast::PrefixOp as UnaryOp;
|
||||
@ -305,6 +322,20 @@ impl Expr {
|
||||
f(*expr);
|
||||
}
|
||||
}
|
||||
Expr::Literal(l) => match l {
|
||||
Literal::Array { values } | Literal::Tuple { values } => {
|
||||
for &val in values {
|
||||
f(val);
|
||||
}
|
||||
}
|
||||
Literal::String(..)
|
||||
| Literal::ByteString(..)
|
||||
| Literal::Byte(..)
|
||||
| Literal::Bool(..)
|
||||
| Literal::Char(..)
|
||||
| Literal::Int
|
||||
| Literal::Float => {}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -633,13 +664,56 @@ impl ExprCollector {
|
||||
let exprs = e.exprs().map(|expr| self.collect_expr(expr)).collect();
|
||||
self.alloc_expr(Expr::Tuple { exprs }, syntax_ptr)
|
||||
}
|
||||
ast::ExprKind::Literal(e) => {
|
||||
let child = e.syntax().children().next();
|
||||
|
||||
if let Some(c) = child {
|
||||
let lit = match c.kind() {
|
||||
SyntaxKind::INT_NUMBER => Literal::Int,
|
||||
SyntaxKind::FLOAT_NUMBER => Literal::Float,
|
||||
SyntaxKind::STRING => {
|
||||
// FIXME: this likely includes the " characters
|
||||
let text = c.text().to_string();
|
||||
Literal::String(text)
|
||||
}
|
||||
SyntaxKind::ARRAY_EXPR => {
|
||||
// TODO: recursively call to self
|
||||
Literal::Array { values: vec![] }
|
||||
}
|
||||
SyntaxKind::PAREN_EXPR => {
|
||||
// TODO: recursively call to self
|
||||
Literal::Tuple { values: vec![] }
|
||||
}
|
||||
SyntaxKind::TRUE_KW => Literal::Bool(true),
|
||||
SyntaxKind::FALSE_KW => Literal::Bool(false),
|
||||
SyntaxKind::BYTE_STRING => {
|
||||
// FIXME: this is completely incorrect for a variety
|
||||
// of reasons, but at least it gives the right type
|
||||
let bytes = c.text().to_string().into_bytes();
|
||||
Literal::ByteString(bytes)
|
||||
}
|
||||
SyntaxKind::CHAR => {
|
||||
let character = c.text().char_at(1).unwrap_or('X');
|
||||
Literal::Char(character)
|
||||
}
|
||||
SyntaxKind::BYTE => {
|
||||
let character = c.text().char_at(1).unwrap_or('X');
|
||||
Literal::Byte(character as u8)
|
||||
}
|
||||
_ => return self.alloc_expr(Expr::Missing, syntax_ptr),
|
||||
};
|
||||
|
||||
self.alloc_expr(Expr::Literal(lit), syntax_ptr)
|
||||
} else {
|
||||
self.alloc_expr(Expr::Missing, syntax_ptr)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO implement HIR for these:
|
||||
ast::ExprKind::Label(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
|
||||
ast::ExprKind::IndexExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
|
||||
ast::ExprKind::ArrayExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
|
||||
ast::ExprKind::RangeExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
|
||||
ast::ExprKind::Literal(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,7 @@ use crate::{
|
||||
db::HirDatabase,
|
||||
type_ref::{TypeRef, Mutability},
|
||||
name::KnownName,
|
||||
expr::{Body, Expr, ExprId, PatId, UnaryOp, BinaryOp, Statement},
|
||||
expr::{Body, Expr, Literal, ExprId, PatId, UnaryOp, BinaryOp, Statement},
|
||||
};
|
||||
|
||||
fn transpose<T>(x: Cancelable<Option<T>>) -> Option<Cancelable<T>> {
|
||||
@ -1067,6 +1067,37 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||
|
||||
Ty::Tuple(Arc::from(ty_vec))
|
||||
}
|
||||
Expr::Literal(lit) => match lit {
|
||||
Literal::Bool(..) => Ty::Bool,
|
||||
Literal::String(..) => Ty::Ref(Arc::new(Ty::Str), Mutability::Shared),
|
||||
Literal::ByteString(..) => {
|
||||
let byte_type = Arc::new(Ty::Uint(primitive::UintTy::U8));
|
||||
let slice_type = Arc::new(Ty::Slice(byte_type));
|
||||
Ty::Ref(slice_type, Mutability::Shared)
|
||||
}
|
||||
Literal::Byte(..) => Ty::Uint(primitive::UintTy::U8),
|
||||
Literal::Char(..) => Ty::Char,
|
||||
Literal::Tuple { values } => {
|
||||
let mut inner_tys = Vec::new();
|
||||
for &expr in values {
|
||||
let inner_ty = self.infer_expr(expr, &Expectation::none())?;
|
||||
inner_tys.push(inner_ty);
|
||||
}
|
||||
Ty::Tuple(Arc::from(inner_tys))
|
||||
}
|
||||
Literal::Array { values } => {
|
||||
// simply take the type of the first element for now
|
||||
let inner_ty = match values.get(0) {
|
||||
Some(&expr) => self.infer_expr(expr, &Expectation::none())?,
|
||||
None => Ty::Unknown,
|
||||
};
|
||||
// TODO: we should return a Ty::Array when it becomes
|
||||
// available
|
||||
Ty::Slice(Arc::new(inner_ty))
|
||||
}
|
||||
// TODO
|
||||
Literal::Int | Literal::Float => Ty::Unknown,
|
||||
},
|
||||
};
|
||||
// use a new type variable if we got Ty::Unknown here
|
||||
let ty = self.insert_type_vars_shallow(ty);
|
||||
|
@ -132,6 +132,26 @@ fn test(a: &u32, b: &mut u32, c: *const u32, d: *mut u32) {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_literals() {
|
||||
check_inference(
|
||||
r#"
|
||||
fn test() {
|
||||
5i32;
|
||||
"hello";
|
||||
b"bytes";
|
||||
'c';
|
||||
b'b';
|
||||
3.14;
|
||||
5000;
|
||||
(0u32, -5isize);
|
||||
[true, true, false]
|
||||
}
|
||||
"#,
|
||||
"literals.txt",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_backwards() {
|
||||
check_inference(
|
||||
|
@ -9,5 +9,5 @@
|
||||
[69; 70) 'd': &str
|
||||
[76; 82) '1usize': [unknown]
|
||||
[88; 94) '1isize': [unknown]
|
||||
[100; 106) '"test"': [unknown]
|
||||
[100; 106) '"test"': &str
|
||||
[112; 118) '1.0f32': [unknown]
|
||||
|
10
crates/ra_hir/src/ty/tests/data/literals.txt
Normal file
10
crates/ra_hir/src/ty/tests/data/literals.txt
Normal file
@ -0,0 +1,10 @@
|
||||
[11; 135) '{ ...lse] }': ()
|
||||
[17; 21) '5i32': [unknown]
|
||||
[27; 34) '"hello"': &str
|
||||
[40; 48) 'b"bytes"': &[u8]
|
||||
[54; 57) ''c'': char
|
||||
[63; 67) 'b'b'': u8
|
||||
[73; 77) '3.14': [unknown]
|
||||
[83; 87) '5000': [unknown]
|
||||
[93; 108) '(0u32, -5isize)': [unknown]
|
||||
[114; 133) '[true,...false]': ()
|
@ -59,24 +59,29 @@ impl SourceFile {
|
||||
assert_eq!(root.kind(), SyntaxKind::SOURCE_FILE);
|
||||
TreeArc::cast(root)
|
||||
}
|
||||
|
||||
pub fn parse(text: &str) -> TreeArc<SourceFile> {
|
||||
let tokens = tokenize(&text);
|
||||
let (green, errors) =
|
||||
parser_impl::parse_with(yellow::GreenBuilder::new(), text, &tokens, grammar::root);
|
||||
SourceFile::new(green, errors)
|
||||
}
|
||||
|
||||
pub fn reparse(&self, edit: &AtomTextEdit) -> TreeArc<SourceFile> {
|
||||
self.incremental_reparse(edit)
|
||||
.unwrap_or_else(|| self.full_reparse(edit))
|
||||
}
|
||||
|
||||
pub fn incremental_reparse(&self, edit: &AtomTextEdit) -> Option<TreeArc<SourceFile>> {
|
||||
reparsing::incremental_reparse(self.syntax(), edit, self.errors())
|
||||
.map(|(green_node, errors)| SourceFile::new(green_node, errors))
|
||||
}
|
||||
|
||||
fn full_reparse(&self, edit: &AtomTextEdit) -> TreeArc<SourceFile> {
|
||||
let text = edit.apply(self.syntax().text().to_string());
|
||||
SourceFile::parse(&text)
|
||||
}
|
||||
|
||||
pub fn errors(&self) -> Vec<SyntaxError> {
|
||||
let mut errors = self.syntax.root_data().clone();
|
||||
errors.extend(validation::validate(self));
|
||||
|
@ -128,40 +128,52 @@ impl SyntaxNode {
|
||||
pub(crate) fn root_data(&self) -> &Vec<SyntaxError> {
|
||||
self.0.root_data()
|
||||
}
|
||||
|
||||
pub(crate) fn replace_with(&self, replacement: GreenNode) -> GreenNode {
|
||||
self.0.replace_self(replacement)
|
||||
}
|
||||
|
||||
pub fn to_owned(&self) -> TreeArc<SyntaxNode> {
|
||||
let ptr = TreeArc(self.0.to_owned());
|
||||
TreeArc::cast(ptr)
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> SyntaxKind {
|
||||
self.0.kind()
|
||||
}
|
||||
|
||||
pub fn range(&self) -> TextRange {
|
||||
self.0.range()
|
||||
}
|
||||
|
||||
pub fn text(&self) -> SyntaxText {
|
||||
SyntaxText::new(self)
|
||||
}
|
||||
|
||||
pub fn is_leaf(&self) -> bool {
|
||||
self.0.is_leaf()
|
||||
}
|
||||
|
||||
pub fn parent(&self) -> Option<&SyntaxNode> {
|
||||
self.0.parent().map(SyntaxNode::from_repr)
|
||||
}
|
||||
|
||||
pub fn first_child(&self) -> Option<&SyntaxNode> {
|
||||
self.0.first_child().map(SyntaxNode::from_repr)
|
||||
}
|
||||
|
||||
pub fn last_child(&self) -> Option<&SyntaxNode> {
|
||||
self.0.last_child().map(SyntaxNode::from_repr)
|
||||
}
|
||||
|
||||
pub fn next_sibling(&self) -> Option<&SyntaxNode> {
|
||||
self.0.next_sibling().map(SyntaxNode::from_repr)
|
||||
}
|
||||
|
||||
pub fn prev_sibling(&self) -> Option<&SyntaxNode> {
|
||||
self.0.prev_sibling().map(SyntaxNode::from_repr)
|
||||
}
|
||||
|
||||
pub fn children(&self) -> SyntaxNodeChildren {
|
||||
SyntaxNodeChildren(self.0.children())
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ impl<'a> SyntaxText<'a> {
|
||||
range: node.range(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chunks(&self) -> impl Iterator<Item = &'a str> {
|
||||
let range = self.range;
|
||||
self.node.descendants().filter_map(move |node| {
|
||||
@ -24,15 +25,19 @@ impl<'a> SyntaxText<'a> {
|
||||
Some(&text[range])
|
||||
})
|
||||
}
|
||||
|
||||
pub fn push_to(&self, buf: &mut String) {
|
||||
self.chunks().for_each(|it| buf.push_str(it));
|
||||
}
|
||||
|
||||
pub fn to_string(&self) -> String {
|
||||
self.chunks().collect()
|
||||
}
|
||||
|
||||
pub fn contains(&self, c: char) -> bool {
|
||||
self.chunks().any(|it| it.contains(c))
|
||||
}
|
||||
|
||||
pub fn find(&self, c: char) -> Option<TextUnit> {
|
||||
let mut acc: TextUnit = 0.into();
|
||||
for chunk in self.chunks() {
|
||||
@ -44,9 +49,11 @@ impl<'a> SyntaxText<'a> {
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn len(&self) -> TextUnit {
|
||||
self.range.len()
|
||||
}
|
||||
|
||||
pub fn slice(&self, range: impl SyntaxTextSlice) -> SyntaxText<'a> {
|
||||
let range = range.restrict(self.range).unwrap_or_else(|| {
|
||||
panic!("invalid slice, range: {:?}, slice: {:?}", self.range, range)
|
||||
@ -56,8 +63,10 @@ impl<'a> SyntaxText<'a> {
|
||||
range,
|
||||
}
|
||||
}
|
||||
pub fn char_at(&self, offset: TextUnit) -> Option<char> {
|
||||
|
||||
pub fn char_at(&self, offset: impl Into<TextUnit>) -> Option<char> {
|
||||
let mut start: TextUnit = 0.into();
|
||||
let offset = offset.into();
|
||||
for chunk in self.chunks() {
|
||||
let end = start + TextUnit::of_str(chunk);
|
||||
if start <= offset && offset < end {
|
||||
|
Loading…
Reference in New Issue
Block a user