Auto merge of #18022 - Veykril:asm-parse, r=Veykril

feat: IDE support for `asm!` expressions

Fixes https://github.com/rust-lang/rust-analyzer/issues/10461, Fixes https://github.com/rust-lang/rust-analyzer/issues/6031 Progresses https://github.com/rust-lang/rust-analyzer/issues/11621

Notably this only works for asm expressions not items yet. Most IDE features work, mainly completions need extra logic still.
This commit is contained in:
bors 2024-09-05 11:50:34 +00:00
commit bdfb2f6745
49 changed files with 2070 additions and 164 deletions

View file

@ -15,6 +15,7 @@ extend-ignore-re = [
'"flate2"',
"raison d'être",
"inout",
"INOUT",
"optin"
]

View file

@ -2624,6 +2624,7 @@ version = "0.1.0"
dependencies = [
"anyhow",
"directories",
"either",
"flate2",
"itertools",
"proc-macro2",

View file

@ -185,6 +185,7 @@ style = { level = "warn", priority = -1 }
suspicious = { level = "warn", priority = -1 }
## allow following lints
too_long_first_doc_paragraph = "allow"
# subjective
single_match = "allow"
# () makes a fine error in most cases

View file

@ -100,7 +100,14 @@ pub struct BodySourceMap {
field_map_back: FxHashMap<ExprId, FieldSource>,
pat_field_map_back: FxHashMap<PatId, PatFieldSource>,
format_args_template_map: FxHashMap<ExprId, Vec<(syntax::TextRange, Name)>>,
template_map: Option<
Box<(
// format_args!
FxHashMap<ExprId, Vec<(syntax::TextRange, Name)>>,
// asm!
FxHashMap<ExprId, Vec<(syntax::TextRange, usize)>>,
)>,
>,
expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, MacroFileId>,
@ -426,7 +433,16 @@ impl BodySourceMap {
node: InFile<&ast::FormatArgsExpr>,
) -> Option<&[(syntax::TextRange, Name)]> {
let src = node.map(AstPtr::new).map(AstPtr::upcast::<ast::Expr>);
self.format_args_template_map.get(self.expr_map.get(&src)?).map(std::ops::Deref::deref)
self.template_map.as_ref()?.0.get(self.expr_map.get(&src)?).map(std::ops::Deref::deref)
}
pub fn asm_template_args(
&self,
node: InFile<&ast::AsmExpr>,
) -> Option<(ExprId, &[(syntax::TextRange, usize)])> {
let src = node.map(AstPtr::new).map(AstPtr::upcast::<ast::Expr>);
let expr = self.expr_map.get(&src)?;
Some(*expr).zip(self.template_map.as_ref()?.1.get(expr).map(std::ops::Deref::deref))
}
/// Get a reference to the body source map's diagnostics.
@ -446,11 +462,14 @@ impl BodySourceMap {
field_map_back,
pat_field_map_back,
expansions,
format_args_template_map,
template_map,
diagnostics,
binding_definitions,
} = self;
format_args_template_map.shrink_to_fit();
if let Some(template_map) = template_map {
template_map.0.shrink_to_fit();
template_map.1.shrink_to_fit();
}
expr_map.shrink_to_fit();
expr_map_back.shrink_to_fit();
pat_map.shrink_to_fit();
@ -463,4 +482,13 @@ impl BodySourceMap {
diagnostics.shrink_to_fit();
binding_definitions.shrink_to_fit();
}
pub fn template_map(
&self,
) -> Option<&(
FxHashMap<Idx<Expr>, Vec<(tt::TextRange, Name)>>,
FxHashMap<Idx<Expr>, Vec<(tt::TextRange, usize)>>,
)> {
self.template_map.as_deref()
}
}

View file

@ -1,6 +1,8 @@
//! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr`
//! representation.
mod asm;
use std::mem;
use base_db::CrateId;
@ -35,8 +37,8 @@ use crate::{
FormatPlaceholder, FormatSign, FormatTrait,
},
Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind,
Expr, ExprId, InlineAsm, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability,
OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, Statement,
Expr, ExprId, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability, OffsetOf, Pat,
PatId, RecordFieldPat, RecordLitField, Statement,
},
item_scope::BuiltinShadowMode,
lang_item::LangItem,
@ -693,10 +695,7 @@ impl ExprCollector<'_> {
}
}
ast::Expr::UnderscoreExpr(_) => self.alloc_expr(Expr::Underscore, syntax_ptr),
ast::Expr::AsmExpr(e) => {
let e = self.collect_expr_opt(e.expr());
self.alloc_expr(Expr::InlineAsm(InlineAsm { e }), syntax_ptr)
}
ast::Expr::AsmExpr(e) => self.lower_inline_asm(e, syntax_ptr),
ast::Expr::OffsetOfExpr(e) => {
let container = Interned::new(TypeRef::from_ast_opt(&self.ctx(), e.ty()));
let fields = e.fields().map(|it| it.as_name()).collect();
@ -1848,7 +1847,7 @@ impl ExprCollector<'_> {
},
syntax_ptr,
);
self.source_map.format_args_template_map.insert(idx, mappings);
self.source_map.template_map.get_or_insert_with(Default::default).0.insert(idx, mappings);
idx
}
@ -2061,6 +2060,7 @@ impl ExprCollector<'_> {
is_assignee_expr: false,
})
}
// endregion: format
fn lang_path(&self, lang: LangItem) -> Option<Path> {

View file

@ -0,0 +1,254 @@
//! Lowering of inline assembly.
use hir_expand::name::Name;
use intern::Symbol;
use rustc_hash::{FxHashMap, FxHashSet};
use syntax::{
ast::{self, HasName, IsString},
AstNode, AstPtr, AstToken, T,
};
use tt::{TextRange, TextSize};
use crate::{
body::lower::{ExprCollector, FxIndexSet},
hir::{AsmOperand, AsmOptions, Expr, ExprId, InlineAsm, InlineAsmRegOrRegClass},
};
impl ExprCollector<'_> {
pub(super) fn lower_inline_asm(
&mut self,
asm: ast::AsmExpr,
syntax_ptr: AstPtr<ast::Expr>,
) -> ExprId {
let mut clobber_abis = FxIndexSet::default();
let mut operands = vec![];
let mut options = AsmOptions::empty();
let mut named_pos: FxHashMap<usize, Symbol> = Default::default();
let mut named_args: FxHashMap<Symbol, usize> = Default::default();
let mut reg_args: FxHashSet<usize> = Default::default();
for piece in asm.asm_pieces() {
let slot = operands.len();
let mut lower_reg = |reg: Option<ast::AsmRegSpec>| {
let reg = reg?;
if let Some(string) = reg.string_token() {
reg_args.insert(slot);
Some(InlineAsmRegOrRegClass::Reg(Symbol::intern(string.text())))
} else {
reg.name_ref().map(|name_ref| {
InlineAsmRegOrRegClass::RegClass(Symbol::intern(&name_ref.text()))
})
}
};
let op = match piece {
ast::AsmPiece::AsmClobberAbi(clobber_abi) => {
if let Some(abi_name) = clobber_abi.string_token() {
clobber_abis.insert(Symbol::intern(abi_name.text()));
}
continue;
}
ast::AsmPiece::AsmOptions(opt) => {
opt.asm_options().for_each(|opt| {
options |= match opt.syntax().first_token().map_or(T![$], |it| it.kind()) {
T![att_syntax] => AsmOptions::ATT_SYNTAX,
T![may_unwind] => AsmOptions::MAY_UNWIND,
T![nomem] => AsmOptions::NOMEM,
T![noreturn] => AsmOptions::NORETURN,
T![nostack] => AsmOptions::NOSTACK,
T![preserves_flags] => AsmOptions::PRESERVES_FLAGS,
T![pure] => AsmOptions::PURE,
T![raw] => AsmOptions::RAW,
T![readonly] => AsmOptions::READONLY,
_ => return,
}
});
continue;
}
ast::AsmPiece::AsmOperandNamed(op) => {
let name = op.name().map(|name| Symbol::intern(&name.text()));
if let Some(name) = &name {
named_args.insert(name.clone(), slot);
named_pos.insert(slot, name.clone());
}
let Some(op) = op.asm_operand() else { continue };
(
name.map(Name::new_symbol_root),
match op {
ast::AsmOperand::AsmRegOperand(op) => {
let Some(dir_spec) = op.asm_dir_spec() else {
continue;
};
let Some(reg) = lower_reg(op.asm_reg_spec()) else {
continue;
};
if dir_spec.in_token().is_some() {
let expr = self.collect_expr_opt(
op.asm_operand_expr().and_then(|it| it.in_expr()),
);
AsmOperand::In { reg, expr }
} else if dir_spec.out_token().is_some() {
let expr = self.collect_expr_opt(
op.asm_operand_expr().and_then(|it| it.in_expr()),
);
AsmOperand::Out { reg, expr: Some(expr), late: false }
} else if dir_spec.lateout_token().is_some() {
let expr = self.collect_expr_opt(
op.asm_operand_expr().and_then(|it| it.in_expr()),
);
AsmOperand::Out { reg, expr: Some(expr), late: true }
} else if dir_spec.inout_token().is_some() {
let Some(op_expr) = op.asm_operand_expr() else { continue };
let in_expr = self.collect_expr_opt(op_expr.in_expr());
let out_expr =
op_expr.out_expr().map(|it| self.collect_expr(it));
match out_expr {
Some(out_expr) => AsmOperand::SplitInOut {
reg,
in_expr,
out_expr: Some(out_expr),
late: false,
},
None => {
AsmOperand::InOut { reg, expr: in_expr, late: false }
}
}
} else if dir_spec.inlateout_token().is_some() {
let Some(op_expr) = op.asm_operand_expr() else { continue };
let in_expr = self.collect_expr_opt(op_expr.in_expr());
let out_expr =
op_expr.out_expr().map(|it| self.collect_expr(it));
match out_expr {
Some(out_expr) => AsmOperand::SplitInOut {
reg,
in_expr,
out_expr: Some(out_expr),
late: false,
},
None => {
AsmOperand::InOut { reg, expr: in_expr, late: false }
}
}
} else {
continue;
}
}
ast::AsmOperand::AsmLabel(l) => {
AsmOperand::Label(self.collect_block_opt(l.block_expr()))
}
ast::AsmOperand::AsmConst(c) => {
AsmOperand::Const(self.collect_expr_opt(c.expr()))
}
ast::AsmOperand::AsmSym(s) => {
let Some(path) =
s.path().and_then(|p| self.expander.parse_path(self.db, p))
else {
continue;
};
AsmOperand::Sym(path)
}
},
)
}
};
operands.push(op);
}
let mut mappings = vec![];
let mut curarg = 0;
if !options.contains(AsmOptions::RAW) {
// Don't treat raw asm as a format string.
asm.template()
.filter_map(|it| Some((it.clone(), self.expand_macros_to_string(it)?)))
.for_each(|(expr, (s, is_direct_literal))| {
let Ok(text) = s.value() else {
return;
};
let template_snippet = match expr {
ast::Expr::Literal(literal) => match literal.kind() {
ast::LiteralKind::String(s) => Some(s.text().to_owned()),
_ => None,
},
_ => None,
};
let str_style = match s.quote_offsets() {
Some(offsets) => {
let raw = usize::from(offsets.quotes.0.len()) - 1;
// subtract 1 for the `r` prefix
(raw != 0).then(|| raw - 1)
}
None => None,
};
let mut parser = rustc_parse_format::Parser::new(
&text,
str_style,
template_snippet,
false,
rustc_parse_format::ParseMode::InlineAsm,
);
parser.curarg = curarg;
let mut unverified_pieces = Vec::new();
while let Some(piece) = parser.next() {
if !parser.errors.is_empty() {
break;
} else {
unverified_pieces.push(piece);
}
}
curarg = parser.curarg;
let to_span = |inner_span: rustc_parse_format::InnerSpan| {
is_direct_literal.then(|| {
TextRange::new(
inner_span.start.try_into().unwrap(),
inner_span.end.try_into().unwrap(),
) - TextSize::from(str_style.map(|it| it + 1).unwrap_or(0) as u32 + 1)
})
};
for piece in unverified_pieces {
match piece {
rustc_parse_format::Piece::String(_) => {}
rustc_parse_format::Piece::NextArgument(arg) => {
// let span = arg_spans.next();
let (operand_idx, _name) = match arg.position {
rustc_parse_format::ArgumentIs(idx)
| rustc_parse_format::ArgumentImplicitlyIs(idx) => {
if idx >= operands.len()
|| named_pos.contains_key(&idx)
|| reg_args.contains(&idx)
{
(None, None)
} else {
(Some(idx), None)
}
}
rustc_parse_format::ArgumentNamed(name) => {
let name = Symbol::intern(name);
(
named_args.get(&name).copied(),
Some(Name::new_symbol_root(name)),
)
}
};
if let Some(operand_idx) = operand_idx {
if let Some(position_span) = to_span(arg.position_span) {
mappings.push((position_span, operand_idx));
}
}
}
}
}
})
};
let idx = self.alloc_expr(
Expr::InlineAsm(InlineAsm { operands: operands.into_boxed_slice(), options }),
syntax_ptr,
);
self.source_map.template_map.get_or_insert_with(Default::default).1.insert(idx, mappings);
idx
}
}

