@@ -30,13 +30,13 @@ use crate::{
3030 BuiltinExplicitOutlivesSuggestion , BuiltinFeatureIssueNote , BuiltinIncompleteFeatures ,
3131 BuiltinIncompleteFeaturesHelp , BuiltinInternalFeatures , BuiltinKeywordIdents ,
3232 BuiltinMissingCopyImpl , BuiltinMissingDebugImpl , BuiltinMissingDoc ,
33- BuiltinMutablesTransmutes , BuiltinNamedAsmLabel , BuiltinNoMangleGeneric ,
34- BuiltinNonShorthandFieldPatterns , BuiltinSpecialModuleNameUsed , BuiltinTrivialBounds ,
35- BuiltinTypeAliasGenericBounds , BuiltinTypeAliasGenericBoundsSuggestion ,
36- BuiltinTypeAliasWhereClause , BuiltinUngatedAsyncFnTrackCaller , BuiltinUnpermittedTypeInit ,
33+ BuiltinMutablesTransmutes , BuiltinNoMangleGeneric , BuiltinNonShorthandFieldPatterns ,
34+ BuiltinSpecialModuleNameUsed , BuiltinTrivialBounds , BuiltinTypeAliasGenericBounds ,
35+ BuiltinTypeAliasGenericBoundsSuggestion , BuiltinTypeAliasWhereClause ,
36+ BuiltinUngatedAsyncFnTrackCaller , BuiltinUnpermittedTypeInit ,
3737 BuiltinUnpermittedTypeInitSub , BuiltinUnreachablePub , BuiltinUnsafe ,
3838 BuiltinUnstableFeatures , BuiltinUnusedDocComment , BuiltinUnusedDocCommentSub ,
39- BuiltinWhileTrue , SuggestChangingAssocTypes ,
39+ BuiltinWhileTrue , InvalidAsmLabel , SuggestChangingAssocTypes ,
4040 } ,
4141 EarlyContext , EarlyLintPass , LateContext , LateLintPass , Level , LintContext ,
4242} ;
@@ -45,7 +45,7 @@ use rustc_ast::tokenstream::{TokenStream, TokenTree};
4545use rustc_ast:: visit:: { FnCtxt , FnKind } ;
4646use rustc_ast:: { self as ast, * } ;
4747use rustc_ast_pretty:: pprust:: { self , expr_to_string} ;
48- use rustc_errors:: { Applicability , LintDiagnostic , MultiSpan } ;
48+ use rustc_errors:: { Applicability , LintDiagnostic } ;
4949use rustc_feature:: { deprecated_attributes, AttributeGate , BuiltinAttribute , GateIssue , Stability } ;
5050use rustc_hir as hir;
5151use rustc_hir:: def:: { DefKind , Res } ;
@@ -69,7 +69,6 @@ use rustc_target::abi::Abi;
6969use rustc_trait_selection:: infer:: { InferCtxtExt , TyCtxtInferExt } ;
7070use rustc_trait_selection:: traits:: query:: evaluate_obligation:: InferCtxtExt as _;
7171use rustc_trait_selection:: traits:: { self , misc:: type_allowed_to_implement_copy} ;
72- use tracing:: debug;
7372
7473use crate :: nonstandard_style:: { method_context, MethodLateContext } ;
7574
@@ -2728,10 +2727,52 @@ declare_lint! {
27282727 "named labels in inline assembly" ,
27292728}
27302729
2731- declare_lint_pass ! ( NamedAsmLabels => [ NAMED_ASM_LABELS ] ) ;
2730+ declare_lint ! {
2731+ /// The `binary_asm_labels` lint detects the use of numeric labels containing only binary
2732+ /// digits in the inline `asm!` macro.
2733+ ///
2734+ /// ### Example
2735+ ///
2736+ /// ```rust,compile_fail
2737+ /// # #![feature(asm_experimental_arch)]
2738+ /// use std::arch::asm;
2739+ ///
2740+ /// fn main() {
2741+ /// unsafe {
2742+ /// asm!("0: jmp 0b");
2743+ /// }
2744+ /// }
2745+ /// ```
2746+ ///
2747+ /// {{produces}}
2748+ ///
2749+ /// ### Explanation
2750+ ///
2751+ /// A [LLVM bug] causes this code to fail to compile because it interprets the `0b` as a binary
2752+ /// literal instead of a reference to the previous local label `0`. Note that even though the
2753+ /// bug is marked as fixed, it only fixes a specific usage of intel syntax within standalone
2754+ /// files, not inline assembly. To work around this bug, don't use labels that could be
2755+ /// confused with a binary literal.
2756+ ///
2757+ /// See the explanation in [Rust By Example] for more details.
2758+ ///
2759+ /// [LLVM bug]: https://bugs.llvm.org/show_bug.cgi?id=36144
2760+ /// [Rust By Example]: https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels
2761+ pub BINARY_ASM_LABELS ,
2762+ Deny ,
2763+ "labels in inline assembly containing only 0 or 1 digits" ,
2764+ }
27322765
2733- impl < ' tcx > LateLintPass < ' tcx > for NamedAsmLabels {
2734- #[ allow( rustc:: diagnostic_outside_of_impl) ]
2766+ declare_lint_pass ! ( AsmLabels => [ NAMED_ASM_LABELS , BINARY_ASM_LABELS ] ) ;
2767+
2768+ #[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
2769+ enum AsmLabelKind {
2770+ Named ,
2771+ FormatArg ,
2772+ Binary ,
2773+ }
2774+
2775+ impl < ' tcx > LateLintPass < ' tcx > for AsmLabels {
27352776 fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx hir:: Expr < ' tcx > ) {
27362777 if let hir:: Expr {
27372778 kind : hir:: ExprKind :: InlineAsm ( hir:: InlineAsm { template_strs, options, .. } ) ,
@@ -2759,7 +2800,8 @@ impl<'tcx> LateLintPass<'tcx> for NamedAsmLabels {
27592800 None
27602801 } ;
27612802
2762- let mut found_labels = Vec :: new ( ) ;
2803+ // diagnostics are emitted per-template, so this is created here as opposed to the outer loop
2804+ let mut spans = Vec :: new ( ) ;
27632805
27642806 // A semicolon might not actually be specified as a separator for all targets, but
27652807 // it seems like LLVM accepts it always.
@@ -2782,16 +2824,21 @@ impl<'tcx> LateLintPass<'tcx> for NamedAsmLabels {
27822824
27832825 // Whether a { bracket has been seen and its } hasn't been found yet.
27842826 let mut in_bracket = false ;
2827+ let mut label_kind = AsmLabelKind :: Named ;
27852828
2786- // A label starts with an ASCII alphabetic character or . or _
27872829 // A label can also start with a format arg, if it's not a raw asm block.
27882830 if !raw && start == '{' {
27892831 in_bracket = true ;
2832+ label_kind = AsmLabelKind :: FormatArg ;
2833+ } else if matches ! ( start, '0' | '1' ) {
2834+ // Binary labels have only the characters `0` or `1`.
2835+ label_kind = AsmLabelKind :: Binary ;
27902836 } else if !( start. is_ascii_alphabetic ( ) || matches ! ( start, '.' | '_' ) ) {
2837+ // Named labels start with ASCII letters, `.` or `_`.
2838+ // anything else is not a label
27912839 break ' label_loop;
27922840 }
27932841
2794- // Labels continue with ASCII alphanumeric characters, _, or $
27952842 for c in chars {
27962843 // Inside a template format arg, any character is permitted for the
27972844 // puproses of label detection because we assume that it can be
@@ -2812,34 +2859,60 @@ impl<'tcx> LateLintPass<'tcx> for NamedAsmLabels {
28122859 } else if !raw && c == '{' {
28132860 // Start of a format arg.
28142861 in_bracket = true ;
2862+ label_kind = AsmLabelKind :: FormatArg ;
28152863 } else {
2816- if !( c. is_ascii_alphanumeric ( ) || matches ! ( c, '_' | '$' ) ) {
2864+ let can_continue = match label_kind {
2865+ // Format arg labels are considered to be named labels for the purposes
2866+ // of continuing outside of their {} pair.
2867+ AsmLabelKind :: Named | AsmLabelKind :: FormatArg => {
2868+ c. is_ascii_alphanumeric ( ) || matches ! ( c, '_' | '$' )
2869+ }
2870+ AsmLabelKind :: Binary => matches ! ( c, '0' | '1' ) ,
2871+ } ;
2872+
2873+ if !can_continue {
28172874 // The potential label had an invalid character inside it, it
28182875 // cannot be a label.
28192876 break ' label_loop;
28202877 }
28212878 }
28222879 }
28232880
2824- // If all characters passed the label checks, this is likely a label.
2825- found_labels . push ( possible_label) ;
2881+ // If all characters passed the label checks, this is a label.
2882+ spans . push ( ( find_label_span ( possible_label) , label_kind ) ) ;
28262883 start_idx = idx + 1 ;
28272884 }
28282885 }
28292886
2830- debug ! ( "NamedAsmLabels::check_expr(): found_labels: {:#?}" , & found_labels) ;
2831-
2832- if found_labels. len ( ) > 0 {
2833- let spans = found_labels
2834- . into_iter ( )
2835- . filter_map ( |label| find_label_span ( label) )
2836- . collect :: < Vec < Span > > ( ) ;
2837- // If there were labels but we couldn't find a span, combine the warnings and
2838- // use the template span.
2839- let target_spans: MultiSpan =
2840- if spans. len ( ) > 0 { spans. into ( ) } else { ( * template_span) . into ( ) } ;
2841-
2842- cx. emit_span_lint ( NAMED_ASM_LABELS , target_spans, BuiltinNamedAsmLabel ) ;
2887+ for ( span, label_kind) in spans {
2888+ let missing_precise_span = span. is_none ( ) ;
2889+ let span = span. unwrap_or ( * template_span) ;
2890+ match label_kind {
2891+ AsmLabelKind :: Named => {
2892+ cx. emit_span_lint (
2893+ NAMED_ASM_LABELS ,
2894+ span,
2895+ InvalidAsmLabel :: Named { missing_precise_span } ,
2896+ ) ;
2897+ }
2898+ AsmLabelKind :: FormatArg => {
2899+ cx. emit_span_lint (
2900+ NAMED_ASM_LABELS ,
2901+ span,
2902+ InvalidAsmLabel :: FormatArg { missing_precise_span } ,
2903+ ) ;
2904+ }
2905+ AsmLabelKind :: Binary => {
2906+ // the binary asm issue only occurs when using intel syntax
2907+ if !options. contains ( InlineAsmOptions :: ATT_SYNTAX ) {
2908+ cx. emit_span_lint (
2909+ BINARY_ASM_LABELS ,
2910+ span,
2911+ InvalidAsmLabel :: Binary { missing_precise_span, span } ,
2912+ )
2913+ }
2914+ }
2915+ } ;
28432916 }
28442917 }
28452918 }
0 commit comments