@@ -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
@@ -326,6 +325,12 @@ impl EarlyLintPass for UnsafeCode {
326
325
self . report_unsafe ( cx, it. span , BuiltinUnsafe :: GlobalAsm ) ;
327
326
}
328
327
328
+ ast:: ItemKind :: ForeignMod ( ForeignMod { safety, .. } ) => {
329
+ if let Safety :: Unsafe ( _) = safety {
330
+ self . report_unsafe ( cx, it. span , BuiltinUnsafe :: UnsafeExternBlock ) ;
331
+ }
332
+ }
333
+
329
334
_ => { }
330
335
}
331
336
}
@@ -2728,10 +2733,52 @@ declare_lint! {
2728
2733
"named labels in inline assembly" ,
2729
2734
}
2730
2735
2731
- declare_lint_pass ! ( NamedAsmLabels => [ NAMED_ASM_LABELS ] ) ;
2736
+ declare_lint ! {
2737
+ /// The `binary_asm_labels` lint detects the use of numeric labels containing only binary
2738
+ /// digits in the inline `asm!` macro.
2739
+ ///
2740
+ /// ### Example
2741
+ ///
2742
+ /// ```rust,compile_fail
2743
+ /// # #![feature(asm_experimental_arch)]
2744
+ /// use std::arch::asm;
2745
+ ///
2746
+ /// fn main() {
2747
+ /// unsafe {
2748
+ /// asm!("0: jmp 0b");
2749
+ /// }
2750
+ /// }
2751
+ /// ```
2752
+ ///
2753
+ /// {{produces}}
2754
+ ///
2755
+ /// ### Explanation
2756
+ ///
2757
+ /// A [LLVM bug] causes this code to fail to compile because it interprets the `0b` as a binary
2758
+ /// literal instead of a reference to the previous local label `0`. Note that even though the
2759
+ /// bug is marked as fixed, it only fixes a specific usage of intel syntax within standalone
2760
+ /// files, not inline assembly. To work around this bug, don't use labels that could be
2761
+ /// confused with a binary literal.
2762
+ ///
2763
+ /// See the explanation in [Rust By Example] for more details.
2764
+ ///
2765
+ /// [LLVM bug]: https://bugs.llvm.org/show_bug.cgi?id=36144
2766
+ /// [Rust By Example]: https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels
2767
+ pub BINARY_ASM_LABELS ,
2768
+ Deny ,
2769
+ "labels in inline assembly containing only 0 or 1 digits" ,
2770
+ }
2771
+
2772
+ declare_lint_pass ! ( AsmLabels => [ NAMED_ASM_LABELS , BINARY_ASM_LABELS ] ) ;
2773
+
2774
+ #[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
2775
+ enum AsmLabelKind {
2776
+ Named ,
2777
+ FormatArg ,
2778
+ Binary ,
2779
+ }
2732
2780
2733
- impl < ' tcx > LateLintPass < ' tcx > for NamedAsmLabels {
2734
- #[ allow( rustc:: diagnostic_outside_of_impl) ]
2781
+ impl < ' tcx > LateLintPass < ' tcx > for AsmLabels {
2735
2782
fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx hir:: Expr < ' tcx > ) {
2736
2783
if let hir:: Expr {
2737
2784
kind : hir:: ExprKind :: InlineAsm ( hir:: InlineAsm { template_strs, options, .. } ) ,
@@ -2759,7 +2806,8 @@ impl<'tcx> LateLintPass<'tcx> for NamedAsmLabels {
2759
2806
None
2760
2807
} ;
2761
2808
2762
- let mut found_labels = Vec :: new ( ) ;
2809
+ // diagnostics are emitted per-template, so this is created here as opposed to the outer loop
2810
+ let mut spans = Vec :: new ( ) ;
2763
2811
2764
2812
// A semicolon might not actually be specified as a separator for all targets, but
2765
2813
// it seems like LLVM accepts it always.
@@ -2782,16 +2830,21 @@ impl<'tcx> LateLintPass<'tcx> for NamedAsmLabels {
2782
2830
2783
2831
// Whether a { bracket has been seen and its } hasn't been found yet.
2784
2832
let mut in_bracket = false ;
2833
+ let mut label_kind = AsmLabelKind :: Named ;
2785
2834
2786
- // A label starts with an ASCII alphabetic character or . or _
2787
2835
// A label can also start with a format arg, if it's not a raw asm block.
2788
2836
if !raw && start == '{' {
2789
2837
in_bracket = true ;
2838
+ label_kind = AsmLabelKind :: FormatArg ;
2839
+ } else if matches ! ( start, '0' | '1' ) {
2840
+ // Binary labels have only the characters `0` or `1`.
2841
+ label_kind = AsmLabelKind :: Binary ;
2790
2842
} else if !( start. is_ascii_alphabetic ( ) || matches ! ( start, '.' | '_' ) ) {
2843
+ // Named labels start with ASCII letters, `.` or `_`.
2844
+ // anything else is not a label
2791
2845
break ' label_loop;
2792
2846
}
2793
2847
2794
- // Labels continue with ASCII alphanumeric characters, _, or $
2795
2848
for c in chars {
2796
2849
// Inside a template format arg, any character is permitted for the
2797
2850
// puproses of label detection because we assume that it can be
@@ -2812,34 +2865,60 @@ impl<'tcx> LateLintPass<'tcx> for NamedAsmLabels {
2812
2865
} else if !raw && c == '{' {
2813
2866
// Start of a format arg.
2814
2867
in_bracket = true ;
2868
+ label_kind = AsmLabelKind :: FormatArg ;
2815
2869
} else {
2816
- if !( c. is_ascii_alphanumeric ( ) || matches ! ( c, '_' | '$' ) ) {
2870
+ let can_continue = match label_kind {
2871
+ // Format arg labels are considered to be named labels for the purposes
2872
+ // of continuing outside of their {} pair.
2873
+ AsmLabelKind :: Named | AsmLabelKind :: FormatArg => {
2874
+ c. is_ascii_alphanumeric ( ) || matches ! ( c, '_' | '$' )
2875
+ }
2876
+ AsmLabelKind :: Binary => matches ! ( c, '0' | '1' ) ,
2877
+ } ;
2878
+
2879
+ if !can_continue {
2817
2880
// The potential label had an invalid character inside it, it
2818
2881
// cannot be a label.
2819
2882
break ' label_loop;
2820
2883
}
2821
2884
}
2822
2885
}
2823
2886
2824
- // If all characters passed the label checks, this is likely a label.
2825
- found_labels . push ( possible_label) ;
2887
+ // If all characters passed the label checks, this is a label.
2888
+ spans . push ( ( find_label_span ( possible_label) , label_kind ) ) ;
2826
2889
start_idx = idx + 1 ;
2827
2890
}
2828
2891
}
2829
2892
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 ) ;
2893
+ for ( span, label_kind) in spans {
2894
+ let missing_precise_span = span. is_none ( ) ;
2895
+ let span = span. unwrap_or ( * template_span) ;
2896
+ match label_kind {
2897
+ AsmLabelKind :: Named => {
2898
+ cx. emit_span_lint (
2899
+ NAMED_ASM_LABELS ,
2900
+ span,
2901
+ InvalidAsmLabel :: Named { missing_precise_span } ,
2902
+ ) ;
2903
+ }
2904
+ AsmLabelKind :: FormatArg => {
2905
+ cx. emit_span_lint (
2906
+ NAMED_ASM_LABELS ,
2907
+ span,
2908
+ InvalidAsmLabel :: FormatArg { missing_precise_span } ,
2909
+ ) ;
2910
+ }
2911
+ AsmLabelKind :: Binary => {
2912
+ // the binary asm issue only occurs when using intel syntax
2913
+ if !options. contains ( InlineAsmOptions :: ATT_SYNTAX ) {
2914
+ cx. emit_span_lint (
2915
+ BINARY_ASM_LABELS ,
2916
+ span,
2917
+ InvalidAsmLabel :: Binary { missing_precise_span, span } ,
2918
+ )
2919
+ }
2920
+ }
2921
+ } ;
2843
2922
}
2844
2923
}
2845
2924
}
0 commit comments