feat: support negative const generic parameters

* feat: support `bool` & `char` const generics
This commit is contained in:
Artur Sinila 2022-07-17 04:18:53 +03:00
parent b9b42e8670
commit a96f0aa7cd
No known key found for this signature in database
GPG key ID: 770E69D690735C61
15 changed files with 153 additions and 126 deletions

View file

@ -238,7 +238,7 @@ pub struct Mark {
} }
/// The body of an item (function, const etc.). /// The body of an item (function, const etc.).
#[derive(Debug, Eq, PartialEq)] #[derive(Debug, Default, Eq, PartialEq)]
pub struct Body { pub struct Body {
pub exprs: Arena<Expr>, pub exprs: Arena<Expr>,
pub pats: Arena<Pat>, pub pats: Arena<Pat>,

View file

@ -1,7 +1,7 @@
//! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr` //! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr`
//! representation. //! representation.
use std::{mem, sync::Arc}; use std::{collections::HashMap, mem, sync::Arc};
use either::Either; use either::Either;
use hir_expand::{ use hir_expand::{
@ -10,8 +10,6 @@ use hir_expand::{
name::{name, AsName, Name}, name::{name, AsName, Name},
ExpandError, HirFileId, InFile, ExpandError, HirFileId, InFile,
}; };
use la_arena::Arena;
use profile::Count;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use syntax::{ use syntax::{
ast::{ ast::{
@ -28,8 +26,8 @@ use crate::{
builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint}, builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
db::DefDatabase, db::DefDatabase,
expr::{ expr::{
dummy_expr_id, Array, BindingAnnotation, Expr, ExprId, FloatTypeWrapper, Label, LabelId, Array, BindingAnnotation, Expr, ExprId, FloatTypeWrapper, Label, LabelId, Literal,
Literal, MatchArm, Pat, PatId, RecordFieldPat, RecordLitField, Statement, MatchArm, Pat, PatId, RecordFieldPat, RecordLitField, Statement,
}, },
intern::Interned, intern::Interned,
item_scope::BuiltinShadowMode, item_scope::BuiltinShadowMode,
@ -82,24 +80,8 @@ pub(super) fn lower(
params: Option<ast::ParamList>, params: Option<ast::ParamList>,
body: Option<ast::Expr>, body: Option<ast::Expr>,
) -> (Body, BodySourceMap) { ) -> (Body, BodySourceMap) {
ExprCollector { let collector = ExprCollector::new(db, expander);
db, collector.collect(params, body)
source_map: BodySourceMap::default(),
body: Body {
exprs: Arena::default(),
pats: Arena::default(),
labels: Arena::default(),
params: Vec::new(),
body_expr: dummy_expr_id(),
block_scopes: Vec::new(),
_c: Count::new(),
or_pats: Default::default(),
},
expander,
name_to_pat_grouping: Default::default(),
is_lowering_inside_or_pat: false,
}
.collect(params, body)
} }
struct ExprCollector<'a> { struct ExprCollector<'a> {
@ -112,7 +94,18 @@ struct ExprCollector<'a> {
is_lowering_inside_or_pat: bool, is_lowering_inside_or_pat: bool,
} }
impl ExprCollector<'_> { impl<'a> ExprCollector<'a> {
pub(crate) fn new(db: &'a dyn DefDatabase, expander: Expander) -> Self {
Self {
db,
expander,
body: Body::default(),
source_map: BodySourceMap::default(),
name_to_pat_grouping: HashMap::default(),
is_lowering_inside_or_pat: false,
}
}
fn collect( fn collect(
mut self, mut self,
param_list: Option<ast::ParamList>, param_list: Option<ast::ParamList>,
@ -197,7 +190,8 @@ impl ExprCollector<'_> {
} }
fn collect_expr(&mut self, expr: ast::Expr) -> ExprId { fn collect_expr(&mut self, expr: ast::Expr) -> ExprId {
self.maybe_collect_expr(expr).unwrap_or_else(|| self.missing_expr()) let expr_id = self.maybe_collect_expr(expr).unwrap_or_else(|| self.missing_expr());
expr_id
} }
/// Returns `None` if and only if the expression is `#[cfg]`d out. /// Returns `None` if and only if the expression is `#[cfg]`d out.
@ -689,7 +683,6 @@ impl ExprCollector<'_> {
}; };
let prev_def_map = mem::replace(&mut self.expander.def_map, def_map); let prev_def_map = mem::replace(&mut self.expander.def_map, def_map);
let prev_local_module = mem::replace(&mut self.expander.module, module); let prev_local_module = mem::replace(&mut self.expander.module, module);
let mut statements: Vec<_> = let mut statements: Vec<_> =
block.statements().filter_map(|s| self.collect_stmt(s)).collect(); block.statements().filter_map(|s| self.collect_stmt(s)).collect();
let tail = block.tail_expr().and_then(|e| self.maybe_collect_expr(e)); let tail = block.tail_expr().and_then(|e| self.maybe_collect_expr(e));

View file

@ -13,7 +13,7 @@
//! See also a neighboring `body` module. //! See also a neighboring `body` module.
use hir_expand::name::Name; use hir_expand::name::Name;
use la_arena::{Idx, RawIdx}; use la_arena::Idx;
use crate::{ use crate::{
builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint}, builtin_type::{BuiltinFloat, BuiltinInt, BuiltinUint},
@ -26,9 +26,6 @@ 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>;
pub(crate) fn dummy_expr_id() -> ExprId {
ExprId::from_raw(RawIdx::from(!0))
}
pub type PatId = Idx<Pat>; pub type PatId = Idx<Pat>;

View file

@ -1,6 +1,9 @@
//! Transforms syntax into `Path` objects, ideally with accounting for hygiene //! Transforms syntax into `Path` objects, ideally with accounting for hygiene
use crate::{intern::Interned, type_ref::ConstScalarOrPath}; use crate::{
intern::Interned,
type_ref::{ConstScalar, ConstScalarOrPath},
};
use either::Either; use either::Either;
use hir_expand::name::{name, AsName}; use hir_expand::name::{name, AsName};
@ -181,7 +184,10 @@ pub(super) fn lower_generic_args(
} }
} }
ast::GenericArg::ConstArg(arg) => { ast::GenericArg::ConstArg(arg) => {
let arg = ConstScalarOrPath::from_expr_opt(arg.expr()); let arg = arg.expr().map_or(
ConstScalarOrPath::Scalar(ConstScalar::Unknown),
ConstScalarOrPath::from_expr,
);
args.push(GenericArg::Const(arg)) args.push(GenericArg::Const(arg))
} }
} }

