feat: initial support for safe_kw in extern blocks

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

View File

@ -148,6 +148,10 @@ pub fn is_unsafe(&self) -> bool {
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 {
self.flags.contains(FnFlags::IS_VARARGS)
}

View File

@ -754,6 +754,7 @@ pub(crate) struct FnFlags: u8 {
const HAS_ASYNC_KW = 1 << 4;
const HAS_UNSAFE_KW = 1 << 5;
const IS_VARARGS = 1 << 6;
const HAS_SAFE_KW = 1 << 7;
}
}

View File

@ -440,6 +440,9 @@ fn lower_function(&mut self, func: &ast::Fn) -> Option<FileItemTreeId<Function>>
if func.unsafe_token().is_some() {
flags |= FnFlags::HAS_UNSAFE_KW;
}
if func.safe_token().is_some() {
flags |= FnFlags::HAS_SAFE_KW;
}
if has_var_args {
flags |= FnFlags::IS_VARARGS;
}

View File

@ -278,6 +278,9 @@ fn print_mod_item(&mut self, item: ModItem) {
if flags.contains(FnFlags::HAS_UNSAFE_KW) {
w!(self, "unsafe ");
}
if flags.contains(FnFlags::HAS_SAFE_KW) {
w!(self, "safe ");
}
if let Some(abi) = 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;
}
match func.lookup(db.upcast()).container {
let loc = func.lookup(db.upcast());
match loc.container {
hir_def::ItemContainerId::ExternBlockId(block) => {
// Function in an `extern` block are always unsafe to call, except when it has
// `"rust-intrinsic"` ABI there are a few exceptions.
// Function in an `extern` block are always unsafe to call, except when
// it is marked as `safe` or it has `"rust-intrinsic"` ABI there are a
// few exceptions.
let id = block.lookup(db.upcast()).id;
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
!data.attrs.by_key(&sym::rustc_safe_intrinsic).exists()
} else {
// Extern items are always unsafe
true
// Extern items without `safe` modifier are always unsafe
!db.function_data(func).is_safe()
}
}
_ => false,

View File

@ -554,7 +554,7 @@ fn rustc_deprecated_safe_2024() {
r#"
//- /ed2021.rs crate:ed2021 edition:2021
#[rustc_deprecated_safe_2024]
unsafe fn safe() -> u8 {
unsafe fn safe_fn() -> u8 {
0
}
//- /ed2024.rs crate:ed2024 edition:2024
@ -564,7 +564,7 @@ unsafe fn not_safe() -> u8 {
}
//- /main.rs crate:main deps:ed2021,ed2024
fn main() {
ed2021::safe();
ed2021::safe_fn();
ed2024::not_safe();
//^^^^^^^^^^^^^^^^^^💡 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;
}
if p.at(T![safe]) {
p.eat(T![safe]);
has_mods = true;
}
if p.at(T![extern]) {
has_extern = 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![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![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 =
Attr* Visibility?
'default'? 'const'? 'async'? 'gen'? 'unsafe'? Abi?
'default'? 'const'? 'async'? 'gen'? 'unsafe'? 'safe'? Abi?
'fn' Name GenericParamList? ParamList RetType? WhereClause?
(body:BlockExpr | ';')
@ -284,6 +284,7 @@ Const =
Static =
Attr* Visibility?
'unsafe'? 'safe'?
'static' 'mut'? Name ':' Type
('=' body:Expr)? ';'

View File

@ -668,6 +668,8 @@ pub fn fn_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![
#[inline]
pub fn gen_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![gen]) }
#[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]) }
}
@ -1761,7 +1763,11 @@ pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![
#[inline]
pub fn mut_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![mut]) }
#[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]) }
#[inline]
pub fn unsafe_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![unsafe]) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]