Skip to content

Commit a74f19a

Browse files
committed
Exempt #[naked] fns from named_asm_labels lint
1 parent 0035d9d commit a74f19a

File tree

10 files changed

+290
-148
lines changed

10 files changed

+290
-148
lines changed

compiler/rustc_ast/src/ast.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2028,6 +2028,7 @@ pub enum InlineAsmOperand {
20282028
#[derive(Clone, Encodable, Decodable, Debug)]
20292029
pub struct InlineAsm {
20302030
pub template: Vec<InlineAsmTemplatePiece>,
2031+
pub template_strs: Box<[(Symbol, Option<Symbol>, Span)]>,
20312032
pub operands: Vec<(InlineAsmOperand, Span)>,
20322033
pub clobber_abi: Option<(Symbol, Span)>,
20332034
pub options: InlineAsmOptions,

compiler/rustc_ast/src/visit.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,20 @@ use crate::token;
1919
use rustc_span::symbol::{Ident, Symbol};
2020
use rustc_span::Span;
2121

22-
#[derive(Copy, Clone, PartialEq)]
22+
#[derive(Copy, Clone, Debug, PartialEq)]
2323
pub enum AssocCtxt {
2424
Trait,
2525
Impl,
2626
}
2727

28-
#[derive(Copy, Clone, PartialEq)]
28+
#[derive(Copy, Clone, Debug, PartialEq)]
2929
pub enum FnCtxt {
3030
Free,
3131
Foreign,
3232
Assoc(AssocCtxt),
3333
}
3434

35-
#[derive(Copy, Clone)]
35+
#[derive(Copy, Clone, Debug)]
3636
pub enum FnKind<'a> {
3737
/// E.g., `fn foo()`, `fn foo(&self)`, or `extern "Abi" fn foo()`.
3838
Fn(FnCtxt, Ident, &'a FnSig, &'a Visibility, Option<&'a Block>),

compiler/rustc_ast_lowering/src/asm.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -392,8 +392,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
392392

393393
let operands = self.arena.alloc_from_iter(operands);
394394
let template = self.arena.alloc_from_iter(asm.template.iter().cloned());
395+
let template_strs = self.arena.alloc_from_iter(asm.template_strs.iter().cloned());
395396
let line_spans = self.arena.alloc_slice(&asm.line_spans[..]);
396-
let hir_asm = hir::InlineAsm { template, operands, options: asm.options, line_spans };
397+
let hir_asm =
398+
hir::InlineAsm { template, template_strs, operands, options: asm.options, line_spans };
397399
self.arena.alloc(hir_asm)
398400
}
399401
}

compiler/rustc_builtin_macros/src/asm.rs

+14-76
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ use rustc_errors::{Applicability, DiagnosticBuilder};
77
use rustc_expand::base::{self, *};
88
use rustc_parse::parser::Parser;
99
use rustc_parse_format as parse;
10-
use rustc_session::lint::{self, BuiltinLintDiagnostics};
10+
use rustc_session::lint;
1111
use rustc_span::symbol::Ident;
1212
use rustc_span::symbol::{kw, sym, Symbol};
13-
use rustc_span::{InnerSpan, MultiSpan, Span};
13+
use rustc_span::{InnerSpan, Span};
1414
use rustc_target::asm::InlineAsmArch;
1515
use smallvec::smallvec;
1616

@@ -484,11 +484,7 @@ fn parse_reg<'a>(
484484
Ok(result)
485485
}
486486

