mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-14 14:13:58 +00:00
Auto merge of #12778 - Logarithmus:feature/fix-negative-const-generics, r=flodiebold
Support negative, `char` & `bool` const generics Before: ![Before](https://user-images.githubusercontent.com/29541480/179379832-0c3b2a74-fef6-427e-b89f-7e31d9c37b3d.png) After: ![After](https://user-images.githubusercontent.com/29541480/179379863-b62475dd-e7bf-41f2-b437-08dfe55951af.png) I tried to implement stuff like `Const<{NUM1 + 3 + NUM2}>` by using already existing constant evaluation mechanism for ordinary constants, but turned out to be harder than I thought, maybe because I've never ever tinkered with compilers before
This commit is contained in:
commit
db6a85d358
11 changed files with 218 additions and 86 deletions
|
@ -24,7 +24,7 @@ use syntax::{ast, AstNode, AstPtr, SyntaxNodePtr};
|
||||||
use crate::{
|
use crate::{
|
||||||
attr::{Attrs, RawAttrs},
|
attr::{Attrs, RawAttrs},
|
||||||
db::DefDatabase,
|
db::DefDatabase,
|
||||||
expr::{Expr, ExprId, Label, LabelId, Pat, PatId},
|
expr::{dummy_expr_id, Expr, ExprId, Label, LabelId, Pat, PatId},
|
||||||
item_scope::BuiltinShadowMode,
|
item_scope::BuiltinShadowMode,
|
||||||
macro_id_to_def_id,
|
macro_id_to_def_id,
|
||||||
nameres::DefMap,
|
nameres::DefMap,
|
||||||
|
@ -389,6 +389,21 @@ impl Body {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for Body {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
body_expr: dummy_expr_id(),
|
||||||
|
exprs: Default::default(),
|
||||||
|
pats: Default::default(),
|
||||||
|
or_pats: Default::default(),
|
||||||
|
labels: Default::default(),
|
||||||
|
params: Default::default(),
|
||||||
|
block_scopes: Default::default(),
|
||||||
|
_c: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Index<ExprId> for Body {
|
impl Index<ExprId> for Body {
|
||||||
type Output = Expr;
|
type Output = Expr;
|
||||||
|
|
||||||
|
|
|
@ -26,8 +26,10 @@ use crate::{
|
||||||
pub use syntax::ast::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp};
|
pub use syntax::ast::{ArithOp, BinaryOp, CmpOp, LogicOp, Ordering, RangeOp, UnaryOp};
|
||||||
|
|
||||||
pub type ExprId = Idx<Expr>;
|
pub type ExprId = Idx<Expr>;
|
||||||
|
|
||||||
|
/// FIXME: this is a hacky function which should be removed
|
||||||
pub(crate) fn dummy_expr_id() -> ExprId {
|
pub(crate) fn dummy_expr_id() -> ExprId {
|
||||||
ExprId::from_raw(RawIdx::from(!0))
|
ExprId::from_raw(RawIdx::from(u32::MAX))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type PatId = Idx<Pat>;
|
pub type PatId = Idx<Pat>;
|
||||||
|
|
|
@ -1,14 +1,21 @@
|
||||||
//! HIR for references to types. Paths in these are not yet resolved. They can
|
//! HIR for references to types. Paths in these are not yet resolved. They can
|
||||||
//! be directly created from an ast::TypeRef, without further queries.
|
//! be directly created from an ast::TypeRef, without further queries.
|
||||||
|
|
||||||
|
use std::fmt::Write;
|
||||||
|
|
||||||
use hir_expand::{
|
use hir_expand::{
|
||||||
name::{AsName, Name},
|
name::{AsName, Name},
|
||||||
AstId, InFile,
|
AstId, InFile,
|
||||||
};
|
};
|
||||||
use std::{convert::TryInto, fmt::Write};
|
|
||||||
use syntax::ast::{self, HasName};
|
use syntax::ast::{self, HasName};
|
||||||
|
|
||||||
use crate::{body::LowerCtx, intern::Interned, path::Path};
|
use crate::{
|
||||||
|
body::LowerCtx,
|
||||||
|
builtin_type::{BuiltinInt, BuiltinType, BuiltinUint},
|
||||||
|
expr::Literal,
|
||||||
|
intern::Interned,
|
||||||
|
path::Path,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
pub enum Mutability {
|
pub enum Mutability {
|
||||||
|
@ -178,7 +185,6 @@ impl TypeRef {
|
||||||
// `hir_ty` level, which would allow knowing the type of:
|
// `hir_ty` level, which would allow knowing the type of:
|
||||||
// let v: [u8; 2 + 2] = [0u8; 4];
|
// let v: [u8; 2 + 2] = [0u8; 4];
|
||||||
let len = ConstScalarOrPath::from_expr_opt(inner.expr());
|
let len = ConstScalarOrPath::from_expr_opt(inner.expr());
|
||||||
|
|
||||||
TypeRef::Array(Box::new(TypeRef::from_ast_opt(ctx, inner.ty())), len)
|
TypeRef::Array(Box::new(TypeRef::from_ast_opt(ctx, inner.ty())), len)
|
||||||
}
|
}
|
||||||
ast::Type::SliceType(inner) => {
|
ast::Type::SliceType(inner) => {
|
||||||
|
@ -403,22 +409,31 @@ impl ConstScalarOrPath {
|
||||||
None => Self::Scalar(ConstScalar::Unknown),
|
None => Self::Scalar(ConstScalar::Unknown),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::Expr::Literal(lit) => {
|
ast::Expr::PrefixExpr(prefix_expr) => match prefix_expr.op_kind() {
|
||||||
let lkind = lit.kind();
|
Some(ast::UnaryOp::Neg) => {
|
||||||
match lkind {
|
let unsigned = prefix_expr
|
||||||
ast::LiteralKind::IntNumber(num)
|
.expr()
|
||||||
if num.suffix() == None || num.suffix() == Some("usize") =>
|
.map_or(Self::Scalar(ConstScalar::Unknown), Self::from_expr);
|
||||||
{
|
// Add sign
|
||||||
Self::Scalar(
|
match unsigned {
|
||||||
num.value()
|
Self::Scalar(ConstScalar::UInt(num)) => {
|
||||||
.and_then(|v| v.try_into().ok())
|
Self::Scalar(ConstScalar::Int(-(num as i128)))
|
||||||
.map(ConstScalar::Usize)
|
}
|
||||||
.unwrap_or(ConstScalar::Unknown),
|
other => other,
|
||||||
)
|
|
||||||
}
|
}
|
||||||
_ => Self::Scalar(ConstScalar::Unknown),
|
|
||||||
}
|
}
|
||||||
}
|
_ => prefix_expr.expr().map_or(Self::Scalar(ConstScalar::Unknown), Self::from_expr),
|
||||||
|
},
|
||||||
|
ast::Expr::Literal(literal) => Self::Scalar(match literal.kind() {
|
||||||
|
ast::LiteralKind::IntNumber(num) => {
|
||||||
|
num.value().map(ConstScalar::UInt).unwrap_or(ConstScalar::Unknown)
|
||||||
|
}
|
||||||
|
ast::LiteralKind::Char(c) => {
|
||||||
|
c.value().map(ConstScalar::Char).unwrap_or(ConstScalar::Unknown)
|
||||||
|
}
|
||||||
|
ast::LiteralKind::Bool(f) => ConstScalar::Bool(f),
|
||||||
|
_ => ConstScalar::Unknown,
|
||||||
|
}),
|
||||||
_ => Self::Scalar(ConstScalar::Unknown),
|
_ => Self::Scalar(ConstScalar::Unknown),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -427,9 +442,10 @@ impl ConstScalarOrPath {
|
||||||
/// A concrete constant value
|
/// A concrete constant value
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub enum ConstScalar {
|
pub enum ConstScalar {
|
||||||
// for now, we only support the trivial case of constant evaluating the length of an array
|
Int(i128),
|
||||||
// Note that this is u64 because the target usize may be bigger than our usize
|
UInt(u128),
|
||||||
Usize(u64),
|
Bool(bool),
|
||||||
|
Char(char),
|
||||||
|
|
||||||
/// Case of an unknown value that rustc might know but we don't
|
/// Case of an unknown value that rustc might know but we don't
|
||||||
// FIXME: this is a hack to get around chalk not being able to represent unevaluatable
|
// FIXME: this is a hack to get around chalk not being able to represent unevaluatable
|
||||||
|
@ -439,21 +455,37 @@ pub enum ConstScalar {
|
||||||
Unknown,
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for ConstScalar {
|
impl ConstScalar {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
pub fn builtin_type(&self) -> BuiltinType {
|
||||||
match self {
|
match self {
|
||||||
ConstScalar::Usize(us) => us.fmt(f),
|
ConstScalar::UInt(_) | ConstScalar::Unknown => BuiltinType::Uint(BuiltinUint::U128),
|
||||||
ConstScalar::Unknown => f.write_char('_'),
|
ConstScalar::Int(_) => BuiltinType::Int(BuiltinInt::I128),
|
||||||
|
ConstScalar::Char(_) => BuiltinType::Char,
|
||||||
|
ConstScalar::Bool(_) => BuiltinType::Bool,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConstScalar {
|
impl From<Literal> for ConstScalar {
|
||||||
/// Gets a target usize out of the ConstScalar
|
fn from(literal: Literal) -> Self {
|
||||||
pub fn as_usize(&self) -> Option<u64> {
|
match literal {
|
||||||
match self {
|
Literal::Char(c) => Self::Char(c),
|
||||||
&ConstScalar::Usize(us) => Some(us),
|
Literal::Bool(flag) => Self::Bool(flag),
|
||||||
_ => None,
|
Literal::Int(num, _) => Self::Int(num),
|
||||||
|
Literal::Uint(num, _) => Self::UInt(num),
|
||||||
|
_ => Self::Unknown,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for ConstScalar {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||||
|
match self {
|
||||||
|
ConstScalar::Int(num) => num.fmt(f),
|
||||||
|
ConstScalar::UInt(num) => num.fmt(f),
|
||||||
|
ConstScalar::Bool(flag) => flag.fmt(f),
|
||||||
|
ConstScalar::Char(c) => write!(f, "'{c}'"),
|
||||||
|
ConstScalar::Unknown => f.write_char('_'),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -347,17 +347,6 @@ pub fn eval_const(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eval_usize(expr: Idx<Expr>, mut ctx: ConstEvalCtx<'_>) -> Option<u64> {
|
|
||||||
if let Ok(ce) = eval_const(expr, &mut ctx) {
|
|
||||||
match ce {
|
|
||||||
ComputedExpr::Literal(Literal::Int(x, _)) => return x.try_into().ok(),
|
|
||||||
ComputedExpr::Literal(Literal::Uint(x, _)) => return x.try_into().ok(),
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn path_to_const(
|
pub(crate) fn path_to_const(
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
resolver: &Resolver,
|
resolver: &Resolver,
|
||||||
|
@ -406,17 +395,14 @@ pub fn unknown_const_as_generic(ty: Ty) -> GenericArg {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Interns a constant scalar with the given type
|
/// Interns a constant scalar with the given type
|
||||||
pub fn intern_scalar_const(value: ConstScalar, ty: Ty) -> Const {
|
pub fn intern_const_scalar(value: ConstScalar, ty: Ty) -> Const {
|
||||||
ConstData { ty, value: ConstValue::Concrete(chalk_ir::ConcreteConst { interned: value }) }
|
ConstData { ty, value: ConstValue::Concrete(chalk_ir::ConcreteConst { interned: value }) }
|
||||||
.intern(Interner)
|
.intern(Interner)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Interns a possibly-unknown target usize
|
/// Interns a possibly-unknown target usize
|
||||||
pub fn usize_const(value: Option<u64>) -> Const {
|
pub fn usize_const(value: Option<u128>) -> Const {
|
||||||
intern_scalar_const(
|
intern_const_scalar(value.map_or(ConstScalar::Unknown, ConstScalar::UInt), TyBuilder::usize())
|
||||||
value.map(ConstScalar::Usize).unwrap_or(ConstScalar::Unknown),
|
|
||||||
TyBuilder::usize(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn const_eval_recover(
|
pub(crate) fn const_eval_recover(
|
||||||
|
@ -463,7 +449,7 @@ pub(crate) fn eval_to_const<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let body = ctx.body.clone();
|
let body = ctx.body.clone();
|
||||||
let ctx = ConstEvalCtx {
|
let mut ctx = ConstEvalCtx {
|
||||||
db: ctx.db,
|
db: ctx.db,
|
||||||
owner: ctx.owner,
|
owner: ctx.owner,
|
||||||
exprs: &body.exprs,
|
exprs: &body.exprs,
|
||||||
|
@ -471,7 +457,12 @@ pub(crate) fn eval_to_const<'a>(
|
||||||
local_data: HashMap::default(),
|
local_data: HashMap::default(),
|
||||||
infer: &ctx.result,
|
infer: &ctx.result,
|
||||||
};
|
};
|
||||||
usize_const(eval_usize(expr, ctx))
|
let computed_expr = eval_const(expr, &mut ctx);
|
||||||
|
let const_scalar = match computed_expr {
|
||||||
|
Ok(ComputedExpr::Literal(literal)) => literal.into(),
|
||||||
|
_ => ConstScalar::Unknown,
|
||||||
|
};
|
||||||
|
intern_const_scalar(const_scalar, TyBuilder::usize())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -605,10 +605,10 @@ impl<'a> InferenceContext<'a> {
|
||||||
let data = c.data(Interner);
|
let data = c.data(Interner);
|
||||||
match data.value {
|
match data.value {
|
||||||
ConstValue::Concrete(cc) => match cc.interned {
|
ConstValue::Concrete(cc) => match cc.interned {
|
||||||
hir_def::type_ref::ConstScalar::Usize(_) => c,
|
|
||||||
hir_def::type_ref::ConstScalar::Unknown => {
|
hir_def::type_ref::ConstScalar::Unknown => {
|
||||||
self.table.new_const_var(data.ty.clone())
|
self.table.new_const_var(data.ty.clone())
|
||||||
}
|
}
|
||||||
|
_ => c,
|
||||||
},
|
},
|
||||||
_ => c,
|
_ => c,
|
||||||
}
|
}
|
||||||
|
|
|
@ -729,7 +729,7 @@ impl<'a> InferenceContext<'a> {
|
||||||
let cur_elem_ty = self.infer_expr_inner(expr, &expected);
|
let cur_elem_ty = self.infer_expr_inner(expr, &expected);
|
||||||
coerce.coerce(self, Some(expr), &cur_elem_ty);
|
coerce.coerce(self, Some(expr), &cur_elem_ty);
|
||||||
}
|
}
|
||||||
consteval::usize_const(Some(items.len() as u64))
|
consteval::usize_const(Some(items.len() as u128))
|
||||||
}
|
}
|
||||||
&Array::Repeat { initializer, repeat } => {
|
&Array::Repeat { initializer, repeat } => {
|
||||||
self.infer_expr_coerce(initializer, &Expectation::has_type(elem_ty));
|
self.infer_expr_coerce(initializer, &Expectation::has_type(elem_ty));
|
||||||
|
@ -766,7 +766,7 @@ impl<'a> InferenceContext<'a> {
|
||||||
Literal::ByteString(bs) => {
|
Literal::ByteString(bs) => {
|
||||||
let byte_type = TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(Interner);
|
let byte_type = TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(Interner);
|
||||||
|
|
||||||
let len = consteval::usize_const(Some(bs.len() as u64));
|
let len = consteval::usize_const(Some(bs.len() as u128));
|
||||||
|
|
||||||
let array_type = TyKind::Array(byte_type, len).intern(Interner);
|
let array_type = TyKind::Array(byte_type, len).intern(Interner);
|
||||||
TyKind::Ref(Mutability::Not, static_lifetime(), array_type).intern(Interner)
|
TyKind::Ref(Mutability::Not, static_lifetime(), array_type).intern(Interner)
|
||||||
|
|
|
@ -11,6 +11,7 @@ use hir_def::{
|
||||||
use hir_expand::name::Name;
|
use hir_expand::name::Name;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
consteval::intern_const_scalar,
|
||||||
infer::{BindingMode, Expectation, InferenceContext, TypeMismatch},
|
infer::{BindingMode, Expectation, InferenceContext, TypeMismatch},
|
||||||
lower::lower_to_chalk_mutability,
|
lower::lower_to_chalk_mutability,
|
||||||
static_lifetime, ConcreteConst, ConstValue, Interner, Substitution, Ty, TyBuilder, TyExt,
|
static_lifetime, ConcreteConst, ConstValue, Interner, Substitution, Ty, TyBuilder, TyExt,
|
||||||
|
@ -262,13 +263,19 @@ impl<'a> InferenceContext<'a> {
|
||||||
if let &Some(slice_pat_id) = slice {
|
if let &Some(slice_pat_id) = slice {
|
||||||
let rest_pat_ty = match expected.kind(Interner) {
|
let rest_pat_ty = match expected.kind(Interner) {
|
||||||
TyKind::Array(_, length) => {
|
TyKind::Array(_, length) => {
|
||||||
let length = match length.data(Interner).value {
|
let len = match length.data(Interner).value {
|
||||||
ConstValue::Concrete(ConcreteConst {
|
ConstValue::Concrete(ConcreteConst {
|
||||||
interned: ConstScalar::Usize(length),
|
interned: ConstScalar::UInt(len),
|
||||||
}) => length.checked_sub((prefix.len() + suffix.len()) as u64),
|
}) => len.checked_sub((prefix.len() + suffix.len()) as u128),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
TyKind::Array(elem_ty.clone(), crate::consteval::usize_const(length))
|
TyKind::Array(
|
||||||
|
elem_ty.clone(),
|
||||||
|
intern_const_scalar(
|
||||||
|
len.map_or(ConstScalar::Unknown, |len| ConstScalar::UInt(len)),
|
||||||
|
TyBuilder::usize(),
|
||||||
|
),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
_ => TyKind::Slice(elem_ty.clone()),
|
_ => TyKind::Slice(elem_ty.clone()),
|
||||||
}
|
}
|
||||||
|
|
|
@ -257,12 +257,7 @@ impl chalk_ir::interner::Interner for Interner {
|
||||||
c1: &Self::InternedConcreteConst,
|
c1: &Self::InternedConcreteConst,
|
||||||
c2: &Self::InternedConcreteConst,
|
c2: &Self::InternedConcreteConst,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
match (c1, c2) {
|
(c1 == &ConstScalar::Unknown) || (c2 == &ConstScalar::Unknown) || (c1 == c2)
|
||||||
(&ConstScalar::Usize(a), &ConstScalar::Usize(b)) => a == b,
|
|
||||||
// we were previously assuming this to be true, I'm not whether true or false on
|
|
||||||
// unknown values is safer.
|
|
||||||
(_, _) => true,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn intern_generic_arg(
|
fn intern_generic_arg(
|
||||||
|
|
|
@ -44,7 +44,7 @@ use syntax::{ast, SmolStr};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
all_super_traits,
|
all_super_traits,
|
||||||
consteval::{intern_scalar_const, path_to_const, unknown_const, unknown_const_as_generic},
|
consteval::{intern_const_scalar, path_to_const, unknown_const, unknown_const_as_generic},
|
||||||
db::HirDatabase,
|
db::HirDatabase,
|
||||||
make_binders,
|
make_binders,
|
||||||
mapping::ToChalk,
|
mapping::ToChalk,
|
||||||
|
@ -1743,7 +1743,7 @@ pub(crate) fn const_or_path_to_chalk(
|
||||||
debruijn: DebruijnIndex,
|
debruijn: DebruijnIndex,
|
||||||
) -> Const {
|
) -> Const {
|
||||||
match value {
|
match value {
|
||||||
ConstScalarOrPath::Scalar(s) => intern_scalar_const(s.clone(), expected_ty),
|
ConstScalarOrPath::Scalar(s) => intern_const_scalar(s.clone(), expected_ty),
|
||||||
ConstScalarOrPath::Path(n) => {
|
ConstScalarOrPath::Path(n) => {
|
||||||
let path = ModPath::from_segments(PathKind::Plain, Some(n.clone()));
|
let path = ModPath::from_segments(PathKind::Plain, Some(n.clone()));
|
||||||
path_to_const(db, resolver, &path, mode, args, debruijn)
|
path_to_const(db, resolver, &path, mode, args, debruijn)
|
||||||
|
|
|
@ -173,27 +173,17 @@ pub(crate) fn hover_for_definition(
|
||||||
Definition::BuiltinType(_) => Some(FamousDefs(sema, sema.scope(node)?.krate())),
|
Definition::BuiltinType(_) => Some(FamousDefs(sema, sema.scope(node)?.krate())),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
if let Some(markup) = render::definition(sema.db, definition, famous_defs.as_ref(), config) {
|
render::definition(sema.db, definition, famous_defs.as_ref(), config).map(|markup| {
|
||||||
let mut res = HoverResult::default();
|
HoverResult {
|
||||||
res.markup = render::process_markup(sema.db, definition, &markup, config);
|
markup: render::process_markup(sema.db, definition, &markup, config),
|
||||||
if let Some(action) = show_implementations_action(sema.db, definition) {
|
actions: show_implementations_action(sema.db, definition)
|
||||||
res.actions.push(action);
|
.into_iter()
|
||||||
|
.chain(show_fn_references_action(sema.db, definition))
|
||||||
|
.chain(runnable_action(sema, definition, file_id))
|
||||||
|
.chain(goto_type_action_for_def(sema.db, definition))
|
||||||
|
.collect(),
|
||||||
}
|
}
|
||||||
|
})
|
||||||
if let Some(action) = show_fn_references_action(sema.db, definition) {
|
|
||||||
res.actions.push(action);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(action) = runnable_action(sema, definition, file_id) {
|
|
||||||
res.actions.push(action);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(action) = goto_type_action_for_def(sema.db, definition) {
|
|
||||||
res.actions.push(action);
|
|
||||||
}
|
|
||||||
return Some(res);
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hover_ranged(
|
fn hover_ranged(
|
||||||
|
|
|
@ -2958,6 +2958,106 @@ struct S$0T<const C: usize = 1, T = Foo>(T);
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn const_generic_positive_i8_literal() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
struct Const<const N: i8>;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let v$0alue = Const::<1>;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
*value*
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let value: Const<1>
|
||||||
|
```
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn const_generic_zero_i8_literal() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
struct Const<const N: i8>;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let v$0alue = Const::<0>;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
*value*
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let value: Const<0>
|
||||||
|
```
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn const_generic_negative_i8_literal() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
struct Const<const N: i8>;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let v$0alue = Const::<-1>;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
*value*
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let value: Const<-1>
|
||||||
|
```
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn const_generic_bool_literal() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
struct Const<const F: bool>;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let v$0alue = Const::<true>;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
*value*
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let value: Const<true>
|
||||||
|
```
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn const_generic_char_literal() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
struct Const<const C: char>;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let v$0alue = Const::<'🦀'>;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
*value*
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let value: Const<'🦀'>
|
||||||
|
```
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn hover_self_param_shows_type() {
|
fn hover_self_param_shows_type() {
|
||||||
check(
|
check(
|
||||||
|
|
Loading…
Reference in a new issue