mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-30 13:51:31 +00:00
Almost there
This commit is contained in:
parent
b63234e20b
commit
2f84b6e2e5
5 changed files with 141 additions and 22 deletions
|
@ -28,8 +28,8 @@ use crate::{
|
||||||
nameres::DefMap,
|
nameres::DefMap,
|
||||||
path::{ModPath, Path},
|
path::{ModPath, Path},
|
||||||
src::{HasChildSource, HasSource},
|
src::{HasChildSource, HasSource},
|
||||||
AsMacroCall, BlockId, DefWithBodyId, HasModule, LocalModuleId, Lookup, MacroId, ModuleId,
|
AsMacroCall, BlockId, DefWithBodyId, HasModule, LocalModuleId, Lookup, MacroId,
|
||||||
UnresolvedMacro,
|
ModuleId, UnresolvedMacro,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use lower::LowerCtx;
|
pub use lower::LowerCtx;
|
||||||
|
|
|
@ -7,14 +7,17 @@ use std::{
|
||||||
|
|
||||||
use chalk_ir::{BoundVar, DebruijnIndex, GenericArgData, IntTy, Scalar};
|
use chalk_ir::{BoundVar, DebruijnIndex, GenericArgData, IntTy, Scalar};
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
|
builtin_type::BuiltinInt,
|
||||||
expr::{ArithOp, BinaryOp, Expr, ExprId, Literal, Pat, PatId},
|
expr::{ArithOp, BinaryOp, Expr, ExprId, Literal, Pat, PatId},
|
||||||
path::ModPath,
|
path::ModPath,
|
||||||
resolver::{resolver_for_expr, ResolveValueResult, Resolver, ValueNs},
|
resolver::{resolver_for_expr, ResolveValueResult, Resolver, ValueNs},
|
||||||
|
src::HasChildSource,
|
||||||
type_ref::ConstScalar,
|
type_ref::ConstScalar,
|
||||||
ConstId, DefWithBodyId, EnumVariantId,
|
ConstId, DefWithBodyId, EnumVariantId, Lookup,
|
||||||
};
|
};
|
||||||
use la_arena::{Arena, Idx};
|
use la_arena::{Arena, Idx, RawIdx};
|
||||||
use stdx::never;
|
use stdx::never;
|
||||||
|
use syntax::ast::HasName;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase, infer::InferenceContext, lower::ParamLoweringMode, to_placeholder_idx,
|
db::HirDatabase, infer::InferenceContext, lower::ParamLoweringMode, to_placeholder_idx,
|
||||||
|
@ -77,6 +80,7 @@ pub enum ConstEvalError {
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum ComputedExpr {
|
pub enum ComputedExpr {
|
||||||
Literal(Literal),
|
Literal(Literal),
|
||||||
|
Enum(String, EnumVariantId, Literal),
|
||||||
Tuple(Box<[ComputedExpr]>),
|
Tuple(Box<[ComputedExpr]>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,6 +108,7 @@ impl Display for ComputedExpr {
|
||||||
Literal::String(x) => std::fmt::Debug::fmt(x, f),
|
Literal::String(x) => std::fmt::Debug::fmt(x, f),
|
||||||
Literal::ByteString(x) => std::fmt::Debug::fmt(x, f),
|
Literal::ByteString(x) => std::fmt::Debug::fmt(x, f),
|
||||||
},
|
},
|
||||||
|
ComputedExpr::Enum(name, _, _) => name.fmt(f),
|
||||||
ComputedExpr::Tuple(t) => {
|
ComputedExpr::Tuple(t) => {
|
||||||
f.write_char('(')?;
|
f.write_char('(')?;
|
||||||
for x in &**t {
|
for x in &**t {
|
||||||
|
@ -116,6 +121,15 @@ impl Display for ComputedExpr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ComputedExpr {
|
||||||
|
pub fn enum_value(&self) -> Option<ComputedExpr> {
|
||||||
|
match self {
|
||||||
|
ComputedExpr::Enum(_, _, lit) => Some(ComputedExpr::Literal(lit.clone())),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn scalar_max(scalar: &Scalar) -> i128 {
|
fn scalar_max(scalar: &Scalar) -> i128 {
|
||||||
match scalar {
|
match scalar {
|
||||||
Scalar::Bool => 1,
|
Scalar::Bool => 1,
|
||||||
|
@ -148,17 +162,56 @@ fn is_valid(scalar: &Scalar, value: i128) -> bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_name(variant: EnumVariantId, ctx: &mut ConstEvalCtx<'_>) -> String {
|
||||||
|
let loc = variant.parent.lookup(ctx.db.upcast());
|
||||||
|
let children = variant.parent.child_source(ctx.db.upcast());
|
||||||
|
let item_tree = loc.id.item_tree(ctx.db.upcast());
|
||||||
|
|
||||||
|
let variant_name = children.value[variant.local_id].name();
|
||||||
|
let enum_name = item_tree[loc.id.value].name.to_string();
|
||||||
|
enum_name + "::" + &variant_name.unwrap().to_string()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn eval_const(
|
pub fn eval_const(
|
||||||
expr_id: ExprId,
|
expr_id: ExprId,
|
||||||
ctx: &mut ConstEvalCtx<'_>,
|
ctx: &mut ConstEvalCtx<'_>,
|
||||||
|
variant: Option<EnumVariantId>,
|
||||||
) -> Result<ComputedExpr, ConstEvalError> {
|
) -> Result<ComputedExpr, ConstEvalError> {
|
||||||
let expr = &ctx.exprs[expr_id];
|
let expr = &ctx.exprs[expr_id];
|
||||||
match expr {
|
match expr {
|
||||||
Expr::Missing => Err(ConstEvalError::IncompleteExpr),
|
Expr::Missing => match variant {
|
||||||
|
Some(variant) => {
|
||||||
|
let prev_idx: u32 = variant.local_id.into_raw().into();
|
||||||
|
let prev_idx = prev_idx.checked_sub(1).map(|idx| Idx::from_raw(RawIdx::from(idx)));
|
||||||
|
let value = match prev_idx {
|
||||||
|
Some(prev) => {
|
||||||
|
let prev_variant = EnumVariantId { local_id: prev, ..variant };
|
||||||
|
1 + match ctx.db.const_eval_variant(prev_variant)? {
|
||||||
|
ComputedExpr::Literal(Literal::Int(v, _)) => v,
|
||||||
|
ComputedExpr::Literal(Literal::Uint(v, _)) => v
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| ConstEvalError::NotSupported("too big u128"))?,
|
||||||
|
_ => {
|
||||||
|
return Err(ConstEvalError::NotSupported(
|
||||||
|
"Enum can't contain this kind of value",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => 0,
|
||||||
|
};
|
||||||
|
Ok(ComputedExpr::Enum(
|
||||||
|
get_name(variant, ctx),
|
||||||
|
variant,
|
||||||
|
Literal::Int(value + 1, Some(BuiltinInt::I128)),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
_ => Err(ConstEvalError::IncompleteExpr),
|
||||||
|
},
|
||||||
Expr::Literal(l) => Ok(ComputedExpr::Literal(l.clone())),
|
Expr::Literal(l) => Ok(ComputedExpr::Literal(l.clone())),
|
||||||
&Expr::UnaryOp { expr, op } => {
|
&Expr::UnaryOp { expr, op } => {
|
||||||
let ty = &ctx.expr_ty(expr);
|
let ty = &ctx.expr_ty(expr);
|
||||||
let ev = eval_const(expr, ctx)?;
|
let ev = eval_const(expr, ctx, None)?;
|
||||||
match op {
|
match op {
|
||||||
hir_def::expr::UnaryOp::Deref => Err(ConstEvalError::NotSupported("deref")),
|
hir_def::expr::UnaryOp::Deref => Err(ConstEvalError::NotSupported("deref")),
|
||||||
hir_def::expr::UnaryOp::Not => {
|
hir_def::expr::UnaryOp::Not => {
|
||||||
|
@ -214,8 +267,8 @@ pub fn eval_const(
|
||||||
}
|
}
|
||||||
&Expr::BinaryOp { lhs, rhs, op } => {
|
&Expr::BinaryOp { lhs, rhs, op } => {
|
||||||
let ty = &ctx.expr_ty(lhs);
|
let ty = &ctx.expr_ty(lhs);
|
||||||
let lhs = eval_const(lhs, ctx)?;
|
let lhs = eval_const(lhs, ctx, None)?;
|
||||||
let rhs = eval_const(rhs, ctx)?;
|
let rhs = eval_const(rhs, ctx, None)?;
|
||||||
let op = op.ok_or(ConstEvalError::IncompleteExpr)?;
|
let op = op.ok_or(ConstEvalError::IncompleteExpr)?;
|
||||||
let v1 = match lhs {
|
let v1 = match lhs {
|
||||||
ComputedExpr::Literal(Literal::Int(v, _)) => v,
|
ComputedExpr::Literal(Literal::Int(v, _)) => v,
|
||||||
|
@ -276,7 +329,7 @@ pub fn eval_const(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let value = match initializer {
|
let value = match initializer {
|
||||||
Some(x) => eval_const(x, ctx)?,
|
Some(x) => eval_const(x, ctx, None)?,
|
||||||
None => continue,
|
None => continue,
|
||||||
};
|
};
|
||||||
if !prev_values.contains_key(&pat_id) {
|
if !prev_values.contains_key(&pat_id) {
|
||||||
|
@ -292,7 +345,7 @@ pub fn eval_const(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let r = match tail {
|
let r = match tail {
|
||||||
&Some(x) => eval_const(x, ctx),
|
&Some(x) => eval_const(x, ctx, None),
|
||||||
None => Ok(ComputedExpr::Tuple(Box::new([]))),
|
None => Ok(ComputedExpr::Tuple(Box::new([]))),
|
||||||
};
|
};
|
||||||
// clean up local data, so caller will receive the exact map that passed to us
|
// clean up local data, so caller will receive the exact map that passed to us
|
||||||
|
@ -339,10 +392,24 @@ pub fn eval_const(
|
||||||
ValueNs::GenericParam(_) => {
|
ValueNs::GenericParam(_) => {
|
||||||
Err(ConstEvalError::NotSupported("const generic without substitution"))
|
Err(ConstEvalError::NotSupported("const generic without substitution"))
|
||||||
}
|
}
|
||||||
ValueNs::EnumVariantId(id) => ctx.db.const_eval_variant(id),
|
ValueNs::EnumVariantId(id) => match ctx.db.const_eval_variant(id)? {
|
||||||
|
ComputedExpr::Literal(lit) => {
|
||||||
|
Ok(ComputedExpr::Enum(get_name(id, ctx), id, lit))
|
||||||
|
}
|
||||||
|
_ => Err(ConstEvalError::NotSupported(
|
||||||
|
"Enums can't evalute to anything but numbers",
|
||||||
|
)),
|
||||||
|
},
|
||||||
_ => Err(ConstEvalError::NotSupported("path that are not const or local")),
|
_ => Err(ConstEvalError::NotSupported("path that are not const or local")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Expr::Cast { expr, .. } => match eval_const(*expr, ctx, None)? {
|
||||||
|
ComputedExpr::Enum(_, _, lit) => Ok(ComputedExpr::Literal(lit)),
|
||||||
|
expr => Err(ConstEvalError::NotSupported(Box::leak(Box::new(format!(
|
||||||
|
"Can't cast type: {:?}",
|
||||||
|
expr
|
||||||
|
))))),
|
||||||
|
},
|
||||||
_ => Err(ConstEvalError::NotSupported("This kind of expression")),
|
_ => Err(ConstEvalError::NotSupported("This kind of expression")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -438,6 +505,7 @@ pub(crate) fn const_eval_query(
|
||||||
local_data: HashMap::default(),
|
local_data: HashMap::default(),
|
||||||
infer,
|
infer,
|
||||||
},
|
},
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
@ -459,6 +527,7 @@ pub(crate) fn const_eval_query_variant(
|
||||||
local_data: HashMap::default(),
|
local_data: HashMap::default(),
|
||||||
infer,
|
infer,
|
||||||
},
|
},
|
||||||
|
Some(variant_id),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -485,7 +554,7 @@ pub(crate) fn eval_to_const<'a>(
|
||||||
local_data: HashMap::default(),
|
local_data: HashMap::default(),
|
||||||
infer: &ctx.result,
|
infer: &ctx.result,
|
||||||
};
|
};
|
||||||
let computed_expr = eval_const(expr, &mut ctx);
|
let computed_expr = eval_const(expr, &mut ctx, None);
|
||||||
let const_scalar = match computed_expr {
|
let const_scalar = match computed_expr {
|
||||||
Ok(ComputedExpr::Literal(literal)) => literal.into(),
|
Ok(ComputedExpr::Literal(literal)) => literal.into(),
|
||||||
_ => ConstScalar::Unknown,
|
_ => ConstScalar::Unknown,
|
||||||
|
|
|
@ -26,7 +26,7 @@ use hir_def::{
|
||||||
resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs},
|
resolver::{HasResolver, ResolveValueResult, Resolver, TypeNs, ValueNs},
|
||||||
type_ref::TypeRef,
|
type_ref::TypeRef,
|
||||||
AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, HasModule, Lookup,
|
AdtId, AssocItemId, DefWithBodyId, EnumVariantId, FieldId, FunctionId, HasModule, Lookup,
|
||||||
TraitId, TypeAliasId, VariantId
|
TraitId, TypeAliasId, VariantId,
|
||||||
};
|
};
|
||||||
use hir_expand::name::{name, Name};
|
use hir_expand::name::{name, Name};
|
||||||
use itertools::Either;
|
use itertools::Either;
|
||||||
|
@ -68,7 +68,6 @@ pub(crate) fn infer_query(db: &dyn HirDatabase, def: DefWithBodyId) -> Arc<Infer
|
||||||
DefWithBodyId::FunctionId(f) => ctx.collect_fn(f),
|
DefWithBodyId::FunctionId(f) => ctx.collect_fn(f),
|
||||||
DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_data(s)),
|
DefWithBodyId::StaticId(s) => ctx.collect_static(&db.static_data(s)),
|
||||||
DefWithBodyId::VariantId(v) => {
|
DefWithBodyId::VariantId(v) => {
|
||||||
// TODO(ole): Get the real type
|
|
||||||
ctx.return_ty = TyBuilder::def_ty(db, v.parent.into()).fill_with_unknown().build()
|
ctx.return_ty = TyBuilder::def_ty(db, v.parent.into()).fill_with_unknown().build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::fmt::Display;
|
||||||
|
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir::{
|
use hir::{
|
||||||
db::HirDatabase, AsAssocItem, AttributeTemplate, HasAttrs, HasSource, HirDisplay, Semantics, TypeInfo,
|
db::HirDatabase, AsAssocItem, AttributeTemplate, HasAttrs, HasSource, HirDisplay, Semantics, StructKind, TypeInfo,
|
||||||
};
|
};
|
||||||
use ide_db::{
|
use ide_db::{
|
||||||
base_db::SourceDatabase,
|
base_db::SourceDatabase,
|
||||||
|
@ -348,12 +348,12 @@ pub(super) fn definition(
|
||||||
Definition::Module(it) => label_and_docs(db, it),
|
Definition::Module(it) => label_and_docs(db, it),
|
||||||
Definition::Function(it) => label_and_docs(db, it),
|
Definition::Function(it) => label_and_docs(db, it),
|
||||||
Definition::Adt(it) => label_and_docs(db, it),
|
Definition::Adt(it) => label_and_docs(db, it),
|
||||||
Definition::Variant(it) => label_value_and_docs(db, it, |&it| {
|
Definition::Variant(it) => label_value_and_docs(db, it, |&it| match it.kind(db) {
|
||||||
let body = it.eval(db);
|
StructKind::Unit => match it.eval(db) {
|
||||||
match body {
|
Ok(x) => Some(format!("{}", x.enum_value().unwrap_or(x))),
|
||||||
Ok(x) => Some(format!("{}", x)),
|
Err(_) => it.value(db).map(|x| format!("{:?}", x)),
|
||||||
Err(_) => it.value(db).map(|x| format!("{}", x)),
|
},
|
||||||
}
|
_ => None,
|
||||||
}),
|
}),
|
||||||
Definition::Const(it) => label_value_and_docs(db, it, |it| {
|
Definition::Const(it) => label_value_and_docs(db, it, |it| {
|
||||||
let body = it.eval(db);
|
let body = it.eval(db);
|
||||||
|
|
|
@ -3529,6 +3529,31 @@ impl<const LEN: usize> Foo<LEN$0> {}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn hover_const_eval_variant() {
|
fn hover_const_eval_variant() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
#[repr(u8)]
|
||||||
|
enum E {
|
||||||
|
A = 4,
|
||||||
|
/// This is a doc
|
||||||
|
B$0 = E::A as u8 + 1,
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
*B*
|
||||||
|
|
||||||
|
```rust
|
||||||
|
test::E
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
B = 5
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
This is a doc
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
// show hex for <10
|
// show hex for <10
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
@ -3586,7 +3611,7 @@ enum E {
|
||||||
enum E {
|
enum E {
|
||||||
A = 1,
|
A = 1,
|
||||||
/// This is a doc
|
/// This is a doc
|
||||||
B$0 = E::A + 1,
|
B$0 = E::A as u8 + 1,
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
|
@ -3602,6 +3627,32 @@ enum E {
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
This is a doc
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
// unspecified variant should increment by one
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
#[repr(u8)]
|
||||||
|
enum E {
|
||||||
|
A = 4,
|
||||||
|
/// This is a doc
|
||||||
|
B$0,
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
*B*
|
||||||
|
|
||||||
|
```rust
|
||||||
|
test::E
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
B = 5
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
This is a doc
|
This is a doc
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue