@@ -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
@@ -326,6 +325,12 @@ impl EarlyLintPass for UnsafeCode {
326325 self . report_unsafe ( cx, it. span , BuiltinUnsafe :: GlobalAsm ) ;
327326 }
328327
328+ ast:: ItemKind :: ForeignMod ( ForeignMod { safety, .. } ) => {
329+ if let Safety :: Unsafe ( _) = safety {
330+ self . report_unsafe ( cx, it. span , BuiltinUnsafe :: UnsafeExternBlock ) ;
331+ }
332+ }
333+
329334 _ => { }
330335 }
331336 }
@@ -2728,10 +2733,52 @@ declare_lint! {
27282733 "named labels in inline assembly" ,
27292734}
27302735
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+ }
27322780
2733- impl < ' tcx > LateLintPass < ' tcx > for NamedAsmLabels {
2734- #[ allow( rustc:: diagnostic_outside_of_impl) ]
2781+ impl < ' tcx > LateLintPass < ' tcx > for AsmLabels {
27352782 fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx hir:: Expr < ' tcx > ) {
27362783 if let hir:: Expr {
27372784 kind : hir:: ExprKind :: InlineAsm ( hir:: InlineAsm { template_strs, options, .. } ) ,
@@ -2759,7 +2806,8 @@ impl<'tcx> LateLintPass<'tcx> for NamedAsmLabels {
27592806 None
27602807 } ;
27612808
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 ( ) ;
27632811
27642812 // A semicolon might not actually be specified as a separator for all targets, but
27652813 // it seems like LLVM accepts it always.
@@ -2782,16 +2830,21 @@ impl<'tcx> LateLintPass<'tcx> for NamedAsmLabels {
27822830
27832831 // Whether a { bracket has been seen and its } hasn't been found yet.
27842832 let mut in_bracket = false ;
2833+ let mut label_kind = AsmLabelKind :: Named ;
27852834
2786- // A label starts with an ASCII alphabetic character or . or _
27872835 // A label can also start with a format arg, if it's not a raw asm block.
27882836 if !raw && start == '{' {
27892837 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 ;
27902842 } else if !( start. is_ascii_alphabetic ( ) || matches ! ( start, '.' | '_' ) ) {
2843+ // Named labels start with ASCII letters, `.` or `_`.
2844+ // anything else is not a label
27912845 break ' label_loop;
27922846 }
27932847
2794- // Labels continue with ASCII alphanumeric characters, _, or $
27952848 for c in chars {
27962849 // Inside a template format arg, any character is permitted for the
27972850 // puproses of label detection because we assume that it can be
@@ -2812,34 +2865,60 @@ impl<'tcx> LateLintPass<'tcx> for NamedAsmLabels {
28122865 } else if !raw && c == '{' {
28132866 // Start of a format arg.
28142867 in_bracket = true ;
2868+ label_kind = AsmLabelKind :: FormatArg ;
28152869 } 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 {
28172880 // The potential label had an invalid character inside it, it
28182881 // cannot be a label.
28192882 break ' label_loop;
28202883 }
28212884 }
28222885 }
28232886
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 ) ) ;
28262889 start_idx = idx + 1 ;
28272890 }
28282891 }
28292892
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+ } ;
28432922 }
28442923 }
28452924 }
0 commit comments