View file

@ -307,7 +307,120 @@ pub struct OffsetOf {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct InlineAsm {
pub e: ExprId,
pub operands: Box<[(Option<Name>, AsmOperand)]>,
pub options: AsmOptions,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct AsmOptions(u16);
bitflags::bitflags! {
impl AsmOptions: u16 {
const PURE = 1 << 0;
const NOMEM = 1 << 1;
const READONLY = 1 << 2;
const PRESERVES_FLAGS = 1 << 3;
const NORETURN = 1 << 4;
const NOSTACK = 1 << 5;
const ATT_SYNTAX = 1 << 6;
const RAW = 1 << 7;
const MAY_UNWIND = 1 << 8;
}
}
impl AsmOptions {
pub const COUNT: usize = Self::all().bits().count_ones() as usize;
pub const GLOBAL_OPTIONS: Self = Self::ATT_SYNTAX.union(Self::RAW);
pub const NAKED_OPTIONS: Self = Self::ATT_SYNTAX.union(Self::RAW).union(Self::NORETURN);
pub fn human_readable_names(&self) -> Vec<&'static str> {
let mut options = vec![];
if self.contains(AsmOptions::PURE) {
options.push("pure");
}
if self.contains(AsmOptions::NOMEM) {
options.push("nomem");
}
if self.contains(AsmOptions::READONLY) {
options.push("readonly");
}
if self.contains(AsmOptions::PRESERVES_FLAGS) {
options.push("preserves_flags");
}
if self.contains(AsmOptions::NORETURN) {
options.push("noreturn");
}
if self.contains(AsmOptions::NOSTACK) {
options.push("nostack");
}
if self.contains(AsmOptions::ATT_SYNTAX) {
options.push("att_syntax");
}
if self.contains(AsmOptions::RAW) {
options.push("raw");
}
if self.contains(AsmOptions::MAY_UNWIND) {
options.push("may_unwind");
}
options
}
}
impl std::fmt::Debug for AsmOptions {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
bitflags::parser::to_writer(self, f)
}
}
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum AsmOperand {
In {
reg: InlineAsmRegOrRegClass,
expr: ExprId,
},
Out {
reg: InlineAsmRegOrRegClass,
expr: Option<ExprId>,
late: bool,
},
InOut {
reg: InlineAsmRegOrRegClass,
expr: ExprId,
late: bool,
},
SplitInOut {
reg: InlineAsmRegOrRegClass,
in_expr: ExprId,
out_expr: Option<ExprId>,
late: bool,
},
Label(ExprId),
Const(ExprId),
Sym(Path),
}
impl AsmOperand {
pub fn reg(&self) -> Option<&InlineAsmRegOrRegClass> {
match self {
Self::In { reg, .. }
| Self::Out { reg, .. }
| Self::InOut { reg, .. }
| Self::SplitInOut { reg, .. } => Some(reg),
Self::Const { .. } | Self::Sym { .. } | Self::Label { .. } => None,
}
}
pub fn is_clobber(&self) -> bool {
matches!(self, AsmOperand::Out { reg: InlineAsmRegOrRegClass::Reg(_), late: _, expr: None })
}
}
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum InlineAsmRegOrRegClass {
Reg(Symbol),
RegClass(Symbol),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@ -372,7 +485,21 @@ impl Expr {
match self {
Expr::Missing => {}
Expr::Path(_) | Expr::OffsetOf(_) => {}
Expr::InlineAsm(it) => f(it.e),
Expr::InlineAsm(it) => it.operands.iter().for_each(|(_, op)| match op {
AsmOperand::In { expr, .. }
| AsmOperand::Out { expr: Some(expr), .. }
| AsmOperand::InOut { expr, .. } => f(*expr),
AsmOperand::SplitInOut { in_expr, out_expr, .. } => {
f(*in_expr);
if let Some(out_expr) = out_expr {
f(*out_expr);
}
}
AsmOperand::Out { expr: None, .. }
| AsmOperand::Const(_)
| AsmOperand::Label(_)
| AsmOperand::Sym(_) => (),
}),
Expr::If { condition, then_branch, else_branch } => {
f(*condition);
f(*then_branch);

View file

@ -50,11 +50,7 @@ fn main() {
let i: u64 = 3;
let o: u64;
unsafe {
builtin #asm ( {
$crate::format_args!("mov {0}, {1}");
$crate::format_args!("add {0}, 5");
}
);
builtin #asm ("mov {0}, {1}", "add {0}, 5", out (reg)o, in (reg)i, );
}
}
"##]],

View file

@ -119,9 +119,8 @@ register_builtin! {
(module_path, ModulePath) => module_path_expand,
(assert, Assert) => assert_expand,
(stringify, Stringify) => stringify_expand,
(llvm_asm, LlvmAsm) => asm_expand,
(asm, Asm) => asm_expand,
(global_asm, GlobalAsm) => global_asm_expand,
(global_asm, GlobalAsm) => asm_expand,
(cfg, Cfg) => cfg_expand,
(core_panic, CorePanic) => panic_expand,
(std_panic, StdPanic) => panic_expand,
@ -324,40 +323,15 @@ fn asm_expand(
tt: &tt::Subtree,
span: Span,
) -> ExpandResult<tt::Subtree> {
// We expand all assembly snippets to `format_args!` invocations to get format syntax
// highlighting for them.
let mut literals = Vec::new();
for tt in tt.token_trees.chunks(2) {
match tt {
[tt::TokenTree::Leaf(tt::Leaf::Literal(lit))]
| [tt::TokenTree::Leaf(tt::Leaf::Literal(lit)), tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: ',', span: _, spacing: _ }))] =>
{
let dollar_krate = dollar_crate(span);
literals.push(quote!(span=>#dollar_krate::format_args!(#lit);));
}
_ => break,
}
}
let mut tt = tt.clone();
tt.delimiter.kind = tt::DelimiterKind::Parenthesis;
let pound = mk_pound(span);
let expanded = quote! {span =>
builtin #pound asm (
{##literals}
)
builtin #pound asm #tt
};
ExpandResult::ok(expanded)
}
fn global_asm_expand(
_db: &dyn ExpandDatabase,
_id: MacroCallId,
_tt: &tt::Subtree,
span: Span,
) -> ExpandResult<tt::Subtree> {
// Expand to nothing (at item-level)
ExpandResult::ok(quote! {span =>})
}
fn cfg_expand(
db: &dyn ExpandDatabase,
id: MacroCallId,

View file

@ -10,7 +10,10 @@ use chalk_ir::{
use either::Either;
use hir_def::{
data::adt::VariantData,
hir::{Array, BinaryOp, BindingId, CaptureBy, Expr, ExprId, Pat, PatId, Statement, UnaryOp},
hir::{
Array, AsmOperand, BinaryOp, BindingId, CaptureBy, Expr, ExprId, Pat, PatId, Statement,
UnaryOp,
},
lang_item::LangItem,
resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
DefWithBodyId, FieldId, HasModule, TupleFieldId, TupleId, VariantId,
@ -666,7 +669,21 @@ impl InferenceContext<'_> {
fn walk_expr_without_adjust(&mut self, tgt_expr: ExprId) {
match &self.body[tgt_expr] {
Expr::OffsetOf(_) => (),
Expr::InlineAsm(e) => self.walk_expr_without_adjust(e.e),
Expr::InlineAsm(e) => e.operands.iter().for_each(|(_, op)| match op {
AsmOperand::In { expr, .. }
| AsmOperand::Out { expr: Some(expr), .. }
| AsmOperand::InOut { expr, .. } => self.walk_expr_without_adjust(*expr),
AsmOperand::SplitInOut { in_expr, out_expr, .. } => {
self.walk_expr_without_adjust(*in_expr);
if let Some(out_expr) = out_expr {
self.walk_expr_without_adjust(*out_expr);
}
}
AsmOperand::Out { expr: None, .. }
| AsmOperand::Const(_)
| AsmOperand::Label(_)
| AsmOperand::Sym(_) => (),
}),
Expr::If { condition, then_branch, else_branch } => {
self.consume_expr(*condition);
self.consume_expr(*then_branch);

View file

@ -9,7 +9,8 @@ use chalk_ir::{cast::Cast, fold::Shift, DebruijnIndex, Mutability, TyVariableKin
use either::Either;
use hir_def::{
hir::{
ArithOp, Array, BinaryOp, ClosureKind, Expr, ExprId, LabelId, Literal, Statement, UnaryOp,
ArithOp, Array, AsmOperand, AsmOptions, BinaryOp, ClosureKind, Expr, ExprId, LabelId,
Literal, Statement, UnaryOp,
},
lang_item::{LangItem, LangItemTarget},
path::{GenericArg, GenericArgs, Path},
@ -41,9 +42,9 @@ use crate::{
primitive::{self, UintTy},
static_lifetime, to_chalk_trait_id,
traits::FnTrait,
Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, FnAbi, FnPointer, FnSig,
FnSubst, Interner, Rawness, Scalar, Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder,
TyExt, TyKind,
Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, CallableSig, FnAbi, FnPointer,
FnSig, FnSubst, Interner, Rawness, Scalar, Substitution, TraitEnvironment, TraitRef, Ty,
TyBuilder, TyExt, TyKind,
};
use super::{
@ -924,9 +925,61 @@ impl InferenceContext<'_> {
expected
}
Expr::OffsetOf(_) => TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner),
Expr::InlineAsm(it) => {
self.infer_expr_no_expect(it.e);
self.result.standard_types.unit.clone()
Expr::InlineAsm(asm) => {
let mut check_expr_asm_operand = |expr, is_input: bool| {
let ty = self.infer_expr_no_expect(expr);
// If this is an input value, we require its type to be fully resolved
// at this point. This allows us to provide helpful coercions which help
// pass the type candidate list in a later pass.
//
// We don't require output types to be resolved at this point, which
// allows them to be inferred based on how they are used later in the
// function.
if is_input {
let ty = self.resolve_ty_shallow(&ty);
match ty.kind(Interner) {
TyKind::FnDef(def, parameters) => {
let fnptr_ty = TyKind::Function(
CallableSig::from_def(self.db, *def, parameters).to_fn_ptr(),
)
.intern(Interner);
_ = self.coerce(Some(expr), &ty, &fnptr_ty);
}
TyKind::Ref(mutbl, _, base_ty) => {
let ptr_ty = TyKind::Raw(*mutbl, base_ty.clone()).intern(Interner);
_ = self.coerce(Some(expr), &ty, &ptr_ty);
}
_ => {}
}
}
};
let diverge = asm.options.contains(AsmOptions::NORETURN);
asm.operands.iter().for_each(|(_, operand)| match *operand {
AsmOperand::In { expr, .. } => check_expr_asm_operand(expr, true),
AsmOperand::Out { expr: Some(expr), .. } | AsmOperand::InOut { expr, .. } => {
check_expr_asm_operand(expr, false)
}
AsmOperand::Out { expr: None, .. } => (),
AsmOperand::SplitInOut { in_expr, out_expr, .. } => {
check_expr_asm_operand(in_expr, true);
if let Some(out_expr) = out_expr {
check_expr_asm_operand(out_expr, false);
}
}
// FIXME
AsmOperand::Label(_) => (),
// FIXME
AsmOperand::Const(_) => (),
// FIXME
AsmOperand::Sym(_) => (),
});
if diverge {
self.result.standard_types.never.clone()
} else {
self.result.standard_types.unit.clone()
}
}
};
// use a new type variable if we got unknown here

View file

@ -3,7 +3,9 @@
use chalk_ir::{cast::Cast, Mutability};
use hir_def::{
hir::{Array, BinaryOp, BindingAnnotation, Expr, ExprId, PatId, Statement, UnaryOp},
hir::{
Array, AsmOperand, BinaryOp, BindingAnnotation, Expr, ExprId, PatId, Statement, UnaryOp,
},
lang_item::LangItem,
};
use hir_expand::name::Name;
@ -39,7 +41,25 @@ impl InferenceContext<'_> {
fn infer_mut_expr_without_adjust(&mut self, tgt_expr: ExprId, mutability: Mutability) {
match &self.body[tgt_expr] {
Expr::Missing => (),
Expr::InlineAsm(e) => self.infer_mut_expr_without_adjust(e.e, Mutability::Not),
Expr::InlineAsm(e) => {
e.operands.iter().for_each(|(_, op)| match op {
AsmOperand::In { expr, .. }
| AsmOperand::Out { expr: Some(expr), .. }
| AsmOperand::InOut { expr, .. } => {
self.infer_mut_expr_without_adjust(*expr, Mutability::Not)
}
AsmOperand::SplitInOut { in_expr, out_expr, .. } => {
self.infer_mut_expr_without_adjust(*in_expr, Mutability::Not);
if let Some(out_expr) = out_expr {
self.infer_mut_expr_without_adjust(*out_expr, Mutability::Not);
}
}
AsmOperand::Out { expr: None, .. }
| AsmOperand::Label(_)
| AsmOperand::Sym(_)
| AsmOperand::Const(_) => (),
});
}
Expr::OffsetOf(_) => (),
&Expr::If { condition, then_branch, else_branch } => {
self.infer_mut_expr(condition, Mutability::Not);
@ -129,7 +149,7 @@ impl InferenceContext<'_> {
target,
}) = base_adjustments
{
// For assignee exprs `IndexMut` obiligations are already applied
// For assignee exprs `IndexMut` obligations are already applied
if !is_assignee_expr {
if let TyKind::Ref(_, _, ty) = target.kind(Interner) {
base_ty = Some(ty.clone());

View file

@ -1,7 +1,7 @@
use expect_test::expect;
use test_utils::{bench, bench_fixture, skip_slow_tests};
use crate::tests::check_infer_with_mismatches;
use crate::tests::{check_infer_with_mismatches, check_no_mismatches};
use super::{check_infer, check_types};
@ -1406,3 +1406,100 @@ fn foo(t: Tensor) {
"#,
);
}
#[test]
fn asm_unit() {
check_no_mismatches(
r#"
//- minicore: asm
fn unit() {
core::arch::asm!("")
}
"#,
);
}
#[test]
fn asm_no_return() {
check_no_mismatches(
r#"
//- minicore: asm
fn unit() -> ! {
core::arch::asm!("", options(noreturn))
}
"#,
);
}
#[test]
fn asm_things() {
check_infer(
r#"
//- minicore: asm, concat
fn main() {
unsafe {
let foo = 1;
let mut o = 0;
core::arch::asm!(
"%input = OpLoad _ {0}",
concat!("%result = ", bar, " _ %input"),
"OpStore {1} %result",
in(reg) &foo,
in(reg) &mut o,
);
o
let thread_id: usize;
core::arch::asm!("
mov {0}, gs:[0x30]
mov {0}, [{0}+0x48]
", out(reg) thread_id, options(pure, readonly, nostack));
static UNMAP_BASE: usize;
const MEM_RELEASE: usize;
static VirtualFree: usize;
const OffPtr: usize;
const OffFn: usize;
core::arch::asm!("
push {free_type}
push {free_size}
push {base}
mov eax, fs:[30h]
mov eax, [eax+8h]
add eax, {off_fn}
mov [eax-{off_fn}+{off_ptr}], eax
push eax
jmp {virtual_free}
",
off_ptr = const OffPtr,
off_fn = const OffFn,
free_size = const 0,
free_type = const MEM_RELEASE,
virtual_free = sym VirtualFree,
base = sym UNMAP_BASE,
options(noreturn),
);
}
}
"#,
expect![[r#"
!0..122 'builti...muto,)': ()
!0..136 'builti...tack))': ()
!0..449 'builti...urn),)': !
10..1236 '{ ... } }': ()
16..1234 'unsafe... }': ()
37..40 'foo': i32
43..44 '1': i32
58..63 'mut o': i32
66..67 '0': i32
293..294 'o': i32
308..317 'thread_id': usize
"#]],
)
}

View file

@ -14,8 +14,8 @@ use tt::TextRange;
use crate::{
db::HirDatabase, Adt, Callee, Const, Enum, ExternCrateDecl, Field, FieldSource, Function, Impl,
Label, LifetimeParam, LocalSource, Macro, Module, Param, SelfParam, Static, Struct, Trait,
TraitAlias, TypeAlias, TypeOrConstParam, Union, Variant,
InlineAsmOperand, Label, LifetimeParam, LocalSource, Macro, Module, Param, SelfParam, Static,
Struct, Trait, TraitAlias, TypeAlias, TypeOrConstParam, Union, Variant,
};
pub trait HasSource {
@ -292,3 +292,26 @@ impl HasSource for ExternCrateDecl {
Some(self.id.lookup(db.upcast()).source(db.upcast()))
}
}
impl HasSource for InlineAsmOperand {
type Ast = ast::AsmOperandNamed;
fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
let (_body, source_map) = db.body_with_source_map(self.owner);
if let Ok(src) = source_map.expr_syntax(self.expr) {
let root = src.file_syntax(db.upcast());
return src
.map(|ast| match ast.to_node(&root) {
ast::Expr::AsmExpr(asm) => asm
.asm_pieces()
.filter_map(|it| match it {
ast::AsmPiece::AsmOperandNamed(it) => Some(it),
_ => None,
})
.nth(self.index),
_ => None,
})
.transpose();
}
None
}
}

View file

@ -43,7 +43,7 @@ use hir_def::{
body::{BodyDiagnostic, SyntheticSyntax},
data::adt::VariantData,
generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance},
hir::{BindingAnnotation, BindingId, ExprOrPatId, LabelId, Pat},
hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, Pat},
item_tree::{AttrOwner, FieldParent, ItemTreeFieldId, ItemTreeNode},
lang_item::LangItemTarget,
layout::{self, ReprOptions, TargetDataLayout},
@ -5246,6 +5246,26 @@ impl Type {
}
}
#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
pub struct InlineAsmOperand {
owner: DefWithBodyId,
expr: ExprId,
index: usize,
}
impl InlineAsmOperand {
pub fn parent(self, _db: &dyn HirDatabase) -> DefWithBody {
self.owner.into()
}
pub fn name(&self, db: &dyn HirDatabase) -> Option<Name> {
match &db.body(self.owner)[self.expr] {
hir_def::hir::Expr::InlineAsm(e) => e.operands.get(self.index)?.0.clone(),
_ => None,
}
}
}
// FIXME: Document this
#[derive(Debug)]
pub struct Callable {

View file

@ -47,9 +47,9 @@ use crate::{
source_analyzer::{resolve_hir_path, SourceAnalyzer},
Access, Adjust, Adjustment, Adt, AutoBorrow, BindingMode, BuiltinAttr, Callable, Const,
ConstParam, Crate, DeriveHelper, Enum, Field, Function, HasSource, HirFileId, Impl, InFile,
ItemInNs, Label, LifetimeParam, Local, Macro, Module, ModuleDef, Name, OverloadedDeref, Path,
ScopeDef, Static, Struct, ToolModule, Trait, TraitAlias, TupleField, Type, TypeAlias,
TypeParam, Union, Variant, VariantDef,
InlineAsmOperand, ItemInNs, Label, LifetimeParam, Local, Macro, Module, ModuleDef, Name,
OverloadedDeref, Path, ScopeDef, Static, Struct, ToolModule, Trait, TraitAlias, TupleField,
Type, TypeAlias, TypeParam, Union, Variant, VariantDef,
};
const CONTINUE_NO_BREAKS: ControlFlow<Infallible, ()> = ControlFlow::Continue(());
@ -368,7 +368,6 @@ impl<'db> SemanticsImpl<'db> {
| BuiltinFnLikeExpander::File
| BuiltinFnLikeExpander::ModulePath
| BuiltinFnLikeExpander::Asm
| BuiltinFnLikeExpander::LlvmAsm
| BuiltinFnLikeExpander::GlobalAsm
| BuiltinFnLikeExpander::LogSyntax
| BuiltinFnLikeExpander::TraceMacros
@ -547,11 +546,11 @@ impl<'db> SemanticsImpl<'db> {
)
}
/// Retrieves all the formatting parts of the format_args! template string.
/// Retrieves all the formatting parts of the format_args! (or `asm!`) template string.
pub fn as_format_args_parts(
&self,
string: &ast::String,
) -> Option<Vec<(TextRange, Option<PathResolution>)>> {
) -> Option<Vec<(TextRange, Option<Either<PathResolution, InlineAsmOperand>>)>> {
let quote = string.open_quote_text_range()?;
let token = self.wrap_token_infile(string.syntax().clone()).into_real_file().ok()?;
@ -561,14 +560,31 @@ impl<'db> SemanticsImpl<'db> {
let string = ast::String::cast(token)?;
let literal =
string.syntax().parent().filter(|it| it.kind() == SyntaxKind::LITERAL)?;
let format_args = ast::FormatArgsExpr::cast(literal.parent()?)?;
let source_analyzer = self.analyze_no_infer(format_args.syntax())?;
let format_args = self.wrap_node_infile(format_args);
let res = source_analyzer
.as_format_args_parts(self.db, format_args.as_ref())?
.map(|(range, res)| (range + quote.end(), res))
.collect();
Some(res)
let parent = literal.parent()?;
if let Some(format_args) = ast::FormatArgsExpr::cast(parent.clone()) {
let source_analyzer = self.analyze_no_infer(format_args.syntax())?;
let format_args = self.wrap_node_infile(format_args);
let res = source_analyzer
.as_format_args_parts(self.db, format_args.as_ref())?
.map(|(range, res)| (range + quote.end(), res.map(Either::Left)))
.collect();
Some(res)
} else {
let asm = ast::AsmExpr::cast(parent)?;
let source_analyzer = self.analyze_no_infer(asm.syntax())?;
let asm = self.wrap_node_infile(asm);
let (owner, (expr, asm_parts)) = source_analyzer.as_asm_parts(asm.as_ref())?;
let res = asm_parts
.iter()
.map(|&(range, index)| {
(
range + quote.end(),
Some(Either::Right(InlineAsmOperand { owner, expr, index })),
)
})
.collect();
Some(res)
}
})()
.map_or(ControlFlow::Continue(()), ControlFlow::Break)
})
@ -579,7 +595,7 @@ impl<'db> SemanticsImpl<'db> {
&self,
original_token: SyntaxToken,
offset: TextSize,
) -> Option<(TextRange, Option<PathResolution>)> {
) -> Option<(TextRange, Option<Either<PathResolution, InlineAsmOperand>>)> {
let original_string = ast::String::cast(original_token.clone())?;
let original_token = self.wrap_token_infile(original_token).into_real_file().ok()?;
let quote = original_string.open_quote_text_range()?;
@ -600,13 +616,26 @@ impl<'db> SemanticsImpl<'db> {
&self,
string: ast::String,
offset: TextSize,
) -> Option<(TextRange, Option<PathResolution>)> {
) -> Option<(TextRange, Option<Either<PathResolution, InlineAsmOperand>>)> {
debug_assert!(offset <= string.syntax().text_range().len());
let literal = string.syntax().parent().filter(|it| it.kind() == SyntaxKind::LITERAL)?;
let format_args = ast::FormatArgsExpr::cast(literal.parent()?)?;
let source_analyzer = &self.analyze_no_infer(format_args.syntax())?;
let format_args = self.wrap_node_infile(format_args);
source_analyzer.resolve_offset_in_format_args(self.db, format_args.as_ref(), offset)
let parent = literal.parent()?;
if let Some(format_args) = ast::FormatArgsExpr::cast(parent.clone()) {
let source_analyzer = &self.analyze_no_infer(format_args.syntax())?;
let format_args = self.wrap_node_infile(format_args);
source_analyzer
.resolve_offset_in_format_args(self.db, format_args.as_ref(), offset)
.map(|(range, res)| (range, res.map(Either::Left)))
} else {
let asm = ast::AsmExpr::cast(parent)?;
let source_analyzer = &self.analyze_no_infer(asm.syntax())?;
let asm = self.wrap_node_infile(asm);
source_analyzer.resolve_offset_in_asm_template(asm.as_ref(), offset).map(
|(owner, (expr, range, index))| {
(range, Some(Either::Right(InlineAsmOperand { owner, expr, index })))
},
)
}
}
/// Maps a node down by mapping its first and last token down.
@ -1782,6 +1811,7 @@ to_def_impls![
(crate::Label, ast::Label, label_to_def),
(crate::Adt, ast::Adt, adt_to_def),
(crate::ExternCrateDecl, ast::ExternCrate, extern_crate_to_def),
(crate::InlineAsmOperand, ast::AsmOperandNamed, asm_operand_to_def),
(MacroCallId, ast::MacroCall, macro_call_to_macro_call),
];

View file

@ -110,7 +110,7 @@ use syntax::{
AstNode, AstPtr, SyntaxNode,
};
use crate::{db::HirDatabase, InFile};
use crate::{db::HirDatabase, InFile, InlineAsmOperand};
#[derive(Default)]
pub(super) struct SourceToDefCache {
@ -273,6 +273,25 @@ impl SourceToDefCtx<'_, '_> {
ast::Adt::Union(it) => self.union_to_def(InFile::new(file_id, it)).map(AdtId::UnionId),
}
}
pub(super) fn asm_operand_to_def(
&mut self,
src: InFile<&ast::AsmOperandNamed>,
) -> Option<InlineAsmOperand> {
let asm = src.value.syntax().parent().and_then(ast::AsmExpr::cast)?;
let index = asm
.asm_pieces()
.filter_map(|it| match it {
ast::AsmPiece::AsmOperandNamed(it) => Some(it),
_ => None,
})
.position(|it| it == *src.value)?;
let container = self.find_pat_or_label_container(src.syntax_ref())?;
let (_, source_map) = self.db.body_with_source_map(container);
let expr = source_map.node_expr(src.with_value(&ast::Expr::AsmExpr(asm)))?;
Some(InlineAsmOperand { owner: container, expr, index })
}
pub(super) fn bind_pat_to_def(
&mut self,
src: InFile<&ast::IdentPat>,

View file

@ -904,6 +904,20 @@ impl SourceAnalyzer {
})
}
pub(crate) fn resolve_offset_in_asm_template(
&self,
asm: InFile<&ast::AsmExpr>,
offset: TextSize,
) -> Option<(DefWithBodyId, (ExprId, TextRange, usize))> {
let (def, _, body_source_map) = self.def.as_ref()?;
let (expr, args) = body_source_map.asm_template_args(asm)?;
Some(*def).zip(
args.iter()
.find(|(range, _)| range.contains_inclusive(offset))
.map(|(range, idx)| (expr, *range, *idx)),
)
}
pub(crate) fn as_format_args_parts<'a>(
&'a self,
db: &'a dyn HirDatabase,
@ -927,6 +941,14 @@ impl SourceAnalyzer {
))
}
pub(crate) fn as_asm_parts(
&self,
asm: InFile<&ast::AsmExpr>,
) -> Option<(DefWithBodyId, (ExprId, &[(TextRange, usize)]))> {
let (def, _, body_source_map) = self.def.as_ref()?;
Some(*def).zip(body_source_map.asm_template_args(asm))
}
fn resolve_impl_method_or_trait_def(
&self,
db: &dyn HirDatabase,

View file

@ -360,6 +360,7 @@ impl CompletionItemKind {
SymbolKind::Field => "fd",
SymbolKind::Function => "fn",
SymbolKind::Impl => "im",
SymbolKind::InlineAsmRegOrRegClass => "ar",
SymbolKind::Label => "lb",
SymbolKind::LifetimeParam => "lt",
SymbolKind::Local => "lc",

View file

@ -10,8 +10,8 @@ use either::Either;
use hir::{
Adt, AsAssocItem, AsExternAssocItem, AssocItem, AttributeTemplate, BuiltinAttr, BuiltinType,
Const, Crate, DefWithBody, DeriveHelper, DocLinkDef, ExternAssocItem, ExternCrateDecl, Field,
Function, GenericParam, HasVisibility, HirDisplay, Impl, Label, Local, Macro, Module,
ModuleDef, Name, PathResolution, Semantics, Static, StaticLifetime, ToolModule, Trait,
Function, GenericParam, HasVisibility, HirDisplay, Impl, InlineAsmOperand, Label, Local, Macro,
Module, ModuleDef, Name, PathResolution, Semantics, Static, StaticLifetime, ToolModule, Trait,
TraitAlias, TupleField, TypeAlias, Variant, VariantDef, Visibility,
};
use span::Edition;
@ -50,6 +50,8 @@ pub enum Definition {
BuiltinAttr(BuiltinAttr),
ToolModule(ToolModule),
ExternCrateDecl(ExternCrateDecl),
InlineAsmRegOrRegClass(()),
InlineAsmOperand(InlineAsmOperand),
}
impl Definition {
@ -83,11 +85,13 @@ impl Definition {
Definition::Label(it) => it.module(db),
Definition::ExternCrateDecl(it) => it.module(db),
Definition::DeriveHelper(it) => it.derive().module(db),
Definition::InlineAsmOperand(it) => it.parent(db).module(db),
Definition::BuiltinAttr(_)
| Definition::BuiltinType(_)
| Definition::BuiltinLifetime(_)
| Definition::TupleField(_)
| Definition::ToolModule(_) => return None,
| Definition::ToolModule(_)
| Definition::InlineAsmRegOrRegClass(_) => return None,
};
Some(module)
}
@ -121,7 +125,9 @@ impl Definition {
| Definition::Local(_)
| Definition::GenericParam(_)
| Definition::Label(_)
| Definition::DeriveHelper(_) => return None,
| Definition::DeriveHelper(_)
| Definition::InlineAsmRegOrRegClass(_)
| Definition::InlineAsmOperand(_) => return None,
};
Some(vis)
}
@ -150,6 +156,8 @@ impl Definition {
Definition::ToolModule(_) => return None, // FIXME
Definition::DeriveHelper(it) => it.name(db),
Definition::ExternCrateDecl(it) => return it.alias_or_name(db),
Definition::InlineAsmRegOrRegClass(_) => return None,
Definition::InlineAsmOperand(op) => return op.name(db),
};
Some(name)
}
@ -212,6 +220,7 @@ impl Definition {
Definition::ToolModule(_) => None,
Definition::DeriveHelper(_) => None,
Definition::TupleField(_) => None,
Definition::InlineAsmRegOrRegClass(_) | Definition::InlineAsmOperand(_) => None,
};
docs.or_else(|| {
@ -268,6 +277,9 @@ impl Definition {
Definition::DeriveHelper(it) => {
format!("derive_helper {}", it.name(db).display(db, edition))
}
// FIXME
Definition::InlineAsmRegOrRegClass(_) => "inline_asm_reg_or_reg_class".to_owned(),
Definition::InlineAsmOperand(_) => "inline_asm_reg_operand".to_owned(),
}
}
}
@ -429,7 +441,6 @@ impl NameClass {
let _p = tracing::info_span!("NameClass::classify").entered();
let parent = name.syntax().parent()?;
let definition = match_ast! {
match parent {
ast::Item(it) => classify_item(sema, it)?,
@ -440,6 +451,7 @@ impl NameClass {
ast::Variant(it) => Definition::Variant(sema.to_def(&it)?),
ast::TypeParam(it) => Definition::GenericParam(sema.to_def(&it)?.into()),
ast::ConstParam(it) => Definition::GenericParam(sema.to_def(&it)?.into()),
ast::AsmOperandNamed(it) => Definition::InlineAsmOperand(sema.to_def(&it)?),
_ => return None,
}
};
@ -699,6 +711,9 @@ impl NameRefClass {
NameRefClass::ExternCrateShorthand { krate, decl: extern_crate }
})
},
ast::AsmRegSpec(_) => {
Some(NameRefClass::Definition(Definition::InlineAsmRegOrRegClass(())))
},
_ => None
}
}
@ -753,6 +768,18 @@ impl From<Impl> for Definition {
}
}
impl From<InlineAsmOperand> for Definition {
fn from(value: InlineAsmOperand) -> Self {
Definition::InlineAsmOperand(value)
}
}
impl From<Either<PathResolution, InlineAsmOperand>> for Definition {
fn from(value: Either<PathResolution, InlineAsmOperand>) -> Self {
value.either(Definition::from, Definition::from)
}
}
impl AsAssocItem for Definition {
fn as_assoc_item(self, db: &dyn hir::db::HirDatabase) -> Option<AssocItem> {
match self {

View file

@ -224,6 +224,7 @@ pub enum SymbolKind {
Function,
Method,
Impl,
InlineAsmRegOrRegClass,
Label,
LifetimeParam,
Local,

View file

@ -200,12 +200,14 @@ impl Definition {
.and_then(syn_ctx_is_root)
}
}
Definition::InlineAsmOperand(it) => name_range(it, sema).and_then(syn_ctx_is_root),
Definition::BuiltinType(_)
| Definition::BuiltinLifetime(_)
| Definition::BuiltinAttr(_)
| Definition::SelfType(_)
| Definition::ToolModule(_)
| Definition::TupleField(_) => return None,
| Definition::TupleField(_)
| Definition::InlineAsmRegOrRegClass(_) => return None,
// FIXME: This should be doable in theory
Definition::DeriveHelper(_) => return None,
};

View file

@ -8,10 +8,11 @@ use std::mem;
use std::{cell::LazyCell, cmp::Reverse};
use base_db::{salsa::Database, SourceDatabase, SourceRootDatabase};
use either::Either;
use hir::{
sym, Adt, AsAssocItem, DefWithBody, FileRange, FileRangeWrapper, HasAttrs, HasContainer,
HasSource, HirFileIdExt, InFile, InFileWrapper, InRealFile, ItemContainer, ModuleSource,
PathResolution, Semantics, Visibility,
HasSource, HirFileIdExt, InFile, InFileWrapper, InRealFile, InlineAsmOperand, ItemContainer,
ModuleSource, PathResolution, Semantics, Visibility,
};
use memchr::memmem::Finder;
use parser::SyntaxKind;
@ -318,6 +319,23 @@ impl Definition {
};
}
if let Definition::InlineAsmOperand(op) = self {
let def = match op.parent(db) {
DefWithBody::Function(f) => f.source(db).map(|src| src.syntax().cloned()),
DefWithBody::Const(c) => c.source(db).map(|src| src.syntax().cloned()),
DefWithBody::Static(s) => s.source(db).map(|src| src.syntax().cloned()),
DefWithBody::Variant(v) => v.source(db).map(|src| src.syntax().cloned()),
// FIXME: implement
DefWithBody::InTypeConst(_) => return SearchScope::empty(),
};
return match def {
Some(def) => SearchScope::file_range(
def.as_ref().original_file_range_with_macro_call_body(db),
),
None => SearchScope::single_file(file_id),
};
}
if let Definition::SelfType(impl_) = self {
return match impl_.source(db).map(|src| src.syntax().cloned()) {
Some(def) => SearchScope::file_range(
@ -908,7 +926,6 @@ impl<'a> FindUsages<'a> {
let finder = &Finder::new(name);
let include_self_kw_refs =
self.include_self_kw_refs.as_ref().map(|ty| (ty, Finder::new("Self")));
for (text, file_id, search_range) in Self::scope_files(sema.db, &search_scope) {
self.sema.db.unwind_if_cancelled();
let tree = LazyCell::new(move || sema.parse(file_id).syntax().clone());
@ -917,7 +934,7 @@ impl<'a> FindUsages<'a> {
for offset in Self::match_indices(&text, finder, search_range) {
tree.token_at_offset(offset).for_each(|token| {
let Some(str_token) = ast::String::cast(token.clone()) else { return };
if let Some((range, nameres)) =
if let Some((range, Some(nameres))) =
sema.check_for_format_args_template(token, offset)
{
if self.found_format_args_ref(file_id, range, str_token, nameres, sink) {}
@ -1087,19 +1104,19 @@ impl<'a> FindUsages<'a> {
file_id: EditionedFileId,
range: TextRange,
token: ast::String,
res: Option<PathResolution>,
res: Either<PathResolution, InlineAsmOperand>,
sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool,
) -> bool {
match res.map(Definition::from) {
Some(def) if def == self.def => {
let reference = FileReference {
range,
name: FileReferenceNode::FormatStringEntry(token, range),
category: ReferenceCategory::READ,
};
sink(file_id, reference)
}
_ => false,
let def = res.either(Definition::from, Definition::from);
if def == self.def {
let reference = FileReference {
range,
name: FileReferenceNode::FormatStringEntry(token, range),
category: ReferenceCategory::READ,
};
sink(file_id, reference)
} else {
false
}
}

View file

@ -219,7 +219,9 @@ pub(crate) fn resolve_doc_path_for_def(
| Definition::Local(_)
| Definition::GenericParam(_)
| Definition::Label(_)
| Definition::DeriveHelper(_) => None,
| Definition::DeriveHelper(_)
| Definition::InlineAsmRegOrRegClass(_)
| Definition::InlineAsmOperand(_) => None,
}
.map(Definition::from)
}
@ -672,7 +674,9 @@ fn filename_and_frag_for_def(
| Definition::BuiltinAttr(_)
| Definition::BuiltinLifetime(_)
| Definition::ToolModule(_)
| Definition::DeriveHelper(_) => return None,
| Definition::DeriveHelper(_)
| Definition::InlineAsmRegOrRegClass(_)
| Definition::InlineAsmOperand(_) => return None,
};
Some((def, res, None))

View file

@ -223,11 +223,12 @@ pub(crate) fn def_to_kind(db: &RootDatabase, def: Definition) -> SymbolInformati
Variable
}
}
Definition::Label(..) => Variable, // For lack of a better variant
Definition::Label(..) | Definition::InlineAsmOperand(_) => Variable, // For lack of a better variant
Definition::DeriveHelper(..) => Attribute,
Definition::BuiltinAttr(..) => Attribute,
Definition::ToolModule(..) => Module,
Definition::ExternCrateDecl(..) => Module,
Definition::InlineAsmRegOrRegClass(..) => Module,
}
}
@ -320,7 +321,9 @@ pub(crate) fn def_to_moniker(
| Definition::DeriveHelper(_)
| Definition::BuiltinLifetime(_)
| Definition::BuiltinAttr(_)
| Definition::ToolModule(_) => return None,
| Definition::ToolModule(_)
| Definition::InlineAsmRegOrRegClass(_)
| Definition::InlineAsmOperand(_) => return None,
Definition::Local(local) => {
if !local.is_param(db) {

View file

@ -237,11 +237,13 @@ impl TryToNav for Definition {
Definition::Trait(it) => it.try_to_nav(db),
Definition::TraitAlias(it) => it.try_to_nav(db),
Definition::TypeAlias(it) => it.try_to_nav(db),
Definition::ExternCrateDecl(it) => Some(it.try_to_nav(db)?),
Definition::ExternCrateDecl(it) => it.try_to_nav(db),
Definition::InlineAsmOperand(it) => it.try_to_nav(db),
Definition::BuiltinLifetime(_)
| Definition::BuiltinType(_)
| Definition::TupleField(_)
| Definition::ToolModule(_)
| Definition::InlineAsmRegOrRegClass(_)
| Definition::BuiltinAttr(_) => None,
// FIXME: The focus range should be set to the helper declaration
Definition::DeriveHelper(it) => it.derive().try_to_nav(db),
@ -693,6 +695,31 @@ impl TryToNav for hir::ConstParam {
}
}
impl TryToNav for hir::InlineAsmOperand {
fn try_to_nav(&self, db: &RootDatabase) -> Option<UpmappingResult<NavigationTarget>> {
let InFile { file_id, value } = &self.source(db)?;
let file_id = *file_id;
Some(orig_range_with_focus(db, file_id, value.syntax(), value.name()).map(
|(FileRange { file_id, range: full_range }, focus_range)| {
let edition = self.parent(db).module(db).krate().edition(db);
NavigationTarget {
file_id,
name: self
.name(db)
.map_or_else(|| "_".into(), |it| it.display(db, edition).to_smolstr()),
alias: None,
kind: Some(SymbolKind::Local),
full_range,
focus_range,
container_name: None,
description: None,
docs: None,
}
},
))
}
}
#[derive(Debug)]
pub struct UpmappingResult<T> {
/// The macro call site.

View file

@ -2975,6 +2975,62 @@ fn test() {
);
}
#[test]
fn asm_operand() {
check(
"bose",
r#"
//- minicore: asm
fn test() {
core::arch::asm!(
"push {base}",
base$0 = const 0
);
}
"#,
r#"
fn test() {
core::arch::asm!(
"push {bose}",
bose = const 0
);
}
"#,
);
}
#[test]
fn asm_operand2() {
check(
"bose",
r#"
//- minicore: asm
fn test() {
core::arch::asm!(
"push {base$0}",
"push {base}",
boo = const 0,
virtual_free = sym VIRTUAL_FREE,
base = const 0,
boo = const 0,
);
}
"#,
r#"
fn test() {
core::arch::asm!(
"push {bose}",
"push {bose}",
boo = const 0,
virtual_free = sym VIRTUAL_FREE,
bose = const 0,
boo = const 0,
);
}
"#,
);
}
#[test]
fn rename_path_inside_use_tree() {
check(

View file

@ -19,21 +19,21 @@ pub(super) fn highlight_format_string(
expanded_string: &ast::String,
range: TextRange,
) {
if !is_format_string(expanded_string) {
if is_format_string(expanded_string) {
// FIXME: Replace this with the HIR info we have now.
lex_format_specifiers(string, &mut |piece_range, kind| {
if let Some(highlight) = highlight_format_specifier(kind) {
stack.add(HlRange {
range: piece_range + range.start(),
highlight: highlight.into(),
binding_hash: None,
});
}
});
return;
}
// FIXME: Replace this with the HIR info we have now.
lex_format_specifiers(string, &mut |piece_range, kind| {
if let Some(highlight) = highlight_format_specifier(kind) {
stack.add(HlRange {
range: piece_range + range.start(),
highlight: highlight.into(),
binding_hash: None,
});
}
});
if let Some(parts) = sema.as_format_args_parts(string) {
parts.into_iter().for_each(|(range, res)| {
if let Some(res) = res {

View file

@ -534,6 +534,10 @@ pub(super) fn highlight_def(
Definition::BuiltinAttr(_) => Highlight::new(HlTag::Symbol(SymbolKind::BuiltinAttr)),
Definition::ToolModule(_) => Highlight::new(HlTag::Symbol(SymbolKind::ToolModule)),
Definition::DeriveHelper(_) => Highlight::new(HlTag::Symbol(SymbolKind::DeriveHelper)),
Definition::InlineAsmRegOrRegClass(_) => {
Highlight::new(HlTag::Symbol(SymbolKind::InlineAsmRegOrRegClass))
}
Definition::InlineAsmOperand(_) => Highlight::new(HlTag::Symbol(SymbolKind::Local)),
};
let def_crate = def.krate(db);

View file

@ -315,6 +315,8 @@ fn module_def_to_hl_tag(def: Definition) -> HlTag {
Definition::BuiltinAttr(_) => SymbolKind::BuiltinAttr,
Definition::ToolModule(_) => SymbolKind::ToolModule,
Definition::DeriveHelper(_) => SymbolKind::DeriveHelper,
Definition::InlineAsmRegOrRegClass(_) => SymbolKind::InlineAsmRegOrRegClass,
Definition::InlineAsmOperand(_) => SymbolKind::Local,
};
HlTag::Symbol(symbol)
}

View file

@ -146,6 +146,7 @@ impl HlTag {
SymbolKind::Field => "field",
SymbolKind::Function => "function",
SymbolKind::Impl => "self_type",
SymbolKind::InlineAsmRegOrRegClass => "reg",
SymbolKind::Label => "label",
SymbolKind::LifetimeParam => "lifetime",
SymbolKind::Local => "variable",

View file

@ -0,0 +1,97 @@
<style>
body { margin: 0; }
pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; }
.lifetime { color: #DFAF8F; font-style: italic; }
.label { color: #DFAF8F; font-style: italic; }
.comment { color: #7F9F7F; }
.documentation { color: #629755; }
.intra_doc_link { font-style: italic; }
.injected { opacity: 0.65 ; }
.struct, .enum { color: #7CB8BB; }
.enum_variant { color: #BDE0F3; }
.string_literal { color: #CC9393; }
.field { color: #94BFF3; }
.function { color: #93E0E3; }
.function.unsafe { color: #BC8383; }
.trait.unsafe { color: #BC8383; }
.operator.unsafe { color: #BC8383; }
.mutable.unsafe { color: #BC8383; text-decoration: underline; }
.keyword.unsafe { color: #BC8383; font-weight: bold; }
.macro.unsafe { color: #BC8383; }
.parameter { color: #94BFF3; }
.text { color: #DCDCCC; }
.type { color: #7CB8BB; }
.builtin_type { color: #8CD0D3; }
.type_param { color: #DFAF8F; }
.attribute { color: #94BFF3; }
.numeric_literal { color: #BFEBBF; }
.bool_literal { color: #BFE6EB; }
.macro { color: #94BFF3; }
.proc_macro { color: #94BFF3; text-decoration: underline; }
.derive { color: #94BFF3; font-style: italic; }
.module { color: #AFD8AF; }
.value_param { color: #DCDCCC; }
.variable { color: #DCDCCC; }
.format_specifier { color: #CC696B; }
.mutable { text-decoration: underline; }
.escape_sequence { color: #94BFF3; }
.keyword { color: #F0DFAF; font-weight: bold; }
.control { font-style: italic; }
.reference { font-style: italic; font-weight: bold; }
.const { font-weight: bolder; }
.invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
</style>
<pre><code><span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
<span class="keyword unsafe">unsafe</span> <span class="brace">{</span>
<span class="keyword">let</span> <span class="variable declaration">foo</span> <span class="operator">=</span> <span class="numeric_literal">1</span><span class="semicolon">;</span>
<span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">o</span> <span class="operator">=</span> <span class="numeric_literal">0</span><span class="semicolon">;</span>
<span class="module crate_root default_library library">core</span><span class="operator">::</span><span class="module default_library library">arch</span><span class="operator">::</span><span class="macro default_library library unsafe">asm</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span>
<span class="string_literal macro">"%input = </span><span class="variable">O</span><span class="string_literal macro">pLoad _ {</span><span class="variable">0</span><span class="string_literal macro">}"</span><span class="comma macro">,</span>
<span class="macro default_library library macro">concat</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"%result = "</span><span class="comma macro">,</span> <span class="string_literal macro">"bar"</span><span class="comma macro">,</span> <span class="string_literal macro">" _ %input"</span><span class="parenthesis macro">)</span><span class="comma macro">,</span>
<span class="string_literal macro">"OpStore {</span><span class="variable">1</span><span class="string_literal macro">} %result</span><span class="variable">"</span><span class="comma macro">,</span>
<span class="keyword control macro">in</span><span class="parenthesis macro">(</span><span class="reg library macro">reg</span><span class="parenthesis macro">)</span> <span class="operator macro">&</span><span class="variable macro">foo</span><span class="comma macro">,</span>
<span class="keyword control macro">in</span><span class="parenthesis macro">(</span><span class="reg library macro">reg</span><span class="parenthesis macro">)</span> <span class="operator macro">&</span><span class="keyword macro">mut</span> <span class="variable macro mutable">o</span><span class="comma macro">,</span>
<span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="keyword">let</span> <span class="variable declaration">thread_id</span><span class="colon">:</span> <span class="builtin_type">usize</span><span class="semicolon">;</span>
<span class="module crate_root default_library library">core</span><span class="operator">::</span><span class="module default_library library">arch</span><span class="operator">::</span><span class="macro default_library library unsafe">asm</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"
mov {</span><span class="variable">0</span><span class="string_literal macro">}, gs:[0x30]
mov {</span><span class="variable">0</span><span class="string_literal macro">}, [{</span><span class="variable">0</span><span class="string_literal macro">}+0x48]
"</span><span class="comma macro">,</span> <span class="keyword macro">out</span><span class="parenthesis macro">(</span><span class="reg library macro">reg</span><span class="parenthesis macro">)</span> <span class="variable macro">thread_id</span><span class="comma macro">,</span> <span class="keyword macro">options</span><span class="parenthesis macro">(</span><span class="keyword macro">pure</span><span class="comma macro">,</span> <span class="keyword macro">readonly</span><span class="comma macro">,</span> <span class="keyword macro">nostack</span><span class="parenthesis macro">)</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="keyword">static</span> <span class="static declaration">UNMAP_BASE</span><span class="colon">:</span> <span class="builtin_type">usize</span><span class="semicolon">;</span>
<span class="keyword const">const</span> <span class="constant const declaration">MEM_RELEASE</span><span class="colon">:</span> <span class="builtin_type">usize</span><span class="semicolon">;</span>
<span class="keyword">static</span> <span class="static declaration">VirtualFree</span><span class="colon">:</span> <span class="builtin_type">usize</span><span class="semicolon">;</span>
<span class="keyword const">const</span> <span class="constant const declaration">OffPtr</span><span class="colon">:</span> <span class="builtin_type">usize</span><span class="semicolon">;</span>
<span class="keyword const">const</span> <span class="constant const declaration">OffFn</span><span class="colon">:</span> <span class="builtin_type">usize</span><span class="semicolon">;</span>
<span class="module crate_root default_library library">core</span><span class="operator">::</span><span class="module default_library library">arch</span><span class="operator">::</span><span class="macro default_library library unsafe">asm</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"
push {</span><span class="variable">free_type</span><span class="string_literal macro">}
push {</span><span class="variable">free_size</span><span class="string_literal macro">}
push {</span><span class="variable">base</span><span class="string_literal macro">}
mov eax, fs:[30h]
mov eax, [eax+8h]
add eax, {</span><span class="variable">off_fn</span><span class="string_literal macro">}
mov [eax-{</span><span class="variable">off_fn</span><span class="string_literal macro">}+{</span><span class="variable">off_ptr</span><span class="string_literal macro">}], eax
push eax
jmp {</span><span class="variable">virtual_free</span><span class="string_literal macro">}
"</span><span class="comma macro">,</span>
<span class="variable declaration macro">off_ptr</span> <span class="operator macro">=</span> <span class="keyword macro">const</span> <span class="constant const macro">OffPtr</span><span class="comma macro">,</span>
<span class="variable declaration macro">off_fn</span> <span class="operator macro">=</span> <span class="keyword macro">const</span> <span class="constant const macro">OffFn</span><span class="comma macro">,</span>
<span class="variable declaration macro">free_size</span> <span class="operator macro">=</span> <span class="keyword macro">const</span> <span class="numeric_literal macro">0</span><span class="comma macro">,</span>
<span class="variable declaration macro">free_type</span> <span class="operator macro">=</span> <span class="keyword macro">const</span> <span class="constant const macro">MEM_RELEASE</span><span class="comma macro">,</span>
<span class="variable declaration macro">virtual_free</span> <span class="operator macro">=</span> <span class="keyword macro">sym</span> <span class="static macro">VirtualFree</span><span class="comma macro">,</span>
<span class="variable declaration macro">base</span> <span class="operator macro">=</span> <span class="keyword macro">sym</span> <span class="static macro">UNMAP_BASE</span><span class="comma macro">,</span>
<span class="keyword macro">options</span><span class="parenthesis macro">(</span><span class="keyword macro">noreturn</span><span class="parenthesis macro">)</span><span class="comma macro">,</span>
<span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="brace">}</span>
<span class="brace">}</span></code></pre>

View file

@ -165,16 +165,16 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="macro">toho</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"{}fmt"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="keyword">let</span> <span class="variable declaration">i</span><span class="colon">:</span> <span class="builtin_type">u64</span> <span class="operator">=</span> <span class="numeric_literal">3</span><span class="semicolon">;</span>
<span class="keyword">let</span> <span class="variable declaration">o</span><span class="colon">:</span> <span class="builtin_type">u64</span><span class="semicolon">;</span>
<span class="macro default_library library">asm</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span>
<span class="string_literal macro">"mov </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro">, </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span>
<span class="string_literal macro">"add </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro">, 5"</span><span class="comma macro">,</span>
<span class="none macro">out</span><span class="parenthesis macro">(</span><span class="none macro">reg</span><span class="parenthesis macro">)</span> <span class="none macro">o</span><span class="comma macro">,</span>
<span class="keyword control macro">in</span><span class="parenthesis macro">(</span><span class="none macro">reg</span><span class="parenthesis macro">)</span> <span class="none macro">i</span><span class="comma macro">,</span>
<span class="module crate_root default_library library">core</span><span class="operator">::</span><span class="module default_library library">arch</span><span class="operator">::</span><span class="macro default_library library unsafe">asm</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span>
<span class="string_literal macro">"mov {</span><span class="variable">0</span><span class="string_literal macro">}, {</span><span class="variable">1</span><span class="string_literal macro">}"</span><span class="comma macro">,</span>
<span class="string_literal macro">"add {</span><span class="variable">0</span><span class="string_literal macro">}, 5</span><span class="variable">"</span><span class="comma macro">,</span>
<span class="keyword macro">out</span><span class="parenthesis macro">(</span><span class="reg library macro">reg</span><span class="parenthesis macro">)</span> <span class="variable macro">o</span><span class="comma macro">,</span>
<span class="keyword control macro">in</span><span class="parenthesis macro">(</span><span class="reg library macro">reg</span><span class="parenthesis macro">)</span> <span class="variable macro">i</span><span class="comma macro">,</span>
<span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="keyword const">const</span> <span class="constant const declaration">CONSTANT</span><span class="colon">:</span> <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="colon">:</span>
<span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">m</span> <span class="operator">=</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
<span class="macro default_library library">format_args</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="macro default_library library macro">concat</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"{}"</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="string_literal macro">"{}"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro default_library library">format_args</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable reference">backslash</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="constant const">CONSTANT</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable mutable">m</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable macro reference">backslash</span><span class="comma macro">,</span> <span class="macro default_library library macro">format_args</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="unresolved_reference macro">foo</span><span class="comma macro">,</span> <span class="string_literal macro">"bar"</span><span class="comma macro">,</span> <span class="macro macro">toho</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="variable macro reference">backslash</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro">reuse_twice</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable reference">backslash</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro default_library library">format_args</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">backslash</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">CONSTANT</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">m</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable macro reference">backslash</span><span class="comma macro">,</span> <span class="macro default_library library macro">format_args</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="unresolved_reference macro">foo</span><span class="comma macro">,</span> <span class="string_literal macro">"bar"</span><span class="comma macro">,</span> <span class="macro macro">toho</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="variable macro reference">backslash</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="macro">reuse_twice</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">backslash</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="brace">}</span></code></pre>

View file

@ -549,7 +549,7 @@ fn main() {
toho!("{}fmt", 0);
let i: u64 = 3;
let o: u64;
asm!(
core::arch::asm!(
"mov {0}, {1}",
"add {0}, 5",
out(reg) o,
@ -1275,3 +1275,64 @@ fn f<'de, T: Deserialize<'de>>() {
);
let _ = analysis.highlight(HL_CONFIG, file_id).unwrap();
}
#[test]
fn test_asm_highlighting() {
check_highlighting(
r#"
//- minicore: asm, concat
fn main() {
unsafe {
let foo = 1;
let mut o = 0;
core::arch::asm!(
"%input = OpLoad _ {0}",
concat!("%result = ", "bar", " _ %input"),
"OpStore {1} %result",
in(reg) &foo,
in(reg) &mut o,
);
let thread_id: usize;
core::arch::asm!("
mov {0}, gs:[0x30]
mov {0}, [{0}+0x48]
", out(reg) thread_id, options(pure, readonly, nostack));
static UNMAP_BASE: usize;
const MEM_RELEASE: usize;
static VirtualFree: usize;
const OffPtr: usize;
const OffFn: usize;
core::arch::asm!("
push {free_type}
push {free_size}
push {base}
mov eax, fs:[30h]
mov eax, [eax+8h]
add eax, {off_fn}
mov [eax-{off_fn}+{off_ptr}], eax
push eax
jmp {virtual_free}
",
off_ptr = const OffPtr,
off_fn = const OffFn,
free_size = const 0,
free_type = const MEM_RELEASE,
virtual_free = sym VirtualFree,
base = sym UNMAP_BASE,
options(noreturn),
);
}
}
"#,
expect_file!["./test_data/highlight_asm.html"],
false,
);
}

View file

@ -245,7 +245,7 @@ fn tuple_expr(p: &mut Parser<'_>) -> CompletedMarker {
// test builtin_expr
// fn foo() {
// builtin#asm(0);
// builtin#asm("");
// builtin#format_args("", 0, 1, a = 2 + 3, a + b);
// builtin#offset_of(Foo, bar.baz.0);
// }
@ -297,18 +297,183 @@ 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]) {
p.bump_remap(T![asm]);
p.expect(T!['(']);
// FIXME: We just put expression here so highlighting kind of keeps working
expr(p);
p.expect(T![')']);
Some(m.complete(p, ASM_EXPR))
parse_asm_expr(p, m)
} else {
m.abandon(p);
None
}
}
// test asm_expr
// fn foo() {
// builtin#asm(
// "mov {tmp}, {x}",
// "shl {tmp}, 1",
// "shl {x}, 2",
// "add {x}, {tmp}",
// x = inout(reg) x,
// tmp = out(reg) _,
// );
// }
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");
}
let mut allow_templates = true;
while !p.at(EOF) && !p.at(T![')']) {
p.expect(T![,]);
// accept trailing commas
if p.at(T![')']) {
break;
}
let op_n = p.start();
// Parse clobber_abi
if p.eat_contextual_kw(T![clobber_abi]) {
parse_clobber_abi(p);
op_n.complete(p, ASM_CLOBBER_ABI);
allow_templates = false;
continue;
}
// Parse options
if p.eat_contextual_kw(T![options]) {
parse_options(p);
op_n.complete(p, ASM_OPTIONS);
allow_templates = false;
continue;
}
// Parse operand names
if p.at(T![ident]) && p.nth_at(1, T![=]) {
name(p);
p.bump(T![=]);
allow_templates = false;
true
} else {
false
};
let op = p.start();
let dir_spec = p.start();
if p.eat(T![in]) || p.eat_contextual_kw(T![out]) || p.eat_contextual_kw(T![lateout]) {
dir_spec.complete(p, ASM_DIR_SPEC);
parse_reg(p);
expr(p);
op.complete(p, ASM_REG_OPERAND);
op_n.complete(p, ASM_OPERAND_NAMED);
} else if p.eat_contextual_kw(T![inout]) || p.eat_contextual_kw(T![inlateout]) {
dir_spec.complete(p, ASM_DIR_SPEC);
parse_reg(p);
expr(p);
if p.eat(T![=>]) {
expr(p);
}
op.complete(p, ASM_REG_OPERAND);
op_n.complete(p, ASM_OPERAND_NAMED);
} else if p.eat_contextual_kw(T![label]) {
dir_spec.abandon(p);
block_expr(p);
op.complete(p, ASM_OPERAND_NAMED);
op_n.complete(p, ASM_LABEL);
} else if p.eat(T![const]) {
dir_spec.abandon(p);
expr(p);
op.complete(p, ASM_CONST);
op_n.complete(p, ASM_OPERAND_NAMED);
} else if p.eat_contextual_kw(T![sym]) {
dir_spec.abandon(p);
paths::type_path(p);
op.complete(p, ASM_SYM);
op_n.complete(p, ASM_OPERAND_NAMED);
} else if allow_templates {
dir_spec.abandon(p);
op.abandon(p);
op_n.abandon(p);
if expr(p).is_none() {
p.err_and_bump("expected asm template");
}
continue;
} else {
dir_spec.abandon(p);
op.abandon(p);
op_n.abandon(p);
p.err_and_bump("expected asm operand");
if p.at(T!['}']) {
break;
}
continue;
};
allow_templates = false;
}
p.expect(T![')']);
Some(m.complete(p, ASM_EXPR))
}
fn parse_options(p: &mut Parser<'_>) {
p.expect(T!['(']);
while !p.eat(T![')']) && !p.at(EOF) {
const OPTIONS: &[SyntaxKind] = &[
T![pure],
T![nomem],
T![readonly],
T![preserves_flags],
T![noreturn],
T![nostack],
T![may_unwind],
T![att_syntax],
T![raw],
];
let m = p.start();
if !OPTIONS.iter().any(|&syntax| p.eat_contextual_kw(syntax)) {
p.err_and_bump("expected asm option");
continue;
}
m.complete(p, ASM_OPTION);
// Allow trailing commas
if p.eat(T![')']) {
break;
}
p.expect(T![,]);
}
}
fn parse_clobber_abi(p: &mut Parser<'_>) {
p.expect(T!['(']);
while !p.eat(T![')']) && !p.at(EOF) {
if !p.expect(T![string]) {
break;
}
// Allow trailing commas
if p.eat(T![')']) {
break;
}
p.expect(T![,]);
}
}
fn parse_reg(p: &mut Parser<'_>) {
p.expect(T!['(']);
if p.at(T![ident]) {
let m = p.start();
name_ref(p);
m.complete(p, ASM_REG_SPEC);
} else if p.at(T![string]) {
let m = p.start();
p.bump_any();
m.complete(p, ASM_REG_SPEC);
} else {
p.err_and_bump("expected register name");
}
p.expect(T![')']);
}
// test array_expr
// fn foo() {
// [];

View file

@ -131,6 +131,14 @@ impl<'t> Parser<'t> {
true
}
pub(crate) fn eat_contextual_kw(&mut self, kind: SyntaxKind) -> bool {
if !self.at_contextual_kw(kind) {
return false;
}
self.bump_remap(kind);
true
}
fn at_composite2(&self, n: usize, k1: SyntaxKind, k2: SyntaxKind) -> bool {
self.inp.kind(self.pos + n) == k1
&& self.inp.kind(self.pos + n + 1) == k2

File diff suppressed because one or more lines are too long

View file

@ -19,6 +19,8 @@ mod ok {
#[test]
fn as_precedence() { run_and_expect_no_errors("test_data/parser/inline/ok/as_precedence.rs"); }
#[test]
fn asm_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/asm_expr.rs"); }
#[test]
fn assoc_const_eq() {
run_and_expect_no_errors("test_data/parser/inline/ok/assoc_const_eq.rs");
}

View file

@ -0,0 +1,83 @@
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 "("
WHITESPACE "\n "
LITERAL
STRING "\"mov {tmp}, {x}\""
COMMA ","
WHITESPACE "\n "
LITERAL
STRING "\"shl {tmp}, 1\""
COMMA ","
WHITESPACE "\n "
LITERAL
STRING "\"shl {x}, 2\""
COMMA ","
WHITESPACE "\n "
LITERAL
STRING "\"add {x}, {tmp}\""
COMMA ","
WHITESPACE "\n "
ASM_OPERAND_NAMED
NAME
IDENT "x"
WHITESPACE " "
EQ "="
WHITESPACE " "
ASM_REG_OPERAND
ASM_DIR_SPEC
INOUT_KW "inout"
L_PAREN "("
ASM_REG_SPEC
NAME_REF
IDENT "reg"
R_PAREN ")"
WHITESPACE " "
PATH_EXPR
PATH
PATH_SEGMENT
NAME_REF
IDENT "x"
COMMA ","
WHITESPACE "\n "
ASM_OPERAND_NAMED
NAME
IDENT "tmp"
WHITESPACE " "
EQ "="
WHITESPACE " "
ASM_REG_OPERAND
ASM_DIR_SPEC
OUT_KW "out"
L_PAREN "("
ASM_REG_SPEC
NAME_REF
IDENT "reg"
R_PAREN ")"
WHITESPACE " "
UNDERSCORE_EXPR
UNDERSCORE "_"
COMMA ","
WHITESPACE "\n "
R_PAREN ")"
SEMICOLON ";"
WHITESPACE "\n"
R_CURLY "}"
WHITESPACE "\n"

View file

@ -0,0 +1,10 @@
fn foo() {
builtin#asm(
"mov {tmp}, {x}",
"shl {tmp}, 1",
"shl {x}, 2",
"add {x}, {tmp}",
x = inout(reg) x,
tmp = out(reg) _,
);
}

View file

@ -19,7 +19,7 @@ SOURCE_FILE
ASM_KW "asm"
L_PAREN "("
LITERAL
INT_NUMBER "0"
STRING "\"\""
R_PAREN ")"
SEMICOLON ";"
WHITESPACE "\n "

View file

@ -1,5 +1,5 @@
fn foo() {
builtin#asm(0);
builtin#asm("");
builtin#format_args("", 0, 1, a = 2 + 3, a + b);
builtin#offset_of(Foo, bar.baz.0);
}

View file

@ -80,6 +80,7 @@ pub(crate) fn symbol_kind(symbol_kind: SymbolKind) -> lsp_types::SymbolKind {
| SymbolKind::ValueParam
| SymbolKind::Label => lsp_types::SymbolKind::VARIABLE,
SymbolKind::Union => lsp_types::SymbolKind::STRUCT,
SymbolKind::InlineAsmRegOrRegClass => lsp_types::SymbolKind::VARIABLE,
}
}
@ -159,6 +160,7 @@ pub(crate) fn completion_item_kind(
SymbolKind::Variant => lsp_types::CompletionItemKind::ENUM_MEMBER,
SymbolKind::BuiltinAttr => lsp_types::CompletionItemKind::FUNCTION,
SymbolKind::ToolModule => lsp_types::CompletionItemKind::MODULE,
SymbolKind::InlineAsmRegOrRegClass => lsp_types::CompletionItemKind::KEYWORD,
},
}
}
@ -702,6 +704,7 @@ fn semantic_token_type_and_modifiers(
SymbolKind::ProcMacro => types::PROC_MACRO,
SymbolKind::BuiltinAttr => types::BUILTIN_ATTRIBUTE,
SymbolKind::ToolModule => types::TOOL_MODULE,
SymbolKind::InlineAsmRegOrRegClass => types::KEYWORD,
},
HlTag::AttributeBracket => types::ATTRIBUTE_BRACKET,
HlTag::BoolLiteral => types::BOOLEAN,

View file

@ -391,8 +391,33 @@ Expr =
OffsetOfExpr =
Attr* 'builtin' '#' 'offset_of' '(' Type ',' fields:(NameRef ('.' NameRef)* ) ')'
// asm := "asm!(" format_string *("," format_string) *("," operand) [","] ")"
// global_asm := "global_asm!(" format_string *("," format_string) *("," operand) [","] ")"
// format_string := STRING_LITERAL / RAW_STRING_LITERAL
AsmExpr =
Attr* 'builtin' '#' 'asm' '(' Expr ')'
Attr* 'builtin' '#' 'asm' '(' template:(Expr (',' Expr)*) (AsmPiece (',' AsmPiece)*)? ','? ')'
// operand_expr := expr / "_" / expr "=>" expr / expr "=>" "_"
AsmOperandExpr = in_expr:Expr ('=>' out_expr:Expr)?
// dir_spec := "in" / "out" / "lateout" / "inout" / "inlateout"
AsmDirSpec = 'in' | 'out' | 'lateout' | 'inout' | 'inlateout'
// reg_spec := <register class> / "\"" <explicit register> "\""
AsmRegSpec = '@string' | NameRef
// reg_operand := [ident "="] dir_spec "(" reg_spec ")" operand_expr
AsmRegOperand = AsmDirSpec '(' AsmRegSpec ')' AsmOperandExpr
// clobber_abi := "clobber_abi(" <abi> *("," <abi>) [","] ")"
AsmClobberAbi = 'clobber_abi' '(' ('@string' (',' '@string')* ','?) ')'
// option := "pure" / "nomem" / "readonly" / "preserves_flags" / "noreturn" / "nostack" / "att_syntax" / "raw"
AsmOption = 'pure' | 'nomem' | 'readonly' | 'preserves_flags' | 'noreturn' | 'nostack' | 'att_syntax' | 'raw' | 'may_unwind'
// options := "options(" option *("," option) [","] ")"
AsmOptions = 'options' '(' AsmOption *(',' AsmOption) ','? ')'
AsmLabel = 'label' BlockExpr
AsmSym = 'sym' Path
AsmConst = 'const' Expr
// operand := reg_operand / clobber_abi / options
AsmOperand = AsmRegOperand | AsmLabel | AsmSym | AsmConst
AsmOperandNamed = (Name '=')? AsmOperand
AsmPiece = AsmOperandNamed | AsmClobberAbi | AsmOptions
FormatArgsExpr =
Attr* 'builtin' '#' 'format_args' '('

View file

@ -64,6 +64,53 @@ impl ArrayType {
pub fn semicolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![;]) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct AsmClobberAbi {
pub(crate) syntax: SyntaxNode,
}
impl AsmClobberAbi {
#[inline]
pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) }
#[inline]
pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) }
#[inline]
pub fn clobber_abi_token(&self) -> Option<SyntaxToken> {
support::token(&self.syntax, T![clobber_abi])
}
#[inline]
pub fn string_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![string]) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct AsmConst {
pub(crate) syntax: SyntaxNode,
}
impl AsmConst {
#[inline]
pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
#[inline]
pub fn const_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![const]) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct AsmDirSpec {
pub(crate) syntax: SyntaxNode,
}
impl AsmDirSpec {
#[inline]
pub fn in_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![in]) }
#[inline]
pub fn inlateout_token(&self) -> Option<SyntaxToken> {
support::token(&self.syntax, T![inlateout])
}
#[inline]
pub fn inout_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![inout]) }
#[inline]
pub fn lateout_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![lateout]) }
#[inline]
pub fn out_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![out]) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct AsmExpr {
pub(crate) syntax: SyntaxNode,
@ -71,7 +118,9 @@ pub struct AsmExpr {
impl ast::HasAttrs for AsmExpr {}
impl AsmExpr {
#[inline]
pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) }
pub fn asm_pieces(&self) -> AstChildren<AsmPiece> { support::children(&self.syntax) }
#[inline]
pub fn template(&self) -> AstChildren<Expr> { support::children(&self.syntax) }
#[inline]
pub fn pound_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![#]) }
#[inline]
@ -79,11 +128,142 @@ impl AsmExpr {
#[inline]
pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) }
#[inline]
pub fn comma_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![,]) }
#[inline]
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]) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct AsmLabel {
pub(crate) syntax: SyntaxNode,
}
impl AsmLabel {
#[inline]
pub fn block_expr(&self) -> Option<BlockExpr> { support::child(&self.syntax) }
#[inline]
pub fn label_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![label]) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct AsmOperandExpr {
pub(crate) syntax: SyntaxNode,
}
impl AsmOperandExpr {
#[inline]
pub fn in_expr(&self) -> Option<Expr> { support::child(&self.syntax) }
#[inline]
pub fn out_expr(&self) -> Option<Expr> { support::child(&self.syntax) }
#[inline]
pub fn fat_arrow_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=>]) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct AsmOperandNamed {
pub(crate) syntax: SyntaxNode,
}
impl ast::HasName for AsmOperandNamed {}
impl AsmOperandNamed {
#[inline]
pub fn asm_operand(&self) -> Option<AsmOperand> { support::child(&self.syntax) }
#[inline]
pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct AsmOption {
pub(crate) syntax: SyntaxNode,
}
impl AsmOption {
#[inline]
pub fn att_syntax_token(&self) -> Option<SyntaxToken> {
support::token(&self.syntax, T![att_syntax])
}
#[inline]
pub fn may_unwind_token(&self) -> Option<SyntaxToken> {
support::token(&self.syntax, T![may_unwind])
}
#[inline]
pub fn nomem_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![nomem]) }
#[inline]
pub fn noreturn_token(&self) -> Option<SyntaxToken> {
support::token(&self.syntax, T![noreturn])
}
#[inline]
pub fn nostack_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![nostack]) }
#[inline]
pub fn preserves_flags_token(&self) -> Option<SyntaxToken> {
support::token(&self.syntax, T![preserves_flags])
}
#[inline]
pub fn pure_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![pure]) }
#[inline]
pub fn raw_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![raw]) }
#[inline]
pub fn readonly_token(&self) -> Option<SyntaxToken> {
support::token(&self.syntax, T![readonly])
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct AsmOptions {
pub(crate) syntax: SyntaxNode,
}
impl AsmOptions {
#[inline]
pub fn asm_option(&self) -> Option<AsmOption> { support::child(&self.syntax) }
#[inline]
pub fn asm_options(&self) -> AstChildren<AsmOption> { support::children(&self.syntax) }
#[inline]
pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) }
#[inline]
pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) }
#[inline]
pub fn comma_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![,]) }
#[inline]
pub fn options_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![options]) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct AsmRegOperand {
pub(crate) syntax: SyntaxNode,
}
impl AsmRegOperand {
#[inline]
pub fn asm_dir_spec(&self) -> Option<AsmDirSpec> { support::child(&self.syntax) }
#[inline]
pub fn asm_operand_expr(&self) -> Option<AsmOperandExpr> { support::child(&self.syntax) }
#[inline]
pub fn asm_reg_spec(&self) -> Option<AsmRegSpec> { support::child(&self.syntax) }
#[inline]
pub fn l_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['(']) }
#[inline]
pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct AsmRegSpec {
pub(crate) syntax: SyntaxNode,
}
impl AsmRegSpec {
#[inline]
pub fn name_ref(&self) -> Option<NameRef> { support::child(&self.syntax) }
#[inline]
pub fn string_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![string]) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct AsmSym {
pub(crate) syntax: SyntaxNode,
}
impl AsmSym {
#[inline]
pub fn path(&self) -> Option<Path> { support::child(&self.syntax) }
#[inline]
pub fn sym_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![sym]) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct AssocItemList {
pub(crate) syntax: SyntaxNode,
@ -2051,6 +2231,21 @@ impl ast::HasGenericParams for Adt {}
impl ast::HasName for Adt {}
impl ast::HasVisibility for Adt {}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum AsmOperand {
AsmConst(AsmConst),
AsmLabel(AsmLabel),
AsmRegOperand(AsmRegOperand),
AsmSym(AsmSym),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum AsmPiece {
AsmClobberAbi(AsmClobberAbi),
AsmOperandNamed(AsmOperandNamed),
AsmOptions(AsmOptions),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum AssocItem {
Const(Const),
@ -2316,6 +2511,48 @@ impl AstNode for ArrayType {
#[inline]
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
impl AstNode for AsmClobberAbi {
#[inline]
fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_CLOBBER_ABI }
#[inline]
fn cast(syntax: SyntaxNode) -> Option<Self> {
if Self::can_cast(syntax.kind()) {
Some(Self { syntax })
} else {
None
}
}
#[inline]
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
impl AstNode for AsmConst {
#[inline]
fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_CONST }
#[inline]
fn cast(syntax: SyntaxNode) -> Option<Self> {
if Self::can_cast(syntax.kind()) {
Some(Self { syntax })
} else {
None
}
}
#[inline]
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
impl AstNode for AsmDirSpec {
#[inline]
fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_DIR_SPEC }
#[inline]
fn cast(syntax: SyntaxNode) -> Option<Self> {
if Self::can_cast(syntax.kind()) {
Some(Self { syntax })
} else {
None
}
}
#[inline]
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
impl AstNode for AsmExpr {
#[inline]
fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_EXPR }
@ -2330,6 +2567,118 @@ impl AstNode for AsmExpr {
#[inline]
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
impl AstNode for AsmLabel {
#[inline]
fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_LABEL }
#[inline]
fn cast(syntax: SyntaxNode) -> Option<Self> {
if Self::can_cast(syntax.kind()) {
Some(Self { syntax })
} else {
None
}
}
#[inline]
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
impl AstNode for AsmOperandExpr {
#[inline]
fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_OPERAND_EXPR }
#[inline]
fn cast(syntax: SyntaxNode) -> Option<Self> {
if Self::can_cast(syntax.kind()) {
Some(Self { syntax })
} else {
None
}
}
#[inline]
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
impl AstNode for AsmOperandNamed {
#[inline]
fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_OPERAND_NAMED }
#[inline]
fn cast(syntax: SyntaxNode) -> Option<Self> {
if Self::can_cast(syntax.kind()) {
Some(Self { syntax })
} else {
None
}
}
#[inline]
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
impl AstNode for AsmOption {
#[inline]
fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_OPTION }
#[inline]
fn cast(syntax: SyntaxNode) -> Option<Self> {
if Self::can_cast(syntax.kind()) {
Some(Self { syntax })
} else {
None
}
}
#[inline]
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
impl AstNode for AsmOptions {
#[inline]
fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_OPTIONS }
#[inline]
fn cast(syntax: SyntaxNode) -> Option<Self> {
if Self::can_cast(syntax.kind()) {
Some(Self { syntax })
} else {
None
}
}
#[inline]
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
impl AstNode for AsmRegOperand {
#[inline]
fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_REG_OPERAND }
#[inline]
fn cast(syntax: SyntaxNode) -> Option<Self> {
if Self::can_cast(syntax.kind()) {
Some(Self { syntax })
} else {
None
}
}
#[inline]
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
impl AstNode for AsmRegSpec {
#[inline]
fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_REG_SPEC }
#[inline]
fn cast(syntax: SyntaxNode) -> Option<Self> {
if Self::can_cast(syntax.kind()) {
Some(Self { syntax })
} else {
None
}
}
#[inline]
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
impl AstNode for AsmSym {
#[inline]
fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_SYM }
#[inline]
fn cast(syntax: SyntaxNode) -> Option<Self> {
if Self::can_cast(syntax.kind()) {
Some(Self { syntax })
} else {
None
}
}
#[inline]
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
impl AstNode for AssocItemList {
#[inline]
fn can_cast(kind: SyntaxKind) -> bool { kind == ASSOC_ITEM_LIST }
@ -4268,6 +4617,84 @@ impl AstNode for Adt {
}
}
}
impl From<AsmConst> for AsmOperand {
#[inline]
fn from(node: AsmConst) -> AsmOperand { AsmOperand::AsmConst(node) }
}
impl From<AsmLabel> for AsmOperand {
#[inline]
fn from(node: AsmLabel) -> AsmOperand { AsmOperand::AsmLabel(node) }
}
impl From<AsmRegOperand> for AsmOperand {
#[inline]
fn from(node: AsmRegOperand) -> AsmOperand { AsmOperand::AsmRegOperand(node) }
}
impl From<AsmSym> for AsmOperand {
#[inline]
fn from(node: AsmSym) -> AsmOperand { AsmOperand::AsmSym(node) }
}
impl AstNode for AsmOperand {
#[inline]
fn can_cast(kind: SyntaxKind) -> bool {
matches!(kind, ASM_CONST | ASM_LABEL | ASM_REG_OPERAND | ASM_SYM)
}
#[inline]
fn cast(syntax: SyntaxNode) -> Option<Self> {
let res = match syntax.kind() {
ASM_CONST => AsmOperand::AsmConst(AsmConst { syntax }),
ASM_LABEL => AsmOperand::AsmLabel(AsmLabel { syntax }),
ASM_REG_OPERAND => AsmOperand::AsmRegOperand(AsmRegOperand { syntax }),
ASM_SYM => AsmOperand::AsmSym(AsmSym { syntax }),
_ => return None,
};
Some(res)
}
#[inline]
fn syntax(&self) -> &SyntaxNode {
match self {
AsmOperand::AsmConst(it) => &it.syntax,
AsmOperand::AsmLabel(it) => &it.syntax,
AsmOperand::AsmRegOperand(it) => &it.syntax,
AsmOperand::AsmSym(it) => &it.syntax,
}
}
}
impl From<AsmClobberAbi> for AsmPiece {
#[inline]
fn from(node: AsmClobberAbi) -> AsmPiece { AsmPiece::AsmClobberAbi(node) }
}
impl From<AsmOperandNamed> for AsmPiece {
#[inline]
fn from(node: AsmOperandNamed) -> AsmPiece { AsmPiece::AsmOperandNamed(node) }
}
impl From<AsmOptions> for AsmPiece {
#[inline]
fn from(node: AsmOptions) -> AsmPiece { AsmPiece::AsmOptions(node) }
}
impl AstNode for AsmPiece {
#[inline]
fn can_cast(kind: SyntaxKind) -> bool {
matches!(kind, ASM_CLOBBER_ABI | ASM_OPERAND_NAMED | ASM_OPTIONS)
}
#[inline]
fn cast(syntax: SyntaxNode) -> Option<Self> {
let res = match syntax.kind() {
ASM_CLOBBER_ABI => AsmPiece::AsmClobberAbi(AsmClobberAbi { syntax }),
ASM_OPERAND_NAMED => AsmPiece::AsmOperandNamed(AsmOperandNamed { syntax }),
ASM_OPTIONS => AsmPiece::AsmOptions(AsmOptions { syntax }),
_ => return None,
};
Some(res)
}
#[inline]
fn syntax(&self) -> &SyntaxNode {
match self {
AsmPiece::AsmClobberAbi(it) => &it.syntax,
AsmPiece::AsmOperandNamed(it) => &it.syntax,
AsmPiece::AsmOptions(it) => &it.syntax,
}
}
}
impl From<Const> for AssocItem {
#[inline]
fn from(node: Const) -> AssocItem { AssocItem::Const(node) }
@ -5803,7 +6230,8 @@ impl AstNode for AnyHasName {
fn can_cast(kind: SyntaxKind) -> bool {
matches!(
kind,
CONST
ASM_OPERAND_NAMED
| CONST
| CONST_PARAM
| ENUM
| FN
@ -5832,6 +6260,10 @@ impl AstNode for AnyHasName {
#[inline]
fn syntax(&self) -> &SyntaxNode { &self.syntax }
}
impl From<AsmOperandNamed> for AnyHasName {
#[inline]
fn from(node: AsmOperandNamed) -> AnyHasName { AnyHasName { syntax: node.syntax } }
}
impl From<Const> for AnyHasName {
#[inline]
fn from(node: Const) -> AnyHasName { AnyHasName { syntax: node.syntax } }
@ -6072,6 +6504,16 @@ impl std::fmt::Display for Adt {
std::fmt::Display::fmt(self.syntax(), f)
}
}
impl std::fmt::Display for AsmOperand {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
}
}
impl std::fmt::Display for AsmPiece {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
}
}
impl std::fmt::Display for AssocItem {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
@ -6142,11 +6584,66 @@ impl std::fmt::Display for ArrayType {
std::fmt::Display::fmt(self.syntax(), f)
}
}
impl std::fmt::Display for AsmClobberAbi {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
}
}
impl std::fmt::Display for AsmConst {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
}
}
impl std::fmt::Display for AsmDirSpec {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
}
}
impl std::fmt::Display for AsmExpr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
}
}
impl std::fmt::Display for AsmLabel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
}
}
impl std::fmt::Display for AsmOperandExpr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
}
}
impl std::fmt::Display for AsmOperandNamed {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
}
}
impl std::fmt::Display for AsmOption {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
}
}
impl std::fmt::Display for AsmOptions {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
}
}
impl std::fmt::Display for AsmRegOperand {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
}
}
impl std::fmt::Display for AsmRegSpec {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
}
}
impl std::fmt::Display for AsmSym {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)
}
}
impl std::fmt::Display for AssocItemList {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.syntax(), f)

View file

@ -1475,6 +1475,19 @@ mod panicking {
}
// endregion:panic
// region:asm
mod arch {
#[rustc_builtin_macro]
pub macro asm("assembly template", $(operands,)* $(options($(option),*))?) {
/* compiler built-in */
}
#[rustc_builtin_macro]
pub macro global_asm("assembly template", $(operands,)* $(options($(option),*))?) {
/* compiler built-in */
}
}
// endregion:asm
#[macro_use]
mod macros {
// region:panic
@ -1487,16 +1500,6 @@ mod macros {
}
// endregion:panic
// region:asm
#[macro_export]
#[rustc_builtin_macro]
macro_rules! asm {
($($arg:tt)*) => {
/* compiler built-in */
};
}
// endregion:asm
// region:assert
#[macro_export]
#[rustc_builtin_macro]

View file

@ -19,6 +19,7 @@ stdx.workspace = true
proc-macro2 = "1.0.47"
quote = "1.0.20"
ungrammar = "1.16.1"
either.workspace = true
itertools.workspace = true
# Avoid adding more dependencies to this crate

View file

@ -11,9 +11,11 @@ use std::{
fs,
};
use either::Either;
use itertools::Itertools;
use proc_macro2::{Punct, Spacing};
use quote::{format_ident, quote};
use stdx::panic_context;
use ungrammar::{Grammar, Rule};
use crate::{
@ -462,6 +464,7 @@ fn generate_syntax_kinds(grammar: KindsSrc) -> String {
let tokens = grammar.tokens.iter().map(|name| format_ident!("{}", name)).collect::<Vec<_>>();
// FIXME: This generates enum kinds?
let nodes = grammar.nodes.iter().map(|name| format_ident!("{}", name)).collect::<Vec<_>>();
let ast = quote! {
@ -711,6 +714,7 @@ fn lower(grammar: &Grammar) -> AstSrc {
for &node in &nodes {
let name = grammar[node].name.clone();
let rule = &grammar[node].rule;
let _g = panic_context::enter(name.clone());
match lower_enum(grammar, rule) {
Some(variants) => {
let enum_src = AstEnumSrc { doc: Vec::new(), name, traits: Vec::new(), variants };
@ -838,11 +842,16 @@ fn lower_separated_list(
Rule::Seq(it) => it,
_ => return false,
};
let (node, repeat, trailing_sep) = match rule.as_slice() {
let (nt, repeat, trailing_sep) = match rule.as_slice() {
[Rule::Node(node), Rule::Rep(repeat), Rule::Opt(trailing_sep)] => {
(node, repeat, Some(trailing_sep))
(Either::Left(node), repeat, Some(trailing_sep))
}
[Rule::Node(node), Rule::Rep(repeat)] => (node, repeat, None),
[Rule::Node(node), Rule::Rep(repeat)] => (Either::Left(node), repeat, None),
[Rule::Token(token), Rule::Rep(repeat), Rule::Opt(trailing_sep)] => {
(Either::Right(token), repeat, Some(trailing_sep))
}
[Rule::Token(token), Rule::Rep(repeat)] => (Either::Right(token), repeat, None),
_ => return false,
};
let repeat = match &**repeat {
@ -851,15 +860,28 @@ fn lower_separated_list(
};
if !matches!(
repeat.as_slice(),
[comma, Rule::Node(n)]
if trailing_sep.map_or(true, |it| comma == &**it) && n == node
[comma, nt_]
if trailing_sep.map_or(true, |it| comma == &**it) && match (nt, nt_) {
(Either::Left(node), Rule::Node(nt_)) => node == nt_,
(Either::Right(token), Rule::Token(nt_)) => token == nt_,
_ => false,
}
) {
return false;
}
let ty = grammar[*node].name.clone();
let name = label.cloned().unwrap_or_else(|| pluralize(&to_lower_snake_case(&ty)));
let field = Field::Node { name, ty, cardinality: Cardinality::Many };
acc.push(field);
match nt {
Either::Right(token) => {
let name = clean_token_name(&grammar[*token].name);
let field = Field::Token(name);
acc.push(field);
}
Either::Left(node) => {
let ty = grammar[*node].name.clone();
let name = label.cloned().unwrap_or_else(|| pluralize(&to_lower_snake_case(&ty)));
let field = Field::Node { name, ty, cardinality: Cardinality::Many };
acc.push(field);
}
}
true
}

View file

@ -113,7 +113,31 @@ const RESERVED: &[&str] = &[
const CONTEXTUAL_KEYWORDS: &[&str] =
&["macro_rules", "union", "default", "raw", "dyn", "auto", "yeet"];
// keywords we use for special macro expansions
const CONTEXTUAL_BUILTIN_KEYWORDS: &[&str] = &["builtin", "offset_of", "format_args", "asm"];
const CONTEXTUAL_BUILTIN_KEYWORDS: &[&str] = &[
"asm",
"att_syntax",
"builtin",
"clobber_abi",
"format_args",
// "in",
"inlateout",
"inout",
"label",
"lateout",
"may_unwind",
"nomem",
"noreturn",
"nostack",
"offset_of",
"options",
"out",
"preserves_flags",
"pure",
// "raw",
"readonly",
"sym",
];
// keywords that are keywords depending on the edition
const EDITION_DEPENDENT_KEYWORDS: &[(&str, Edition)] = &[
("try", Edition::Edition2018),