feat: initial support for safe_kw in extern blocks

This commit is contained in:
roife 2024-10-20 16:45:38 +08:00
parent 687b72c36a
commit 9f1e450c4f
12 changed files with 264 additions and 9 deletions

View file

@ -148,6 +148,10 @@ impl FunctionData {
self.flags.contains(FnFlags::HAS_UNSAFE_KW) self.flags.contains(FnFlags::HAS_UNSAFE_KW)
} }
pub fn is_safe(&self) -> bool {
self.flags.contains(FnFlags::HAS_SAFE_KW)
}
pub fn is_varargs(&self) -> bool { pub fn is_varargs(&self) -> bool {
self.flags.contains(FnFlags::IS_VARARGS) self.flags.contains(FnFlags::IS_VARARGS)
} }

View file

@ -754,6 +754,7 @@ bitflags::bitflags! {
const HAS_ASYNC_KW = 1 << 4; const HAS_ASYNC_KW = 1 << 4;
const HAS_UNSAFE_KW = 1 << 5; const HAS_UNSAFE_KW = 1 << 5;
const IS_VARARGS = 1 << 6; const IS_VARARGS = 1 << 6;
const HAS_SAFE_KW = 1 << 7;
} }
} }

View file

@ -440,6 +440,9 @@ impl<'a> Ctx<'a> {
if func.unsafe_token().is_some() { if func.unsafe_token().is_some() {
flags |= FnFlags::HAS_UNSAFE_KW; flags |= FnFlags::HAS_UNSAFE_KW;
} }
if func.safe_token().is_some() {
flags |= FnFlags::HAS_SAFE_KW;
}
if has_var_args { if has_var_args {
flags |= FnFlags::IS_VARARGS; flags |= FnFlags::IS_VARARGS;
} }

View file

@ -278,6 +278,9 @@ impl Printer<'_> {
if flags.contains(FnFlags::HAS_UNSAFE_KW) { if flags.contains(FnFlags::HAS_UNSAFE_KW) {
w!(self, "unsafe "); w!(self, "unsafe ");
} }
if flags.contains(FnFlags::HAS_SAFE_KW) {
w!(self, "safe ");
}
if let Some(abi) = abi { if let Some(abi) = abi {
w!(self, "extern \"{}\" ", abi); w!(self, "extern \"{}\" ", abi);
} }

View file

@ -257,10 +257,12 @@ pub fn is_fn_unsafe_to_call(db: &dyn HirDatabase, func: FunctionId) -> bool {
return true; return true;
} }
match func.lookup(db.upcast()).container { let loc = func.lookup(db.upcast());
match loc.container {
hir_def::ItemContainerId::ExternBlockId(block) => { hir_def::ItemContainerId::ExternBlockId(block) => {
// Function in an `extern` block are always unsafe to call, except when it has // Function in an `extern` block are always unsafe to call, except when
// `"rust-intrinsic"` ABI there are a few exceptions. // it is marked as `safe` or it has `"rust-intrinsic"` ABI there are a
// few exceptions.
let id = block.lookup(db.upcast()).id; let id = block.lookup(db.upcast()).id;
let is_intrinsic = let is_intrinsic =
@ -270,8 +272,8 @@ pub fn is_fn_unsafe_to_call(db: &dyn HirDatabase, func: FunctionId) -> bool {
// Intrinsics are unsafe unless they have the rustc_safe_intrinsic attribute // Intrinsics are unsafe unless they have the rustc_safe_intrinsic attribute
!data.attrs.by_key(&sym::rustc_safe_intrinsic).exists() !data.attrs.by_key(&sym::rustc_safe_intrinsic).exists()
} else { } else {
// Extern items are always unsafe // Extern items without `safe` modifier are always unsafe
true !db.function_data(func).is_safe()
} }
} }
_ => false, _ => false,

View file

@ -554,7 +554,7 @@ fn main() {
r#" r#"
//- /ed2021.rs crate:ed2021 edition:2021 //- /ed2021.rs crate:ed2021 edition:2021
#[rustc_deprecated_safe_2024] #[rustc_deprecated_safe_2024]
unsafe fn safe() -> u8 { unsafe fn safe_fn() -> u8 {
0 0
} }
//- /ed2024.rs crate:ed2024 edition:2024 //- /ed2024.rs crate:ed2024 edition:2024
@ -564,7 +564,7 @@ unsafe fn not_safe() -> u8 {
} }
//- /main.rs crate:main deps:ed2021,ed2024 //- /main.rs crate:main deps:ed2021,ed2024
fn main() { fn main() {
ed2021::safe(); ed2021::safe_fn();
ed2024::not_safe(); ed2024::not_safe();
//^^^^^^^^^^^^^^^^^^💡 error: this operation is unsafe and requires an unsafe function or block //^^^^^^^^^^^^^^^^^^💡 error: this operation is unsafe and requires an unsafe function or block
} }

View file

@ -135,6 +135,11 @@ pub(super) fn opt_item(p: &mut Parser<'_>, m: Marker) -> Result<(), Marker> {
has_mods = true; has_mods = true;
} }
if p.at(T![safe]) {
p.eat(T![safe]);
has_mods = true;
}
if p.at(T![extern]) { if p.at(T![extern]) {
has_extern = true; has_extern = true;
has_mods = true; has_mods = true;
@ -189,6 +194,7 @@ pub(super) fn opt_item(p: &mut Parser<'_>, m: Marker) -> Result<(), Marker> {
T![fn] => fn_(p, m), T![fn] => fn_(p, m),
T![const] if p.nth(1) != T!['{'] => consts::konst(p, m), T![const] if p.nth(1) != T!['{'] => consts::konst(p, m),
T![static] if matches!(p.nth(1), IDENT | T![_] | T![mut]) => consts::static_(p, m),
T![trait] => traits::trait_(p, m), T![trait] => traits::trait_(p, m),
T![impl] => traits::impl_(p, m), T![impl] => traits::impl_(p, m),

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,208 @@
SOURCE_FILE
EXTERN_BLOCK
UNSAFE_KW "unsafe"
WHITESPACE " "
ABI
EXTERN_KW "extern"
WHITESPACE " "
EXTERN_ITEM_LIST
L_CURLY "{"
WHITESPACE "\n "
FN
COMMENT "// sqrt (from libm) may be called with any `f64`"
WHITESPACE "\n "
VISIBILITY
PUB_KW "pub"
WHITESPACE " "
SAFE_KW "safe"
WHITESPACE " "
FN_KW "fn"
WHITESPACE " "
NAME
IDENT "sqrt"
PARAM_LIST
L_PAREN "("
PARAM
IDENT_PAT
NAME
IDENT "x"
COLON ":"
WHITESPACE " "
PATH_TYPE
PATH
PATH_SEGMENT
NAME_REF
IDENT "f64"
R_PAREN ")"
WHITESPACE " "
RET_TYPE
THIN_ARROW "->"
WHITESPACE " "
PATH_TYPE
PATH
PATH_SEGMENT
NAME_REF
IDENT "f64"
SEMICOLON ";"
WHITESPACE "\n\n "
FN
COMMENT "// strlen (from libc) requires a valid pointer,"
WHITESPACE "\n "
COMMENT "// so we mark it as being an unsafe fn"
WHITESPACE "\n "
VISIBILITY
PUB_KW "pub"
WHITESPACE " "
UNSAFE_KW "unsafe"
WHITESPACE " "
FN_KW "fn"
WHITESPACE " "
NAME
IDENT "strlen"
PARAM_LIST
L_PAREN "("
PARAM
IDENT_PAT
NAME
IDENT "p"
COLON ":"
WHITESPACE " "
PTR_TYPE
STAR "*"
CONST_KW "const"
WHITESPACE " "
PATH_TYPE
PATH
PATH_SEGMENT
NAME_REF
IDENT "c_char"
R_PAREN ")"
WHITESPACE " "
RET_TYPE
THIN_ARROW "->"
WHITESPACE " "
PATH_TYPE
PATH
PATH_SEGMENT
NAME_REF
IDENT "usize"
SEMICOLON ";"
WHITESPACE "\n\n "
FN
COMMENT "// this function doesn't say safe or unsafe, so it defaults to unsafe"
WHITESPACE "\n "
VISIBILITY
PUB_KW "pub"
WHITESPACE " "
FN_KW "fn"
WHITESPACE " "
NAME
IDENT "free"
PARAM_LIST
L_PAREN "("
PARAM
IDENT_PAT
NAME
IDENT "p"
COLON ":"
WHITESPACE " "
PTR_TYPE
STAR "*"
MUT_KW "mut"
WHITESPACE " "
PATH_TYPE
PATH
PATH
PATH
PATH_SEGMENT
NAME_REF
IDENT "core"
COLON2 "::"
PATH_SEGMENT
NAME_REF
IDENT "ffi"
COLON2 "::"
PATH_SEGMENT
NAME_REF
IDENT "c_void"
R_PAREN ")"
SEMICOLON ";"
WHITESPACE "\n\n "
STATIC
VISIBILITY
PUB_KW "pub"
WHITESPACE " "
SAFE_KW "safe"
WHITESPACE " "
STATIC_KW "static"
WHITESPACE " "
MUT_KW "mut"
WHITESPACE " "
NAME
IDENT "COUNTER"
COLON ":"
WHITESPACE " "
PATH_TYPE
PATH
PATH_SEGMENT
NAME_REF
IDENT "i32"
SEMICOLON ";"
WHITESPACE "\n\n "
STATIC
VISIBILITY
PUB_KW "pub"
WHITESPACE " "
UNSAFE_KW "unsafe"
WHITESPACE " "
STATIC_KW "static"
WHITESPACE " "
NAME
IDENT "IMPORTANT_BYTES"
COLON ":"
WHITESPACE " "
ARRAY_TYPE
L_BRACK "["
PATH_TYPE
PATH
PATH_SEGMENT
NAME_REF
IDENT "u8"
SEMICOLON ";"
WHITESPACE " "
CONST_ARG
LITERAL
INT_NUMBER "256"
R_BRACK "]"
SEMICOLON ";"
WHITESPACE "\n\n "
STATIC
VISIBILITY
PUB_KW "pub"
WHITESPACE " "
SAFE_KW "safe"
WHITESPACE " "
STATIC_KW "static"
WHITESPACE " "
NAME
IDENT "LINES"
COLON ":"
WHITESPACE " "
PATH_TYPE
PATH
PATH_SEGMENT
NAME_REF
IDENT "SyncUnsafeCell"
GENERIC_ARG_LIST
L_ANGLE "<"
TYPE_ARG
PATH_TYPE
PATH
PATH_SEGMENT
NAME_REF
IDENT "i32"
R_ANGLE ">"
SEMICOLON ";"
WHITESPACE "\n"
R_CURLY "}"
WHITESPACE "\n"

View file

@ -0,0 +1,17 @@
unsafe extern {
// sqrt (from libm) may be called with any `f64`
pub safe fn sqrt(x: f64) -> f64;
// strlen (from libc) requires a valid pointer,
// so we mark it as being an unsafe fn
pub unsafe fn strlen(p: *const c_char) -> usize;
// this function doesn't say safe or unsafe, so it defaults to unsafe
pub fn free(p: *mut core::ffi::c_void);
pub safe static mut COUNTER: i32;
pub unsafe static IMPORTANT_BYTES: [u8; 256];
pub safe static LINES: SyncUnsafeCell<i32>;
}

View file

@ -190,7 +190,7 @@ UseTreeList =
Fn = Fn =
Attr* Visibility? Attr* Visibility?
'default'? 'const'? 'async'? 'gen'? 'unsafe'? Abi? 'default'? 'const'? 'async'? 'gen'? 'unsafe'? 'safe'? Abi?
'fn' Name GenericParamList? ParamList RetType? WhereClause? 'fn' Name GenericParamList? ParamList RetType? WhereClause?
(body:BlockExpr | ';') (body:BlockExpr | ';')
@ -284,6 +284,7 @@ Const =
Static = Static =
Attr* Visibility? Attr* Visibility?
'unsafe'? 'safe'?
'static' 'mut'? Name ':' Type 'static' 'mut'? Name ':' Type
('=' body:Expr)? ';' ('=' body:Expr)? ';'

View file

@ -668,6 +668,8 @@ impl Fn {
#[inline] #[inline]
pub fn gen_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![gen]) } pub fn gen_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![gen]) }
#[inline] #[inline]
pub fn safe_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![safe]) }
#[inline]
pub fn unsafe_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![unsafe]) } pub fn unsafe_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![unsafe]) }
} }
@ -1761,7 +1763,11 @@ impl Static {
#[inline] #[inline]
pub fn mut_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![mut]) } pub fn mut_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![mut]) }
#[inline] #[inline]
pub fn safe_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![safe]) }
#[inline]
pub fn static_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![static]) } pub fn static_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![static]) }
#[inline]
pub fn unsafe_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![unsafe]) }
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]