dependencies = [
"anymap",
"base_db",
+ "bitflags",
"cfg",
"cov-mark",
"dashmap",
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)?;
write!(f, ", ")?;
} else {
first = false;
- if data.has_self_param {
+ if data.has_self_param() {
write_self_param(type_ref, f)?;
continue;
}
// `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 {
}
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 })
}
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) {
///
/// 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.
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"
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,
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 {
.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
.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)]
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},
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)]
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)]
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);
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) => {
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;
}
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
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 });
}
}
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 });
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
}
}
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) {