mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-27 10:17:15 +00:00
Differentiate between asm!(), global_asm!() and naked_asm!(), and make only asm!() unsafe
This commit is contained in:
parent
edb804a100
commit
bd8087e86e
16 changed files with 198 additions and 16 deletions
|
|
@ -10,7 +10,7 @@ use tt::TextRange;
|
|||
|
||||
use crate::{
|
||||
expr_store::lower::{ExprCollector, FxIndexSet},
|
||||
hir::{AsmOperand, AsmOptions, Expr, ExprId, InlineAsm, InlineAsmRegOrRegClass},
|
||||
hir::{AsmOperand, AsmOptions, Expr, ExprId, InlineAsm, InlineAsmKind, InlineAsmRegOrRegClass},
|
||||
};
|
||||
|
||||
impl ExprCollector<'_> {
|
||||
|
|
@ -269,8 +269,17 @@ impl ExprCollector<'_> {
|
|||
}
|
||||
})
|
||||
};
|
||||
|
||||
let kind = if asm.global_asm_token().is_some() {
|
||||
InlineAsmKind::GlobalAsm
|
||||
} else if asm.naked_asm_token().is_some() {
|
||||
InlineAsmKind::NakedAsm
|
||||
} else {
|
||||
InlineAsmKind::Asm
|
||||
};
|
||||
|
||||
let idx = self.alloc_expr(
|
||||
Expr::InlineAsm(InlineAsm { operands: operands.into_boxed_slice(), options }),
|
||||
Expr::InlineAsm(InlineAsm { operands: operands.into_boxed_slice(), options, kind }),
|
||||
syntax_ptr,
|
||||
);
|
||||
self.source_map
|
||||
|
|
|
|||
|
|
@ -332,6 +332,17 @@ pub struct OffsetOf {
|
|||
pub struct InlineAsm {
|
||||
pub operands: Box<[(Option<Name>, AsmOperand)]>,
|
||||
pub options: AsmOptions,
|
||||
pub kind: InlineAsmKind,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum InlineAsmKind {
|
||||
/// `asm!()`.
|
||||
Asm,
|
||||
/// `global_asm!()`.
|
||||
GlobalAsm,
|
||||
/// `naked_asm!()`.
|
||||
NakedAsm,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||
|
|
|
|||
|
|
@ -28,6 +28,20 @@ fn test_asm_expand() {
|
|||
r#"
|
||||
#[rustc_builtin_macro]
|
||||
macro_rules! asm {() => {}}
|
||||
#[rustc_builtin_macro]
|
||||
macro_rules! global_asm {() => {}}
|
||||
#[rustc_builtin_macro]
|
||||
macro_rules! naked_asm {() => {}}
|
||||
|
||||
// FIXME: This creates an error
|
||||
// global_asm! {
|
||||
// ""
|
||||
// }
|
||||
|
||||
#[unsafe(naked)]
|
||||
extern "C" fn foo() {
|
||||
naked_asm!("");
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let i: u64 = 3;
|
||||
|
|
@ -45,6 +59,20 @@ fn main() {
|
|||
expect![[r##"
|
||||
#[rustc_builtin_macro]
|
||||
macro_rules! asm {() => {}}
|
||||
#[rustc_builtin_macro]
|
||||
macro_rules! global_asm {() => {}}
|
||||
#[rustc_builtin_macro]
|
||||
macro_rules! naked_asm {() => {}}
|
||||
|
||||
// FIXME: This creates an error
|
||||
// global_asm! {
|
||||
// ""
|
||||
// }
|
||||
|
||||
#[unsafe(naked)]
|
||||
extern "C" fn foo() {
|
||||
builtin #naked_asm ("");
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let i: u64 = 3;
|
||||
|
|
|
|||
|
|
@ -125,8 +125,8 @@ register_builtin! {
|
|||
(assert, Assert) => assert_expand,
|
||||
(stringify, Stringify) => stringify_expand,
|
||||
(asm, Asm) => asm_expand,
|
||||
(global_asm, GlobalAsm) => asm_expand,
|
||||
(naked_asm, NakedAsm) => asm_expand,
|
||||
(global_asm, GlobalAsm) => global_asm_expand,
|
||||
(naked_asm, NakedAsm) => naked_asm_expand,
|
||||
(cfg, Cfg) => cfg_expand,
|
||||
(core_panic, CorePanic) => panic_expand,
|
||||
(std_panic, StdPanic) => panic_expand,
|
||||
|
|
@ -325,6 +325,36 @@ fn asm_expand(
|
|||
ExpandResult::ok(expanded)
|
||||
}
|
||||
|
||||
fn global_asm_expand(
|
||||
_db: &dyn ExpandDatabase,
|
||||
_id: MacroCallId,
|
||||
tt: &tt::TopSubtree,
|
||||
span: Span,
|
||||
) -> ExpandResult<tt::TopSubtree> {
|
||||
let mut tt = tt.clone();
|
||||
tt.top_subtree_delimiter_mut().kind = tt::DelimiterKind::Parenthesis;
|
||||
let pound = mk_pound(span);
|
||||
let expanded = quote! {span =>
|
||||
builtin #pound global_asm #tt
|
||||
};
|
||||
ExpandResult::ok(expanded)
|
||||
}
|
||||
|
||||
fn naked_asm_expand(
|
||||
_db: &dyn ExpandDatabase,
|
||||
_id: MacroCallId,
|
||||
tt: &tt::TopSubtree,
|
||||
span: Span,
|
||||
) -> ExpandResult<tt::TopSubtree> {
|
||||
let mut tt = tt.clone();
|
||||
tt.top_subtree_delimiter_mut().kind = tt::DelimiterKind::Parenthesis;
|
||||
let pound = mk_pound(span);
|
||||
let expanded = quote! {span =>
|
||||
builtin #pound naked_asm #tt
|
||||
};
|
||||
ExpandResult::ok(expanded)
|
||||
}
|
||||
|
||||
fn cfg_expand(
|
||||
db: &dyn ExpandDatabase,
|
||||
id: MacroCallId,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use either::Either;
|
|||
use hir_def::{
|
||||
AdtId, DefWithBodyId, FieldId, FunctionId, VariantId,
|
||||
expr_store::{Body, path::Path},
|
||||
hir::{AsmOperand, Expr, ExprId, ExprOrPatId, Pat, PatId, Statement, UnaryOp},
|
||||
hir::{AsmOperand, Expr, ExprId, ExprOrPatId, InlineAsmKind, Pat, PatId, Statement, UnaryOp},
|
||||
resolver::{HasResolver, ResolveValueResult, Resolver, ValueNs},
|
||||
signatures::StaticFlags,
|
||||
type_ref::Rawness,
|
||||
|
|
@ -315,7 +315,12 @@ impl<'db> UnsafeVisitor<'db> {
|
|||
self.inside_assignment = old_inside_assignment;
|
||||
}
|
||||
Expr::InlineAsm(asm) => {
|
||||
self.on_unsafe_op(current.into(), UnsafetyReason::InlineAsm);
|
||||
if asm.kind == InlineAsmKind::Asm {
|
||||
// `naked_asm!()` requires `unsafe` on the attribute (`#[unsafe(naked)]`),
|
||||
// and `global_asm!()` doesn't require it at all.
|
||||
self.on_unsafe_op(current.into(), UnsafetyReason::InlineAsm);
|
||||
}
|
||||
|
||||
asm.operands.iter().for_each(|(_, op)| match op {
|
||||
AsmOperand::In { expr, .. }
|
||||
| AsmOperand::Out { expr: Some(expr), .. }
|
||||
|
|
|
|||
|
|
@ -3220,7 +3220,8 @@ impl Macro {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn is_asm_or_global_asm(&self, db: &dyn HirDatabase) -> bool {
|
||||
/// Is this `asm!()`, or a variant of it (e.g. `global_asm!()`)?
|
||||
pub fn is_asm_like(&self, db: &dyn HirDatabase) -> bool {
|
||||
match self.id {
|
||||
MacroId::Macro2Id(it) => {
|
||||
matches!(it.lookup(db).expander, MacroExpander::BuiltIn(m) if m.is_asm())
|
||||
|
|
|
|||
|
|
@ -1776,7 +1776,7 @@ impl<'db> SemanticsImpl<'db> {
|
|||
|
||||
pub fn is_unsafe_macro_call(&self, macro_call: &ast::MacroCall) -> bool {
|
||||
let Some(mac) = self.resolve_macro_call(macro_call) else { return false };
|
||||
if mac.is_asm_or_global_asm(self.db) {
|
||||
if mac.is_asm_like(self.db) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -983,4 +983,19 @@ fn test() {
|
|||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn naked_asm_is_safe() {
|
||||
check_diagnostics(
|
||||
r#"
|
||||
#[rustc_builtin_macro]
|
||||
macro_rules! naked_asm { () => {} }
|
||||
|
||||
#[unsafe(naked)]
|
||||
extern "C" fn naked() {
|
||||
naked_asm!("");
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -253,8 +253,7 @@ fn builtin_expr(p: &mut Parser<'_>) -> Option<CompletedMarker> {
|
|||
let m = p.start();
|
||||
p.bump_remap(T![builtin]);
|
||||
p.bump(T![#]);
|
||||
if p.at_contextual_kw(T![offset_of]) {
|
||||
p.bump_remap(T![offset_of]);
|
||||
if p.eat_contextual_kw(T![offset_of]) {
|
||||
p.expect(T!['(']);
|
||||
type_(p);
|
||||
p.expect(T![,]);
|
||||
|
|
@ -278,8 +277,7 @@ fn builtin_expr(p: &mut Parser<'_>) -> Option<CompletedMarker> {
|
|||
p.expect(T![')']);
|
||||
}
|
||||
Some(m.complete(p, OFFSET_OF_EXPR))
|
||||
} else if p.at_contextual_kw(T![format_args]) {
|
||||
p.bump_remap(T![format_args]);
|
||||
} else if p.eat_contextual_kw(T![format_args]) {
|
||||
p.expect(T!['(']);
|
||||
expr(p);
|
||||
if p.eat(T![,]) {
|
||||
|
|
@ -302,7 +300,16 @@ fn builtin_expr(p: &mut Parser<'_>) -> Option<CompletedMarker> {
|
|||
}
|
||||
p.expect(T![')']);
|
||||
Some(m.complete(p, FORMAT_ARGS_EXPR))
|
||||
} else if p.at_contextual_kw(T![asm]) {
|
||||
} else if p.eat_contextual_kw(T![asm])
|
||||
|| p.eat_contextual_kw(T![global_asm])
|
||||
|| p.eat_contextual_kw(T![naked_asm])
|
||||
{
|
||||
// test asm_kinds
|
||||
// fn foo() {
|
||||
// builtin#asm("");
|
||||
// builtin#global_asm("");
|
||||
// builtin#naked_asm("");
|
||||
// }
|
||||
parse_asm_expr(p, m)
|
||||
} else {
|
||||
m.abandon(p);
|
||||
|
|
@ -322,7 +329,6 @@ fn builtin_expr(p: &mut Parser<'_>) -> Option<CompletedMarker> {
|
|||
// );
|
||||
// }
|
||||
fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option<CompletedMarker> {
|
||||
p.bump_remap(T![asm]);
|
||||
p.expect(T!['(']);
|
||||
if expr(p).is_none() {
|
||||
p.err_and_bump("expected asm template");
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -21,6 +21,8 @@ mod ok {
|
|||
#[test]
|
||||
fn asm_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/asm_expr.rs"); }
|
||||
#[test]
|
||||
fn asm_kinds() { run_and_expect_no_errors("test_data/parser/inline/ok/asm_kinds.rs"); }
|
||||
#[test]
|
||||
fn asm_label() { run_and_expect_no_errors("test_data/parser/inline/ok/asm_label.rs"); }
|
||||
#[test]
|
||||
fn assoc_const_eq() {
|
||||
|
|
|
|||
49
crates/parser/test_data/parser/inline/ok/asm_kinds.rast
Normal file
49
crates/parser/test_data/parser/inline/ok/asm_kinds.rast
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
SOURCE_FILE
|
||||
FN
|
||||
FN_KW "fn"
|
||||
WHITESPACE " "
|
||||
NAME
|
||||
IDENT "foo"
|
||||
PARAM_LIST
|
||||
L_PAREN "("
|
||||
R_PAREN ")"
|
||||
WHITESPACE " "
|
||||
BLOCK_EXPR
|
||||
STMT_LIST
|
||||
L_CURLY "{"
|
||||
WHITESPACE "\n "
|
||||
EXPR_STMT
|
||||
ASM_EXPR
|
||||
BUILTIN_KW "builtin"
|
||||
POUND "#"
|
||||
ASM_KW "asm"
|
||||
L_PAREN "("
|
||||
LITERAL
|
||||
STRING "\"\""
|
||||
R_PAREN ")"
|
||||
SEMICOLON ";"
|
||||
WHITESPACE "\n "
|
||||
EXPR_STMT
|
||||
ASM_EXPR
|
||||
BUILTIN_KW "builtin"
|
||||
POUND "#"
|
||||
GLOBAL_ASM_KW "global_asm"
|
||||
L_PAREN "("
|
||||
LITERAL
|
||||
STRING "\"\""
|
||||
R_PAREN ")"
|
||||
SEMICOLON ";"
|
||||
WHITESPACE "\n "
|
||||
EXPR_STMT
|
||||
ASM_EXPR
|
||||
BUILTIN_KW "builtin"
|
||||
POUND "#"
|
||||
NAKED_ASM_KW "naked_asm"
|
||||
L_PAREN "("
|
||||
LITERAL
|
||||
STRING "\"\""
|
||||
R_PAREN ")"
|
||||
SEMICOLON ";"
|
||||
WHITESPACE "\n"
|
||||
R_CURLY "}"
|
||||
WHITESPACE "\n"
|
||||
5
crates/parser/test_data/parser/inline/ok/asm_kinds.rs
Normal file
5
crates/parser/test_data/parser/inline/ok/asm_kinds.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
fn foo() {
|
||||
builtin#asm("");
|
||||
builtin#global_asm("");
|
||||
builtin#naked_asm("");
|
||||
}
|
||||
|
|
@ -409,7 +409,8 @@ OffsetOfExpr =
|
|||
// global_asm := "global_asm!(" format_string *("," format_string) *("," operand) [","] ")"
|
||||
// format_string := STRING_LITERAL / RAW_STRING_LITERAL
|
||||
AsmExpr =
|
||||
Attr* 'builtin' '#' 'asm' '(' template:(Expr (',' Expr)*) (AsmPiece (',' AsmPiece)*)? ','? ')'
|
||||
Attr* 'builtin' '#' ( 'asm' | 'global_asm' | 'naked_asm' )
|
||||
'(' template:(Expr (',' Expr)*) (AsmPiece (',' AsmPiece)*)? ','? ')'
|
||||
|
||||
// operand_expr := expr / "_" / expr "=>" expr / expr "=>" "_"
|
||||
AsmOperandExpr = in_expr:Expr ('=>' out_expr:Expr)?
|
||||
|
|
|
|||
|
|
@ -118,6 +118,14 @@ impl AsmExpr {
|
|||
pub fn asm_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![asm]) }
|
||||
#[inline]
|
||||
pub fn builtin_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![builtin]) }
|
||||
#[inline]
|
||||
pub fn global_asm_token(&self) -> Option<SyntaxToken> {
|
||||
support::token(&self.syntax, T![global_asm])
|
||||
}
|
||||
#[inline]
|
||||
pub fn naked_asm_token(&self) -> Option<SyntaxToken> {
|
||||
support::token(&self.syntax, T![naked_asm])
|
||||
}
|
||||
}
|
||||
pub struct AsmLabel {
|
||||
pub(crate) syntax: SyntaxNode,
|
||||
|
|
|
|||
|
|
@ -116,6 +116,8 @@ const CONTEXTUAL_KEYWORDS: &[&str] =
|
|||
// keywords we use for special macro expansions
|
||||
const CONTEXTUAL_BUILTIN_KEYWORDS: &[&str] = &[
|
||||
"asm",
|
||||
"naked_asm",
|
||||
"global_asm",
|
||||
"att_syntax",
|
||||
"builtin",
|
||||
"clobber_abi",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue