@@ -30,13 +30,13 @@ use crate::{
30
30
BuiltinExplicitOutlivesSuggestion , BuiltinFeatureIssueNote , BuiltinIncompleteFeatures ,
31
31
BuiltinIncompleteFeaturesHelp , BuiltinInternalFeatures , BuiltinKeywordIdents ,
32
32
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 ,
37
37
BuiltinUnpermittedTypeInitSub , BuiltinUnreachablePub , BuiltinUnsafe ,
38
38
BuiltinUnstableFeatures , BuiltinUnusedDocComment , BuiltinUnusedDocCommentSub ,
39
- BuiltinWhileTrue , SuggestChangingAssocTypes ,
39
+ BuiltinWhileTrue , InvalidAsmLabel , SuggestChangingAssocTypes ,
40
40
} ,
41
41
EarlyContext , EarlyLintPass , LateContext , LateLintPass , Level , LintContext ,
42
42
} ;
@@ -45,7 +45,7 @@ use rustc_ast::tokenstream::{TokenStream, TokenTree};
45
45
use rustc_ast:: visit:: { FnCtxt , FnKind } ;
46
46
use rustc_ast:: { self as ast, * } ;
47
47
use rustc_ast_pretty:: pprust:: { self , expr_to_string} ;
48
- use rustc_errors:: { Applicability , LintDiagnostic , MultiSpan } ;
48
+ use rustc_errors:: { Applicability , LintDiagnostic } ;
49
49
use rustc_feature:: { deprecated_attributes, AttributeGate , BuiltinAttribute , GateIssue , Stability } ;
50
50
use rustc_hir as hir;
51
51
use rustc_hir:: def:: { DefKind , Res } ;
@@ -69,7 +69,6 @@ use rustc_target::abi::Abi;
69
69
use rustc_trait_selection:: infer:: { InferCtxtExt , TyCtxtInferExt } ;
70
70
use rustc_trait_selection:: traits:: query:: evaluate_obligation:: InferCtxtExt as _;
71
71
use rustc_trait_selection:: traits:: { self , misc:: type_allowed_to_implement_copy} ;
72
- use tracing:: debug;
73
72
74
73
use crate :: nonstandard_style:: { method_context, MethodLateContext } ;
75
74
@@ -2728,10 +2727,52 @@ declare_lint! {
2728
2727
"named labels in inline assembly" ,
2729
2728
}
2730
2729
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
+ }
2732
2765
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 {
2735
2776
fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx hir:: Expr < ' tcx > ) {
2736
2777
if let hir:: Expr {
2737
2778
kind : hir:: ExprKind :: InlineAsm ( hir:: InlineAsm { template_strs, options, .. } ) ,
@@ -2759,7 +2800,8 @@ impl<'tcx> LateLintPass<'tcx> for NamedAsmLabels {
2759
2800
None
2760
2801
} ;
2761
2802
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 ( ) ;
2763
2805
2764
2806
// A semicolon might not actually be specified as a separator for all targets, but
2765
2807
// it seems like LLVM accepts it always.
@@ -2782,16 +2824,21 @@ impl<'tcx> LateLintPass<'tcx> for NamedAsmLabels {
2782
2824
2783
2825
// Whether a { bracket has been seen and its } hasn't been found yet.
2784
2826
let mut in_bracket = false ;
2827
+ let mut label_kind = AsmLabelKind :: Named ;
2785
2828
2786
- // A label starts with an ASCII alphabetic character or . or _
2787
2829
// A label can also start with a format arg, if it's not a raw asm block.
2788
2830
if !raw && start == '{' {
2789
2831
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 ;
2790
2836
} else if !( start. is_ascii_alphabetic ( ) || matches ! ( start, '.' | '_' ) ) {
2837
+ // Named labels start with ASCII letters, `.` or `_`.
2838
+ // anything else is not a label
2791
2839
break ' label_loop;
2792
2840
}
2793
2841
2794
- // Labels continue with ASCII alphanumeric characters, _, or $
2795
2842
for c in chars {
2796
2843
// Inside a template format arg, any character is permitted for the
2797
2844
// puproses of label detection because we assume that it can be
@@ -2812,34 +2859,60 @@ impl<'tcx> LateLintPass<'tcx> for NamedAsmLabels {
2812
2859
} else if !raw && c == '{' {
2813
2860
// Start of a format arg.
2814
2861
in_bracket = true ;
2862
+ label_kind = AsmLabelKind :: FormatArg ;
2815
2863
} 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 {
2817
2874
// The potential label had an invalid character inside it, it
2818
2875
// cannot be a label.
2819
2876
break ' label_loop;
2820
2877
}
2821
2878
}
2822
2879
}
2823
2880
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 ) ) ;
2826
2883
start_idx = idx + 1 ;
2827
2884
}
2828
2885
}
2829
2886
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
+ } ;
2843
2916
}
2844
2917
}
2845
2918
}
0 commit comments