487-
fn expand_preparsed_asm(
488-
ecx: &mut ExtCtxt<'_>,
489-
args: AsmArgs,
490-
is_local_asm: bool,
491-
) -> Option<ast::InlineAsm> {
487+
fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::InlineAsm> {
492488
let mut template = vec![];
493489
// Register operands are implicitly used since they are not allowed to be
494490
// referenced in the template string.
@@ -501,6 +497,8 @@ fn expand_preparsed_asm(
501497
let mut line_spans = Vec::with_capacity(args.templates.len());
502498
let mut curarg = 0;
503499

500+
let mut template_strs = Vec::with_capacity(args.templates.len());
501+
504502
for template_expr in args.templates.into_iter() {
505503
if !template.is_empty() {
506504
template.push(ast::InlineAsmTemplatePiece::String("\n".to_string()));
@@ -524,8 +522,13 @@ fn expand_preparsed_asm(
524522
ast::StrStyle::Raw(raw) => Some(raw as usize),
525523
};
526524

527-
let template_str = &template_str.as_str();
528525
let template_snippet = ecx.source_map().span_to_snippet(template_sp).ok();
526+
template_strs.push((
527+
template_str,
528+
template_snippet.as_ref().map(|s| Symbol::intern(s)),
529+
template_sp,
530+
));
531+
let template_str = &template_str.as_str();
529532

530533
if let Some(InlineAsmArch::X86 | InlineAsmArch::X86_64) = ecx.sess.asm_arch {
531534
let find_span = |needle: &str| -> Span {
@@ -560,72 +563,6 @@ fn expand_preparsed_asm(
560563
}
561564
}
562565

563-
// Lint against the use of named labels in inline `asm!` but not `global_asm!`
564-
if is_local_asm {
565-
let find_label_span = |needle: &str| -> Option<Span> {
566-
if let Some(snippet) = &template_snippet {
567-
if let Some(pos) = snippet.find(needle) {
568-
let end = pos
569-
+ &snippet[pos..]
570-
.find(|c| c == ':')
571-
.unwrap_or(snippet[pos..].len() - 1);
572-
let inner = InnerSpan::new(pos, end);
573-
return Some(template_sp.from_inner(inner));
574-
}
575-
}
576-
577-
None
578-
};
579-
580-
let mut found_labels = Vec::new();
581-
582-
// A semicolon might not actually be specified as a separator for all targets, but it seems like LLVM accepts it always
583-
let statements = template_str.split(|c| matches!(c, '\n' | ';'));
584-
for statement in statements {
585-
// If there's a comment, trim it from the statement
586-
let statement = statement.find("//").map_or(statement, |idx| &statement[..idx]);
587-
let mut start_idx = 0;
588-
for (idx, _) in statement.match_indices(':') {
589-
let possible_label = statement[start_idx..idx].trim();
590-
let mut chars = possible_label.chars();
591-
if let Some(c) = chars.next() {
592-
// A label starts with an alphabetic character or . or _ and continues with alphanumeric characters, _, or $
593-
if (c.is_alphabetic() || matches!(c, '.' | '_'))
594-
&& chars.all(|c| c.is_alphanumeric() || matches!(c, '_' | '$'))
595-
{
596-
found_labels.push(possible_label);
597-
} else {
598-
// If we encounter a non-label, there cannot be any further labels, so stop checking
599-
break;
600-
}
601-
} else {
602-
// Empty string means a leading ':' in this section, which is not a label
603-
break;
604-
}
605-
606-
start_idx = idx + 1;
607-
}
608-
}
609-
610-
if found_labels.len() > 0 {
611-
let spans =
612-
found_labels.into_iter().filter_map(find_label_span).collect::<Vec<Span>>();
613-
// If there were labels but we couldn't find a span, combine the warnings and use the template span
614-
let target_spans: MultiSpan =
615-
if spans.len() > 0 { spans.into() } else { template_sp.into() };
616-
ecx.parse_sess().buffer_lint_with_diagnostic(
617-
lint::builtin::NAMED_ASM_LABELS,
618-
target_spans,
619-
ecx.current_expansion.lint_node_id,
620-
"avoid using named labels in inline assembly",
621-
BuiltinLintDiagnostics::NamedAsmLabel(
622-
"only local labels of the form `<number>:` should be used in inline asm"
623-
.to_string(),
624-
),
625-
);
626-
}
627-
}
628-
629566
// Don't treat raw asm as a format string.
630567
if args.options.contains(ast::InlineAsmOptions::RAW) {
631568
template.push(ast::InlineAsmTemplatePiece::String(template_str.to_string()));
@@ -819,6 +756,7 @@ fn expand_preparsed_asm(
819756

820757
Some(ast::InlineAsm {
821758
template,
759+
template_strs: template_strs.into_boxed_slice(),
822760
operands: args.operands,
823761
clobber_abi: args.clobber_abi,
824762
options: args.options,
@@ -833,7 +771,7 @@ pub fn expand_asm<'cx>(
833771
) -> Box<dyn base::MacResult + 'cx> {
834772
match parse_args(ecx, sp, tts, false) {
835773
Ok(args) => {
836-
let expr = if let Some(inline_asm) = expand_preparsed_asm(ecx, args, true) {
774+
let expr = if let Some(inline_asm) = expand_preparsed_asm(ecx, args) {
837775
P(ast::Expr {
838776
id: ast::DUMMY_NODE_ID,
839777
kind: ast::ExprKind::InlineAsm(P(inline_asm)),
@@ -860,7 +798,7 @@ pub fn expand_global_asm<'cx>(
860798
) -> Box<dyn base::MacResult + 'cx> {
861799
match parse_args(ecx, sp, tts, true) {
862800
Ok(args) => {
863-
if let Some(inline_asm) = expand_preparsed_asm(ecx, args, false) {
801+
if let Some(inline_asm) = expand_preparsed_asm(ecx, args) {
864802
MacEager::items(smallvec![P(ast::Item {
865803
ident: Ident::invalid(),
866804
attrs: Vec::new(),

compiler/rustc_hir/src/hir.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2386,6 +2386,7 @@ impl<'hir> InlineAsmOperand<'hir> {
23862386
#[derive(Debug, HashStable_Generic)]
23872387
pub struct InlineAsm<'hir> {
23882388
pub template: &'hir [InlineAsmTemplatePiece],
2389+
pub template_strs: &'hir [(Symbol, Option<Symbol>, Span)],
23892390
pub operands: &'hir [(InlineAsmOperand<'hir>, Span)],
23902391
pub options: InlineAsmOptions,
23912392
pub line_spans: &'hir [Span],

compiler/rustc_lint/src/builtin.rs

+134-3
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,11 @@ use rustc_middle::ty::print::with_no_trimmed_paths;
4545
use rustc_middle::ty::subst::{GenericArgKind, Subst};
4646
use rustc_middle::ty::Instance;
4747
use rustc_middle::ty::{self, layout::LayoutError, Ty, TyCtxt};
48-
use rustc_session::lint::FutureIncompatibilityReason;
48+
use rustc_session::lint::{BuiltinLintDiagnostics, FutureIncompatibilityReason};
4949
use rustc_session::Session;
50-
use rustc_span::edition::Edition;
51-
use rustc_span::source_map::Spanned;
5250
use rustc_span::symbol::{kw, sym, Ident, Symbol};
51+
use rustc_span::{edition::Edition, InnerSpan};
52+
use rustc_span::{source_map::Spanned, MultiSpan};
5353
use rustc_span::{BytePos, Span};
5454
use rustc_target::abi::{LayoutOf, VariantIdx};
5555
use rustc_trait_selection::traits::misc::can_type_implement_copy;
@@ -3140,3 +3140,134 @@ impl<'tcx> LateLintPass<'tcx> for DerefNullPtr {
31403140
}
31413141
}
31423142
}
3143+
3144+
declare_lint! {
3145+
/// The `named_asm_labels` lint detects the use of named labels in the
3146+
/// inline `asm!` macro.
3147+
///
3148+
/// ### Example
3149+
///
3150+
/// ```rust,compile_fail
3151+
/// #![feature(asm)]
3152+
/// fn main() {
3153+
/// unsafe {
3154+
/// asm!("foo: bar");
3155+
/// }
3156+
/// }
3157+
/// ```
3158+
///
3159+
/// {{produces}}
3160+
///
3161+
/// ### Explanation
3162+
///
3163+
/// LLVM is allowed to duplicate inline assembly blocks for any
3164+
/// reason, for example when it is in a function that gets inlined. Because
3165+
/// of this, GNU assembler [local labels] *must* be used instead of labels
3166+
/// with a name. Using named labels might cause assembler or linker errors.
3167+
///
3168+
/// See the [unstable book] for more details.
3169+
///
3170+
/// [local labels]: https://sourceware.org/binutils/docs/as/Symbol-Names.html#Local-Labels
3171+
/// [unstable book]: https://doc.rust-lang.org/nightly/unstable-book/library-features/asm.html#labels
3172+
pub NAMED_ASM_LABELS,
3173+
Deny,
3174+
"named labels in inline assembly",
3175+
}
3176+
3177+
declare_lint_pass!(NamedAsmLabels => [NAMED_ASM_LABELS]);
3178+
3179+
impl<'tcx> LateLintPass<'tcx> for NamedAsmLabels {
3180+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
3181+
if let hir::Expr {
3182+
kind: hir::ExprKind::InlineAsm(hir::InlineAsm { template_strs, .. }),
3183+
..
3184+
} = expr
3185+
{
3186+
let hir = cx.tcx.hir();
3187+
if let Some(enclosing_body) = cx.enclosing_body {
3188+
let fn_id = hir.get_parent_item(enclosing_body.hir_id);
3189+
let attrs = hir.attrs(fn_id);
3190+
// Process lint only if the containing fn does not have a `#[naked]` attribute
3191+
if attrs.iter().any(|attr| attr.has_name(sym::naked)) {
3192+
return;
3193+
}
3194+
3195+
for (template_sym, template_snippet, template_span) in template_strs.iter() {
3196+
let template_str = &template_sym.as_str();
3197+
let find_label_span = |needle: &str| -> Option<Span> {
3198+
if let Some(snippet) = template_snippet {
3199+
let snippet = snippet.as_str();
3200+
if let Some(pos) = snippet.find(needle) {
3201+
let end = pos
3202+
+ &snippet[pos..]
3203+
.find(|c| c == ':')
3204+
.unwrap_or(snippet[pos..].len() - 1);
3205+
let inner = InnerSpan::new(pos, end);
3206+
return Some(template_span.from_inner(inner));
3207+
}
3208+
}
3209+
3210+
None
3211+
};
3212+
3213+
let mut found_labels = Vec::new();
3214+
3215+
// A semicolon might not actually be specified as a separator for all targets, but it seems like LLVM accepts it always
3216+
let statements = template_str.split(|c| matches!(c, '\n' | ';'));
3217+
for statement in statements {
3218+
// If there's a comment, trim it from the statement
3219+
let statement =
3220+
statement.find("//").map_or(statement, |idx| &statement[..idx]);
3221+
let mut start_idx = 0;
3222+
for (idx, _) in statement.match_indices(':') {
3223+
let possible_label = statement[start_idx..idx].trim();
3224+
let mut chars = possible_label.chars();
3225+
if let Some(c) = chars.next() {
3226+
// A label starts with an alphabetic character or . or _ and continues with alphanumeric characters, _, or $
3227+
if (c.is_alphabetic() || matches!(c, '.' | '_'))
3228+
&& chars.all(|c| c.is_alphanumeric() || matches!(c, '_' | '$'))
3229+
{
3230+
found_labels.push(possible_label);
3231+
} else {
3232+
// If we encounter a non-label, there cannot be any further labels, so stop checking
3233+
break;
3234+
}
3235+
} else {
3236+
// Empty string means a leading ':' in this section, which is not a label
3237+
break;
3238+
}
3239+
3240+
start_idx = idx + 1;
3241+
}
3242+
}
3243+
3244+
debug!("NamedAsmLabels::check_expr(): found_labels: {:#?}", &found_labels);
3245+
3246+
if found_labels.len() > 0 {
3247+
let spans = found_labels
3248+
.into_iter()
3249+
.filter_map(|label| find_label_span(label))
3250+
.collect::<Vec<Span>>();
3251+
// If there were labels but we couldn't find a span, combine the warnings and use the template span
3252+
let target_spans: MultiSpan =
3253+
if spans.len() > 0 { spans.into() } else { (*template_span).into() };
3254+
3255+
cx.lookup_with_diagnostics(
3256+
NAMED_ASM_LABELS,
3257+
Some(target_spans),
3258+
|diag| {
3259+
let mut err =
3260+
diag.build("avoid using named labels in inline assembly");
3261+
err.emit();
3262+
},
3263+
BuiltinLintDiagnostics::NamedAsmLabel(
3264+
"only local labels of the form `<number>:` should be used in inline asm"
3265+
.to_string(),
3266+
),
3267+
);
3268+
}
3269+
}
3270+
}
3271+
}
3272+
}
3273+
}

compiler/rustc_lint/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ macro_rules! late_lint_passes {
171171
NonPanicFmt: NonPanicFmt,
172172
NoopMethodCall: NoopMethodCall,
173173
InvalidAtomicOrdering: InvalidAtomicOrdering,
174+
NamedAsmLabels: NamedAsmLabels,
174175
]
175176
);
176177
};

0 commit comments

Comments
 (0)