mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-13 05:38:46 +00:00
Use bitflags to compress function properties
Very minor savings, only 1 MB or so
This commit is contained in:
parent
f7e6b186e1
commit
ee4b5a34d8
11 changed files with 120 additions and 71 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -498,6 +498,7 @@ version = "0.0.0"
|
|||
dependencies = [
|
||||
"anymap",
|
||||
"base_db",
|
||||
"bitflags",
|
||||
"cfg",
|
||||
"cov-mark",
|
||||
"dashmap",
|
||||
|
|
|
@ -20,22 +20,21 @@ impl HirDisplay for Function {
|
|||
fn hir_fmt(&self, f: &mut HirFormatter) -> Result<(), HirDisplayError> {
|
||||
let data = f.db.function_data(self.id);
|
||||
write_visibility(self.module(f.db).id, self.visibility(f.db), f)?;
|
||||
let qual = &data.qualifier;
|
||||
if qual.is_default {
|
||||
if data.is_default() {
|
||||
write!(f, "default ")?;
|
||||
}
|
||||
if qual.is_const {
|
||||
if data.is_const() {
|
||||
write!(f, "const ")?;
|
||||
}
|
||||
if qual.is_async {
|
||||
if data.is_async() {
|
||||
write!(f, "async ")?;
|
||||
}
|
||||
if qual.is_unsafe {
|
||||
if data.is_unsafe() {
|
||||
write!(f, "unsafe ")?;
|
||||
}
|
||||
if let Some(abi) = &qual.abi {
|
||||
if let Some(abi) = &data.abi {
|
||||
// FIXME: String escape?
|
||||
write!(f, "extern \"{}\" ", abi)?;
|
||||
write!(f, "extern \"{}\" ", &**abi)?;
|
||||
}
|
||||
write!(f, "fn {}", data.name)?;
|
||||
|
||||
|
@ -68,7 +67,7 @@ impl HirDisplay for Function {
|
|||
write!(f, ", ")?;
|
||||
} else {
|
||||
first = false;
|
||||
if data.has_self_param {
|
||||
if data.has_self_param() {
|
||||
write_self_param(type_ref, f)?;
|
||||
continue;
|
||||
}
|
||||
|
@ -88,7 +87,7 @@ impl HirDisplay for Function {
|
|||
// `FunctionData::ret_type` will be `::core::future::Future<Output = ...>` for async fns.
|
||||
// Use ugly pattern match to strip the Future trait.
|
||||
// Better way?
|
||||
let ret_type = if !qual.is_async {
|
||||
let ret_type = if !data.is_async() {
|
||||
&data.ret_type
|
||||
} else {
|
||||
match &*data.ret_type {
|
||||
|
|
|
@ -832,7 +832,7 @@ impl Function {
|
|||
}
|
||||
|
||||
pub fn self_param(self, db: &dyn HirDatabase) -> Option<SelfParam> {
|
||||
if !db.function_data(self.id).has_self_param {
|
||||
if !db.function_data(self.id).has_self_param() {
|
||||
return None;
|
||||
}
|
||||
Some(SelfParam { func: self.id })
|
||||
|
@ -864,7 +864,7 @@ impl Function {
|
|||
}
|
||||
|
||||
pub fn is_unsafe(self, db: &dyn HirDatabase) -> bool {
|
||||
db.function_data(self.id).qualifier.is_unsafe
|
||||
db.function_data(self.id).is_unsafe()
|
||||
}
|
||||
|
||||
pub fn diagnostics(self, db: &dyn HirDatabase, sink: &mut DiagnosticSink) {
|
||||
|
@ -878,7 +878,7 @@ impl Function {
|
|||
///
|
||||
/// This is false in the case of required (not provided) trait methods.
|
||||
pub fn has_body(self, db: &dyn HirDatabase) -> bool {
|
||||
db.function_data(self.id).has_body
|
||||
db.function_data(self.id).has_body()
|
||||
}
|
||||
|
||||
/// A textual representation of the HIR of this function for debugging purposes.
|
||||
|
|
|
@ -10,6 +10,7 @@ edition = "2018"
|
|||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
bitflags = "1.2.1"
|
||||
cov-mark = { version = "1.1", features = ["thread-local"] }
|
||||
dashmap = { version = "4.0.2", features = ["raw-api"] }
|
||||
log = "0.4.8"
|
||||
|
|
|
@ -10,7 +10,7 @@ use crate::{
|
|||
body::Expander,
|
||||
db::DefDatabase,
|
||||
intern::Interned,
|
||||
item_tree::{AssocItem, FunctionQualifier, ItemTreeId, ModItem, Param},
|
||||
item_tree::{AssocItem, FnFlags, ItemTreeId, ModItem, Param},
|
||||
type_ref::{TraitRef, TypeBound, TypeRef},
|
||||
visibility::RawVisibility,
|
||||
AssocContainerId, AssocItemId, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId,
|
||||
|
@ -23,14 +23,9 @@ pub struct FunctionData {
|
|||
pub params: Vec<Interned<TypeRef>>,
|
||||
pub ret_type: Interned<TypeRef>,
|
||||
pub attrs: Attrs,
|
||||
/// True if the first param is `self`. This is relevant to decide whether this
|
||||
/// can be called as a method.
|
||||
pub has_self_param: bool,
|
||||
pub has_body: bool,
|
||||
pub qualifier: FunctionQualifier,
|
||||
pub is_in_extern_block: bool,
|
||||
pub is_varargs: bool,
|
||||
pub visibility: RawVisibility,
|
||||
pub abi: Option<Interned<str>>,
|
||||
flags: FnFlags,
|
||||
}
|
||||
|
||||
impl FunctionData {
|
||||
|
@ -53,6 +48,11 @@ impl FunctionData {
|
|||
.next_back()
|
||||
.map_or(false, |param| matches!(item_tree[param], Param::Varargs));
|
||||
|
||||
let mut flags = func.flags;
|
||||
if is_varargs {
|
||||
flags |= FnFlags::IS_VARARGS;
|
||||
}
|
||||
|
||||
Arc::new(FunctionData {
|
||||
name: func.name.clone(),
|
||||
params: enabled_params
|
||||
|
@ -64,14 +64,45 @@ impl FunctionData {
|
|||
.collect(),
|
||||
ret_type: func.ret_type.clone(),
|
||||
attrs: item_tree.attrs(db, krate, ModItem::from(loc.id.value).into()),
|
||||
has_self_param: func.has_self_param,
|
||||
has_body: func.has_body,
|
||||
qualifier: func.qualifier.clone(),
|
||||
is_in_extern_block: func.is_in_extern_block,
|
||||
is_varargs,
|
||||
visibility: item_tree[func.visibility].clone(),
|
||||
abi: func.abi.clone(),
|
||||
flags,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn has_body(&self) -> bool {
|
||||
self.flags.contains(FnFlags::HAS_BODY)
|
||||
}
|
||||
|
||||
/// True if the first param is `self`. This is relevant to decide whether this
|
||||
/// can be called as a method.
|
||||
pub fn has_self_param(&self) -> bool {
|
||||
self.flags.contains(FnFlags::HAS_SELF_PARAM)
|
||||
}
|
||||
|
||||
pub fn is_default(&self) -> bool {
|
||||
self.flags.contains(FnFlags::IS_DEFAULT)
|
||||
}
|
||||
|
||||
pub fn is_const(&self) -> bool {
|
||||
self.flags.contains(FnFlags::IS_CONST)
|
||||
}
|
||||
|
||||
pub fn is_async(&self) -> bool {
|
||||
self.flags.contains(FnFlags::IS_ASYNC)
|
||||
}
|
||||
|
||||
pub fn is_unsafe(&self) -> bool {
|
||||
self.flags.contains(FnFlags::IS_UNSAFE)
|
||||
}
|
||||
|
||||
pub fn is_in_extern_block(&self) -> bool {
|
||||
self.flags.contains(FnFlags::IS_IN_EXTERN_BLOCK)
|
||||
}
|
||||
|
||||
pub fn is_varargs(&self) -> bool {
|
||||
self.flags.contains(FnFlags::IS_VARARGS)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
|
|
|
@ -24,7 +24,7 @@ use la_arena::{Arena, Idx, RawIdx};
|
|||
use profile::Count;
|
||||
use rustc_hash::FxHashMap;
|
||||
use smallvec::SmallVec;
|
||||
use syntax::{ast, match_ast, SmolStr, SyntaxKind};
|
||||
use syntax::{ast, match_ast, SyntaxKind};
|
||||
|
||||
use crate::{
|
||||
attr::{Attrs, RawAttrs},
|
||||
|
@ -556,15 +556,11 @@ pub struct Function {
|
|||
pub name: Name,
|
||||
pub visibility: RawVisibilityId,
|
||||
pub generic_params: GenericParamsId,
|
||||
pub has_self_param: bool,
|
||||
pub has_body: bool,
|
||||
pub qualifier: FunctionQualifier,
|
||||
/// Whether the function is located in an `extern` block (*not* whether it is an
|
||||
/// `extern "abi" fn`).
|
||||
pub is_in_extern_block: bool,
|
||||
pub abi: Option<Interned<str>>,
|
||||
pub params: IdRange<Param>,
|
||||
pub ret_type: Interned<TypeRef>,
|
||||
pub ast_id: FileAstId<ast::Fn>,
|
||||
pub(crate) flags: FnFlags,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
|
@ -573,13 +569,20 @@ pub enum Param {
|
|||
Varargs,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct FunctionQualifier {
|
||||
pub is_default: bool,
|
||||
pub is_const: bool,
|
||||
pub is_async: bool,
|
||||
pub is_unsafe: bool,
|
||||
pub abi: Option<SmolStr>,
|
||||
bitflags::bitflags! {
|
||||
/// NOTE: Shared with `FunctionData`
|
||||
pub(crate) struct FnFlags: u8 {
|
||||
const HAS_SELF_PARAM = 1 << 0;
|
||||
const HAS_BODY = 1 << 1;
|
||||
const IS_DEFAULT = 1 << 2;
|
||||
const IS_CONST = 1 << 3;
|
||||
const IS_ASYNC = 1 << 4;
|
||||
const IS_UNSAFE = 1 << 5;
|
||||
/// Whether the function is located in an `extern` block (*not* whether it is an
|
||||
/// `extern "abi" fn`).
|
||||
const IS_IN_EXTERN_BLOCK = 1 << 6;
|
||||
const IS_VARARGS = 1 << 7;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
|
|
|
@ -395,39 +395,51 @@ impl Ctx {
|
|||
ret_type
|
||||
};
|
||||
|
||||
let has_body = func.body().is_some();
|
||||
let abi = func.abi().map(|abi| {
|
||||
// FIXME: Abi::abi() -> Option<SyntaxToken>?
|
||||
match abi.syntax().last_token() {
|
||||
Some(tok) if tok.kind() == SyntaxKind::STRING => {
|
||||
// FIXME: Better way to unescape?
|
||||
Interned::new_str(tok.text().trim_matches('"'))
|
||||
}
|
||||
_ => {
|
||||
// `extern` default to be `extern "C"`.
|
||||
Interned::new_str("C")
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let ast_id = self.source_ast_id_map.ast_id(func);
|
||||
let qualifier = FunctionQualifier {
|
||||
is_default: func.default_token().is_some(),
|
||||
is_const: func.const_token().is_some(),
|
||||
is_async: func.async_token().is_some(),
|
||||
is_unsafe: func.unsafe_token().is_some(),
|
||||
abi: func.abi().map(|abi| {
|
||||
// FIXME: Abi::abi() -> Option<SyntaxToken>?
|
||||
match abi.syntax().last_token() {
|
||||
Some(tok) if tok.kind() == SyntaxKind::STRING => {
|
||||
// FIXME: Better way to unescape?
|
||||
tok.text().trim_matches('"').into()
|
||||
}
|
||||
_ => {
|
||||
// `extern` default to be `extern "C"`.
|
||||
"C".into()
|
||||
}
|
||||
}
|
||||
}),
|
||||
};
|
||||
|
||||
let mut flags = FnFlags::empty();
|
||||
if func.body().is_some() {
|
||||
flags |= FnFlags::HAS_BODY;
|
||||
}
|
||||
if has_self_param {
|
||||
flags |= FnFlags::HAS_SELF_PARAM;
|
||||
}
|
||||
if func.default_token().is_some() {
|
||||
flags |= FnFlags::IS_DEFAULT;
|
||||
}
|
||||
if func.const_token().is_some() {
|
||||
flags |= FnFlags::IS_CONST;
|
||||
}
|
||||
if func.async_token().is_some() {
|
||||
flags |= FnFlags::IS_ASYNC;
|
||||
}
|
||||
if func.unsafe_token().is_some() {
|
||||
flags |= FnFlags::IS_UNSAFE;
|
||||
}
|
||||
|
||||
let mut res = Function {
|
||||
name,
|
||||
visibility,
|
||||
generic_params: GenericParamsId::EMPTY,
|
||||
has_self_param,
|
||||
has_body,
|
||||
qualifier,
|
||||
is_in_extern_block: false,
|
||||
abi,
|
||||
params,
|
||||
ret_type: Interned::new(ret_type),
|
||||
ast_id,
|
||||
flags,
|
||||
};
|
||||
res.generic_params = self.lower_generic_params(GenericsOwner::Function(&res), func);
|
||||
|
||||
|
@ -640,8 +652,10 @@ impl Ctx {
|
|||
ast::ExternItem::Fn(ast) => {
|
||||
let func_id = self.lower_function(&ast)?;
|
||||
let func = &mut self.data().functions[func_id.index];
|
||||
func.qualifier.is_unsafe = is_intrinsic_fn_unsafe(&func.name);
|
||||
func.is_in_extern_block = true;
|
||||
if is_intrinsic_fn_unsafe(&func.name) {
|
||||
func.flags |= FnFlags::IS_UNSAFE;
|
||||
}
|
||||
func.flags |= FnFlags::IS_IN_EXTERN_BLOCK;
|
||||
func_id.into()
|
||||
}
|
||||
ast::ExternItem::Static(ast) => {
|
||||
|
|
|
@ -91,7 +91,7 @@ impl<'a, 'b> DeclValidator<'a, 'b> {
|
|||
|
||||
fn validate_func(&mut self, func: FunctionId) {
|
||||
let data = self.db.function_data(func);
|
||||
if data.is_in_extern_block {
|
||||
if data.is_in_extern_block() {
|
||||
cov_mark::hit!(extern_func_incorrect_case_ignored);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ impl<'a, 'b> UnsafeValidator<'a, 'b> {
|
|||
let def = self.owner;
|
||||
let unsafe_expressions = unsafe_expressions(db, self.infer.as_ref(), def);
|
||||
let is_unsafe = match self.owner {
|
||||
DefWithBodyId::FunctionId(it) => db.function_data(it).qualifier.is_unsafe,
|
||||
DefWithBodyId::FunctionId(it) => db.function_data(it).is_unsafe(),
|
||||
DefWithBodyId::StaticId(_) | DefWithBodyId::ConstId(_) => false,
|
||||
};
|
||||
if is_unsafe
|
||||
|
@ -86,7 +86,7 @@ fn walk_unsafe(
|
|||
match expr {
|
||||
&Expr::Call { callee, .. } => {
|
||||
if let Some(func) = infer[callee].as_fn_def(db) {
|
||||
if db.function_data(func).qualifier.is_unsafe {
|
||||
if db.function_data(func).is_unsafe() {
|
||||
unsafe_exprs.push(UnsafeExpr { expr: current, inside_unsafe_block });
|
||||
}
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ fn walk_unsafe(
|
|||
Expr::MethodCall { .. } => {
|
||||
if infer
|
||||
.method_resolution(current)
|
||||
.map(|func| db.function_data(func).qualifier.is_unsafe)
|
||||
.map(|func| db.function_data(func).is_unsafe())
|
||||
.unwrap_or(false)
|
||||
{
|
||||
unsafe_exprs.push(UnsafeExpr { expr: current, inside_unsafe_block });
|
||||
|
|
|
@ -1054,7 +1054,7 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
|
|||
let ret = (&ctx_ret).lower_ty(&data.ret_type);
|
||||
let generics = generics(db.upcast(), def.into());
|
||||
let num_binders = generics.len();
|
||||
Binders::new(num_binders, CallableSig::from_params_and_return(params, ret, data.is_varargs))
|
||||
Binders::new(num_binders, CallableSig::from_params_and_return(params, ret, data.is_varargs()))
|
||||
}
|
||||
|
||||
/// Build the declared type of a function. This should not need to look at the
|
||||
|
|
|
@ -675,7 +675,7 @@ fn is_valid_candidate(
|
|||
}
|
||||
}
|
||||
if let Some(receiver_ty) = receiver_ty {
|
||||
if !data.has_self_param {
|
||||
if !data.has_self_param() {
|
||||
return false;
|
||||
}
|
||||
let transformed_receiver_ty = match transform_receiver_ty(db, m, self_ty) {
|
||||
|
|
Loading…
Reference in a new issue