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:
commit
bdfb2f6745
49 changed files with 2070 additions and 164 deletions
|
@ -15,6 +15,7 @@ extend-ignore-re = [
|
|||
'"flate2"',
|
||||
"raison d'être",
|
||||
"inout",
|
||||
"INOUT",
|
||||
"optin"
|
||||
]
|
||||
|
||||
|
|
|
@ -2624,6 +2624,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"anyhow",
|
||||
"directories",
|
||||
"either",
|
||||
"flate2",
|
||||
"itertools",
|
||||
"proc-macro2",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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> {
|
||||
|
|
254
src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs
Normal file
254
src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs
Normal 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
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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, );
|
||||
}
|
||||
}
|
||||
"##]],
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,10 +925,62 @@ impl InferenceContext<'_> {
|
|||
expected
|
||||
}
|
||||
Expr::OffsetOf(_) => TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner),
|
||||
Expr::InlineAsm(it) => {
|
||||
self.infer_expr_no_expect(it.e);
|
||||
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
|
||||
let ty = self.insert_type_vars_shallow(ty);
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 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(|(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 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)
|
||||
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),
|
||||
];
|
||||
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -224,6 +224,7 @@ pub enum SymbolKind {
|
|||
Function,
|
||||
Method,
|
||||
Impl,
|
||||
InlineAsmRegOrRegClass,
|
||||
Label,
|
||||
LifetimeParam,
|
||||
Local,
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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 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)
|
||||
}
|
||||
_ => false,
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -19,10 +19,7 @@ pub(super) fn highlight_format_string(
|
|||
expanded_string: &ast::String,
|
||||
range: TextRange,
|
||||
) {
|
||||
if !is_format_string(expanded_string) {
|
||||
return;
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -34,6 +31,9 @@ pub(super) fn highlight_format_string(
|
|||
}
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(parts) = sema.as_format_args_parts(string) {
|
||||
parts.into_iter().for_each(|(range, res)| {
|
||||
if let Some(res) = res {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
// [];
|
||||
|
|
|
@ -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
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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"
|
|
@ -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) _,
|
||||
);
|
||||
}
|
|
@ -19,7 +19,7 @@ SOURCE_FILE
|
|||
ASM_KW "asm"
|
||||
L_PAREN "("
|
||||
LITERAL
|
||||
INT_NUMBER "0"
|
||||
STRING "\"\""
|
||||
R_PAREN ")"
|
||||
SEMICOLON ";"
|
||||
WHITESPACE "\n "
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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' '('
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -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),
|
||||
|
|
Loading…
Add table
Reference in a new issue