View file

@ -5,10 +5,15 @@ 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 {
@ -177,7 +182,10 @@ impl TypeRef {
// `hir_def::body::lower` to lower this into an `Expr` and then evaluate it at the // `hir_def::body::lower` to lower this into an `Expr` and then evaluate it at the
// `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 = inner.expr().map_or(
ConstScalarOrPath::Scalar(ConstScalar::Unknown),
ConstScalarOrPath::from_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)
} }
@ -386,16 +394,9 @@ impl std::fmt::Display for ConstScalarOrPath {
} }
impl ConstScalarOrPath { impl ConstScalarOrPath {
pub(crate) fn from_expr_opt(expr: Option<ast::Expr>) -> Self {
match expr {
Some(x) => Self::from_expr(x),
None => Self::Scalar(ConstScalar::Unknown),
}
}
// FIXME: as per the comments on `TypeRef::Array`, this evaluation should not happen at this // FIXME: as per the comments on `TypeRef::Array`, this evaluation should not happen at this
// parse stage. // parse stage.
fn from_expr(expr: ast::Expr) -> Self { pub(crate) fn from_expr(expr: ast::Expr) -> Self {
match expr { match expr {
ast::Expr::PathExpr(p) => { ast::Expr::PathExpr(p) => {
match p.path().and_then(|x| x.segment()).and_then(|x| x.name_ref()) { match p.path().and_then(|x| x.segment()).and_then(|x| x.name_ref()) {
@ -403,22 +404,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 +437,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 +450,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_str("{unknown}"),
} }
} }
} }

View file

@ -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,19 +395,24 @@ 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_with_type(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_with_type(
value.map(ConstScalar::Usize).unwrap_or(ConstScalar::Unknown), value.map(ConstScalar::UInt).unwrap_or(ConstScalar::Unknown),
TyBuilder::usize(), TyBuilder::usize(),
) )
} }
/// Interns a constant scalar with the default type
pub fn intern_const_scalar(value: ConstScalar) -> Const {
intern_const_scalar_with_type(value, TyBuilder::builtin(value.builtin_type()))
}
pub(crate) fn const_eval_recover( pub(crate) fn const_eval_recover(
_: &dyn HirDatabase, _: &dyn HirDatabase,
_: &[String], _: &[String],
@ -463,7 +457,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 +465,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_with_type(const_scalar, TyBuilder::usize())
} }
#[cfg(test)] #[cfg(test)]

View file

@ -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,
} }

View file

@ -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)

View file

@ -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,18 @@ 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)),
),
)
} }
_ => TyKind::Slice(elem_ty.clone()), _ => TyKind::Slice(elem_ty.clone()),
} }

View file

@ -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 == 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(

View file

@ -44,7 +44,9 @@ 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_with_type, path_to_const, unknown_const, unknown_const_as_generic,
},
db::HirDatabase, db::HirDatabase,
make_binders, make_binders,
mapping::ToChalk, mapping::ToChalk,
@ -1742,7 +1744,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_with_type(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)

View file

@ -3011,14 +3011,14 @@ struct TS(usize);
fn main() { fn main() {
let x; let x;
[x,] = &[1,]; [x,] = &[1,];
//^^^^expected &[i32; 1], got [{unknown}; _] //^^^^expected &[i32; 1], got [{unknown}; {unknown}]
// FIXME we only want the outermost error, but this matches the current // FIXME we only want the outermost error, but this matches the current
// behavior of slice patterns // behavior of slice patterns
let x; let x;
[(x,),] = &[(1,),]; [(x,),] = &[(1,),];
// ^^^^expected {unknown}, got ({unknown},) // ^^^^expected {unknown}, got ({unknown},)
//^^^^^^^expected &[(i32,); 1], got [{unknown}; _] //^^^^^^^expected &[(i32,); 1], got [{unknown}; {unknown}]
let x; let x;
((x,),) = &((1,),); ((x,),) = &((1,),);

View file

@ -328,7 +328,7 @@ fn div(x: i32, y: i32) -> Option<i32> {
} }
fn main() { fn main() {
run(f()) // FIXME: remove this error run(f()) // FIXME: remove this error
//^^^ error: expected Rate<5>, found Rate<_> //^^^ error: expected Rate<5>, found Rate<{unknown}>
} }
"#, "#,
); );

View file

@ -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(

View file

@ -17,6 +17,12 @@ pub use map::ArenaMap;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct RawIdx(u32); pub struct RawIdx(u32);
impl Default for RawIdx {
fn default() -> Self {
Self(u32::MAX)
}
}
impl From<RawIdx> for u32 { impl From<RawIdx> for u32 {
fn from(raw: RawIdx) -> u32 { fn from(raw: RawIdx) -> u32 {
raw.0 raw.0
@ -47,6 +53,12 @@ pub struct Idx<T> {
_ty: PhantomData<fn() -> T>, _ty: PhantomData<fn() -> T>,
} }
impl<T> Default for Idx<T> {
fn default() -> Self {
Self::from_raw(RawIdx::default())
}
}
impl<T> Clone for Idx<T> { impl<T> Clone for Idx<T> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
*self *self