mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-28 04:44:57 +00:00
Support constants in const eval
This commit is contained in:
parent
b8e1d09b90
commit
0e2989e421
5 changed files with 198 additions and 50 deletions
|
@ -32,7 +32,7 @@ pub mod symbols;
|
||||||
|
|
||||||
mod display;
|
mod display;
|
||||||
|
|
||||||
use std::{collections::HashMap, iter, ops::ControlFlow, sync::Arc};
|
use std::{iter, ops::ControlFlow, sync::Arc};
|
||||||
|
|
||||||
use arrayvec::ArrayVec;
|
use arrayvec::ArrayVec;
|
||||||
use base_db::{CrateDisplayName, CrateId, CrateOrigin, Edition, FileId, ProcMacroKind};
|
use base_db::{CrateDisplayName, CrateId, CrateOrigin, Edition, FileId, ProcMacroKind};
|
||||||
|
@ -58,6 +58,7 @@ use hir_ty::{
|
||||||
consteval::{
|
consteval::{
|
||||||
eval_const, unknown_const_as_generic, ComputedExpr, ConstEvalCtx, ConstEvalError, ConstExt,
|
eval_const, unknown_const_as_generic, ComputedExpr, ConstEvalCtx, ConstEvalError, ConstExt,
|
||||||
},
|
},
|
||||||
|
could_unify,
|
||||||
diagnostics::BodyValidationDiagnostic,
|
diagnostics::BodyValidationDiagnostic,
|
||||||
method_resolution::{self, TyFingerprint},
|
method_resolution::{self, TyFingerprint},
|
||||||
primitive::UintTy,
|
primitive::UintTy,
|
||||||
|
@ -1602,20 +1603,7 @@ impl Const {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eval(self, db: &dyn HirDatabase) -> Result<ComputedExpr, ConstEvalError> {
|
pub fn eval(self, db: &dyn HirDatabase) -> Result<ComputedExpr, ConstEvalError> {
|
||||||
let body = db.body(self.id.into());
|
db.const_eval(self.id)
|
||||||
let root = &body.exprs[body.body_expr];
|
|
||||||
let infer = db.infer_query(self.id.into());
|
|
||||||
let infer = infer.as_ref();
|
|
||||||
let result = eval_const(
|
|
||||||
root,
|
|
||||||
&mut ConstEvalCtx {
|
|
||||||
exprs: &body.exprs,
|
|
||||||
pats: &body.pats,
|
|
||||||
local_data: HashMap::default(),
|
|
||||||
infer: &mut |x| infer[x].clone(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
result
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,22 +8,20 @@ use std::{
|
||||||
|
|
||||||
use chalk_ir::{BoundVar, DebruijnIndex, GenericArgData, IntTy, Scalar};
|
use chalk_ir::{BoundVar, DebruijnIndex, GenericArgData, IntTy, Scalar};
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
expr::{ArithOp, BinaryOp, Expr, Literal, Pat},
|
expr::{ArithOp, BinaryOp, Expr, ExprId, Literal, Pat},
|
||||||
path::ModPath,
|
path::ModPath,
|
||||||
resolver::{Resolver, ValueNs},
|
resolver::{resolver_for_expr, ResolveValueResult, Resolver, ValueNs},
|
||||||
type_ref::ConstScalar,
|
type_ref::ConstScalar,
|
||||||
|
ConstId, DefWithBodyId,
|
||||||
};
|
};
|
||||||
use hir_expand::name::Name;
|
use hir_expand::name::Name;
|
||||||
use la_arena::{Arena, Idx};
|
use la_arena::{Arena, Idx};
|
||||||
use stdx::never;
|
use stdx::never;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase,
|
db::HirDatabase, infer::InferenceContext, lower::ParamLoweringMode, to_placeholder_idx,
|
||||||
infer::{Expectation, InferenceContext},
|
utils::Generics, Const, ConstData, ConstValue, GenericArg, InferenceResult, Interner, Ty,
|
||||||
lower::ParamLoweringMode,
|
TyKind,
|
||||||
to_placeholder_idx,
|
|
||||||
utils::Generics,
|
|
||||||
Const, ConstData, ConstValue, GenericArg, Interner, Ty, TyKind,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Extension trait for [`Const`]
|
/// Extension trait for [`Const`]
|
||||||
|
@ -55,21 +53,30 @@ impl ConstExt for Const {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ConstEvalCtx<'a> {
|
pub struct ConstEvalCtx<'a> {
|
||||||
|
pub db: &'a dyn HirDatabase,
|
||||||
|
pub owner: DefWithBodyId,
|
||||||
pub exprs: &'a Arena<Expr>,
|
pub exprs: &'a Arena<Expr>,
|
||||||
pub pats: &'a Arena<Pat>,
|
pub pats: &'a Arena<Pat>,
|
||||||
pub local_data: HashMap<Name, ComputedExpr>,
|
pub local_data: HashMap<Name, ComputedExpr>,
|
||||||
pub infer: &'a mut dyn FnMut(Idx<Expr>) -> Ty,
|
infer: &'a InferenceResult,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
impl ConstEvalCtx<'_> {
|
||||||
|
fn expr_ty(&mut self, expr: ExprId) -> Ty {
|
||||||
|
self.infer[expr].clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum ConstEvalError {
|
pub enum ConstEvalError {
|
||||||
NotSupported(&'static str),
|
NotSupported(&'static str),
|
||||||
TypeError,
|
SemanticError(&'static str),
|
||||||
|
Loop,
|
||||||
IncompleteExpr,
|
IncompleteExpr,
|
||||||
Panic(String),
|
Panic(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum ComputedExpr {
|
pub enum ComputedExpr {
|
||||||
Literal(Literal),
|
Literal(Literal),
|
||||||
Tuple(Box<[ComputedExpr]>),
|
Tuple(Box<[ComputedExpr]>),
|
||||||
|
@ -143,12 +150,16 @@ fn is_valid(scalar: &Scalar, value: i128) -> bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eval_const(expr: &Expr, ctx: &mut ConstEvalCtx<'_>) -> Result<ComputedExpr, ConstEvalError> {
|
pub fn eval_const(
|
||||||
|
expr_id: ExprId,
|
||||||
|
ctx: &mut ConstEvalCtx<'_>,
|
||||||
|
) -> Result<ComputedExpr, ConstEvalError> {
|
||||||
|
let expr = &ctx.exprs[expr_id];
|
||||||
match expr {
|
match expr {
|
||||||
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.infer)(expr);
|
let ty = &ctx.expr_ty(expr);
|
||||||
let ev = eval_const(&ctx.exprs[expr], ctx)?;
|
let ev = eval_const(expr, ctx)?;
|
||||||
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 => {
|
||||||
|
@ -203,9 +214,9 @@ pub fn eval_const(expr: &Expr, ctx: &mut ConstEvalCtx<'_>) -> Result<ComputedExp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&Expr::BinaryOp { lhs, rhs, op } => {
|
&Expr::BinaryOp { lhs, rhs, op } => {
|
||||||
let ty = &(ctx.infer)(lhs);
|
let ty = &ctx.expr_ty(lhs);
|
||||||
let lhs = eval_const(&ctx.exprs[lhs], ctx)?;
|
let lhs = eval_const(lhs, ctx)?;
|
||||||
let rhs = eval_const(&ctx.exprs[rhs], ctx)?;
|
let rhs = eval_const(rhs, ctx)?;
|
||||||
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,
|
||||||
|
@ -249,7 +260,7 @@ pub fn eval_const(expr: &Expr, ctx: &mut ConstEvalCtx<'_>) -> Result<ComputedExp
|
||||||
}
|
}
|
||||||
Ok(ComputedExpr::Literal(Literal::Int(r, None)))
|
Ok(ComputedExpr::Literal(Literal::Int(r, None)))
|
||||||
}
|
}
|
||||||
BinaryOp::LogicOp(_) => Err(ConstEvalError::TypeError),
|
BinaryOp::LogicOp(_) => Err(ConstEvalError::SemanticError("logic op on numbers")),
|
||||||
_ => Err(ConstEvalError::NotSupported("bin op on this operators")),
|
_ => Err(ConstEvalError::NotSupported("bin op on this operators")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -266,7 +277,7 @@ pub fn eval_const(expr: &Expr, ctx: &mut ConstEvalCtx<'_>) -> Result<ComputedExp
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let value = match initializer {
|
let value = match initializer {
|
||||||
Some(x) => eval_const(&ctx.exprs[x], ctx)?,
|
Some(x) => eval_const(x, ctx)?,
|
||||||
None => continue,
|
None => continue,
|
||||||
};
|
};
|
||||||
if !prev_values.contains_key(&name) {
|
if !prev_values.contains_key(&name) {
|
||||||
|
@ -282,7 +293,7 @@ pub fn eval_const(expr: &Expr, ctx: &mut ConstEvalCtx<'_>) -> Result<ComputedExp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let r = match tail {
|
let r = match tail {
|
||||||
&Some(x) => eval_const(&ctx.exprs[x], ctx),
|
&Some(x) => eval_const(x, ctx),
|
||||||
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
|
||||||
|
@ -295,19 +306,47 @@ pub fn eval_const(expr: &Expr, ctx: &mut ConstEvalCtx<'_>) -> Result<ComputedExp
|
||||||
r
|
r
|
||||||
}
|
}
|
||||||
Expr::Path(p) => {
|
Expr::Path(p) => {
|
||||||
let name = p.mod_path().as_ident().ok_or(ConstEvalError::NotSupported("big paths"))?;
|
let resolver = resolver_for_expr(ctx.db.upcast(), ctx.owner, expr_id);
|
||||||
let r = ctx
|
let pr = resolver
|
||||||
.local_data
|
.resolve_path_in_value_ns(ctx.db.upcast(), p.mod_path())
|
||||||
.get(name)
|
.ok_or(ConstEvalError::SemanticError("unresolved path"))?;
|
||||||
.ok_or(ConstEvalError::NotSupported("Non local name resolution"))?;
|
let pr = match pr {
|
||||||
Ok(r.clone())
|
ResolveValueResult::ValueNs(v) => v,
|
||||||
|
ResolveValueResult::Partial(..) => {
|
||||||
|
return match ctx
|
||||||
|
.infer
|
||||||
|
.assoc_resolutions_for_expr(expr_id)
|
||||||
|
.ok_or(ConstEvalError::SemanticError("unresolved assoc item"))?
|
||||||
|
{
|
||||||
|
hir_def::AssocItemId::FunctionId(_) => {
|
||||||
|
Err(ConstEvalError::NotSupported("assoc function"))
|
||||||
|
}
|
||||||
|
hir_def::AssocItemId::ConstId(c) => ctx.db.const_eval(c),
|
||||||
|
hir_def::AssocItemId::TypeAliasId(_) => {
|
||||||
|
Err(ConstEvalError::NotSupported("assoc type alias"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
match pr {
|
||||||
|
ValueNs::LocalBinding(_) => {
|
||||||
|
let name =
|
||||||
|
p.mod_path().as_ident().ok_or(ConstEvalError::NotSupported("big paths"))?;
|
||||||
|
let r = ctx
|
||||||
|
.local_data
|
||||||
|
.get(name)
|
||||||
|
.ok_or(ConstEvalError::NotSupported("Unexpected missing local"))?;
|
||||||
|
Ok(r.clone())
|
||||||
|
}
|
||||||
|
ValueNs::ConstId(id) => ctx.db.const_eval(id),
|
||||||
|
_ => Err(ConstEvalError::NotSupported("path that are not const or local")),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => Err(ConstEvalError::NotSupported("This kind of expression")),
|
_ => Err(ConstEvalError::NotSupported("This kind of expression")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eval_usize(expr: Idx<Expr>, mut ctx: ConstEvalCtx<'_>) -> Option<u64> {
|
pub fn eval_usize(expr: Idx<Expr>, mut ctx: ConstEvalCtx<'_>) -> Option<u64> {
|
||||||
let expr = &ctx.exprs[expr];
|
|
||||||
if let Ok(ce) = eval_const(expr, &mut ctx) {
|
if let Ok(ce) = eval_const(expr, &mut ctx) {
|
||||||
match ce {
|
match ce {
|
||||||
ComputedExpr::Literal(Literal::Int(x, _)) => return x.try_into().ok(),
|
ComputedExpr::Literal(Literal::Int(x, _)) => return x.try_into().ok(),
|
||||||
|
@ -380,10 +419,39 @@ pub fn usize_const(value: Option<u64>) -> Const {
|
||||||
.intern(Interner)
|
.intern(Interner)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn eval_to_const(
|
pub(crate) fn const_eval_recover(
|
||||||
|
_: &dyn HirDatabase,
|
||||||
|
_: &[String],
|
||||||
|
_: &ConstId,
|
||||||
|
) -> Result<ComputedExpr, ConstEvalError> {
|
||||||
|
Err(ConstEvalError::Loop)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn const_eval_query(
|
||||||
|
db: &dyn HirDatabase,
|
||||||
|
const_id: ConstId,
|
||||||
|
) -> Result<ComputedExpr, ConstEvalError> {
|
||||||
|
let def = const_id.into();
|
||||||
|
let body = db.body(def);
|
||||||
|
let mut infer = db.infer_query(def);
|
||||||
|
let result = eval_const(
|
||||||
|
body.body_expr,
|
||||||
|
&mut ConstEvalCtx {
|
||||||
|
db,
|
||||||
|
owner: const_id.into(),
|
||||||
|
exprs: &body.exprs,
|
||||||
|
pats: &body.pats,
|
||||||
|
local_data: HashMap::default(),
|
||||||
|
infer: &mut infer,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn eval_to_const<'a>(
|
||||||
expr: Idx<Expr>,
|
expr: Idx<Expr>,
|
||||||
mode: ParamLoweringMode,
|
mode: ParamLoweringMode,
|
||||||
ctx: &mut InferenceContext,
|
ctx: &mut InferenceContext<'a>,
|
||||||
args: impl FnOnce() -> Generics,
|
args: impl FnOnce() -> Generics,
|
||||||
debruijn: DebruijnIndex,
|
debruijn: DebruijnIndex,
|
||||||
) -> Const {
|
) -> Const {
|
||||||
|
@ -396,10 +464,12 @@ pub(crate) fn eval_to_const(
|
||||||
}
|
}
|
||||||
let body = ctx.body.clone();
|
let body = ctx.body.clone();
|
||||||
let ctx = ConstEvalCtx {
|
let ctx = ConstEvalCtx {
|
||||||
|
db: ctx.db,
|
||||||
|
owner: ctx.owner,
|
||||||
exprs: &body.exprs,
|
exprs: &body.exprs,
|
||||||
pats: &body.pats,
|
pats: &body.pats,
|
||||||
local_data: HashMap::default(),
|
local_data: HashMap::default(),
|
||||||
infer: &mut |x| ctx.infer_expr(x, &Expectation::None),
|
infer: &ctx.result,
|
||||||
};
|
};
|
||||||
usize_const(eval_usize(expr, ctx))
|
usize_const(eval_usize(expr, ctx))
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,13 +5,14 @@ use std::sync::Arc;
|
||||||
|
|
||||||
use base_db::{impl_intern_key, salsa, CrateId, Upcast};
|
use base_db::{impl_intern_key, salsa, CrateId, Upcast};
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
db::DefDatabase, expr::ExprId, BlockId, ConstParamId, DefWithBodyId, FunctionId, GenericDefId,
|
db::DefDatabase, expr::ExprId, BlockId, ConstId, ConstParamId, DefWithBodyId, FunctionId,
|
||||||
ImplId, LifetimeParamId, LocalFieldId, TypeOrConstParamId, VariantId,
|
GenericDefId, ImplId, LifetimeParamId, LocalFieldId, TypeOrConstParamId, VariantId,
|
||||||
};
|
};
|
||||||
use la_arena::ArenaMap;
|
use la_arena::ArenaMap;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
chalk_db,
|
chalk_db,
|
||||||
|
consteval::{ComputedExpr, ConstEvalError},
|
||||||
method_resolution::{InherentImpls, TraitImpls},
|
method_resolution::{InherentImpls, TraitImpls},
|
||||||
Binders, CallableDefId, FnDefId, GenericArg, ImplTraitId, InferenceResult, Interner, PolyFnSig,
|
Binders, CallableDefId, FnDefId, GenericArg, ImplTraitId, InferenceResult, Interner, PolyFnSig,
|
||||||
QuantifiedWhereClause, ReturnTypeImplTraits, TraitRef, Ty, TyDefId, ValueTyDefId,
|
QuantifiedWhereClause, ReturnTypeImplTraits, TraitRef, Ty, TyDefId, ValueTyDefId,
|
||||||
|
@ -41,6 +42,10 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
|
||||||
#[salsa::invoke(crate::lower::const_param_ty_query)]
|
#[salsa::invoke(crate::lower::const_param_ty_query)]
|
||||||
fn const_param_ty(&self, def: ConstParamId) -> Ty;
|
fn const_param_ty(&self, def: ConstParamId) -> Ty;
|
||||||
|
|
||||||
|
#[salsa::invoke(crate::consteval::const_eval_query)]
|
||||||
|
#[salsa::cycle(crate::consteval::const_eval_recover)]
|
||||||
|
fn const_eval(&self, def: ConstId) -> Result<ComputedExpr, ConstEvalError>;
|
||||||
|
|
||||||
#[salsa::invoke(crate::lower::impl_trait_query)]
|
#[salsa::invoke(crate::lower::impl_trait_query)]
|
||||||
fn impl_trait(&self, def: ImplId) -> Option<Binders<TraitRef>>;
|
fn impl_trait(&self, def: ImplId) -> Option<Binders<TraitRef>>;
|
||||||
|
|
||||||
|
|
|
@ -358,12 +358,12 @@ impl Index<PatId> for InferenceResult {
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) struct InferenceContext<'a> {
|
pub(crate) struct InferenceContext<'a> {
|
||||||
pub(crate) db: &'a dyn HirDatabase,
|
pub(crate) db: &'a dyn HirDatabase,
|
||||||
owner: DefWithBodyId,
|
pub(crate) owner: DefWithBodyId,
|
||||||
pub(crate) body: Arc<Body>,
|
pub(crate) body: Arc<Body>,
|
||||||
pub(crate) resolver: Resolver,
|
pub(crate) resolver: Resolver,
|
||||||
table: unify::InferenceTable<'a>,
|
table: unify::InferenceTable<'a>,
|
||||||
trait_env: Arc<TraitEnvironment>,
|
trait_env: Arc<TraitEnvironment>,
|
||||||
result: InferenceResult,
|
pub(crate) result: InferenceResult,
|
||||||
/// The return type of the function being inferred, the closure or async block if we're
|
/// The return type of the function being inferred, the closure or async block if we're
|
||||||
/// currently within one.
|
/// currently within one.
|
||||||
///
|
///
|
||||||
|
|
|
@ -3399,6 +3399,7 @@ impl<const LEN: usize> Foo<LEN$0> {}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: move these tests to consteval module
|
||||||
#[test]
|
#[test]
|
||||||
fn hover_const_eval() {
|
fn hover_const_eval() {
|
||||||
check(
|
check(
|
||||||
|
@ -3577,6 +3578,90 @@ const FOO$0: &str = "bar";
|
||||||
This is a doc
|
This is a doc
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
const F1: i32 = 1;
|
||||||
|
const F$03: i32 = 3 * F2;
|
||||||
|
const F2: i32 = 2 * F1;
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
*F3*
|
||||||
|
|
||||||
|
```rust
|
||||||
|
test
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
const F3: i32 = 6
|
||||||
|
```
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
const F1: i32 = 1 * F3;
|
||||||
|
const F2: i32 = 2 * F1;
|
||||||
|
const F$03: i32 = 3 * F2;
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
*F3*
|
||||||
|
|
||||||
|
```rust
|
||||||
|
test
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
const F3: i32 = 3 * F2
|
||||||
|
```
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
struct U5;
|
||||||
|
impl U5 {
|
||||||
|
const VAL: usize = 5;
|
||||||
|
}
|
||||||
|
const X$0X: usize = U5::VAL;
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
*XX*
|
||||||
|
|
||||||
|
```rust
|
||||||
|
test
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
const XX: usize = 5
|
||||||
|
```
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: this should evaluate to zero
|
||||||
|
#[test]
|
||||||
|
fn hover_const_eval_broken() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
struct U0;
|
||||||
|
trait ToConst {
|
||||||
|
const VAL: usize;
|
||||||
|
}
|
||||||
|
impl ToConst for U0 {
|
||||||
|
const VAL: usize = 0;
|
||||||
|
}
|
||||||
|
const X$0X: usize = U0::VAL;
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
*XX*
|
||||||
|
|
||||||
|
```rust
|
||||||
|
test
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
const XX: usize = U0::VAL
|
||||||
|
```
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue