@@ -7,10 +7,10 @@ use rustc_errors::{Applicability, DiagnosticBuilder};
77use rustc_expand:: base:: { self , * } ;
88use rustc_parse:: parser:: Parser ;
99use rustc_parse_format as parse;
10- use rustc_session:: lint;
10+ use rustc_session:: lint:: { self , BuiltinLintDiagnostics } ;
1111use rustc_span:: symbol:: Ident ;
1212use rustc_span:: symbol:: { kw, sym, Symbol } ;
13- use rustc_span:: { InnerSpan , Span } ;
13+ use rustc_span:: { InnerSpan , MultiSpan , Span } ;
1414use rustc_target:: asm:: InlineAsmArch ;
1515use smallvec:: smallvec;
1616
@@ -397,7 +397,11 @@ fn parse_reg<'a>(
397397 Ok ( result)
398398}
399399
400- fn expand_preparsed_asm ( ecx : & mut ExtCtxt < ' _ > , args : AsmArgs ) -> Option < ast:: InlineAsm > {
400+ fn expand_preparsed_asm (
401+ ecx : & mut ExtCtxt < ' _ > ,
402+ args : AsmArgs ,
403+ is_local_asm : bool ,
404+ ) -> Option < ast:: InlineAsm > {
401405 let mut template = vec ! [ ] ;
402406 // Register operands are implicitly used since they are not allowed to be
403407 // referenced in the template string.
@@ -469,6 +473,72 @@ fn expand_preparsed_asm(ecx: &mut ExtCtxt<'_>, args: AsmArgs) -> Option<ast::Inl
469473 }
470474 }
471475
476+ // Lint against the use of named labels in inline `asm!` but not `global_asm!`
477+ if is_local_asm {
478+ let find_label_span = |needle : & str | -> Option < Span > {
479+ if let Some ( snippet) = & template_snippet {
480+ if let Some ( pos) = snippet. find ( needle) {
481+ let end = pos
482+ + & snippet[ pos..]
483+ . find ( |c| c == ':' )
484+ . unwrap_or ( snippet[ pos..] . len ( ) - 1 ) ;
485+ let inner = InnerSpan :: new ( pos, end) ;
486+ return Some ( template_sp. from_inner ( inner) ) ;
487+ }
488+ }
489+
490+ None
491+ } ;
492+
493+ let mut found_labels = Vec :: new ( ) ;
494+
495+ // A semicolon might not actually be specified as a separator for all targets, but it seems like LLVM accepts it always
496+ let statements = template_str. split ( |c| matches ! ( c, '\n' | ';' ) ) ;
497+ for statement in statements {
498+ // If there's a comment, trim it from the statement
499+ let statement = statement. find ( "//" ) . map_or ( statement, |idx| & statement[ ..idx] ) ;
500+ let mut start_idx = 0 ;
501+ for ( idx, _) in statement. match_indices ( ':' ) {
502+ let possible_label = statement[ start_idx..idx] . trim ( ) ;
503+ let mut chars = possible_label. chars ( ) ;
504+ if let Some ( c) = chars. next ( ) {
505+ // A label starts with an alphabetic character or . or _ and continues with alphanumeric characters, _, or $
506+ if ( c. is_alphabetic ( ) || matches ! ( c, '.' | '_' ) )
507+ && chars. all ( |c| c. is_alphanumeric ( ) || matches ! ( c, '_' | '$' ) )
508+ {
509+ found_labels. push ( possible_label) ;
510+ } else {
511+ // If we encounter a non-label, there cannot be any further labels, so stop checking
512+ break ;
513+ }
514+ } else {
515+ // Empty string means a leading ':' in this section, which is not a label
516+ break ;
517+ }
518+
519+ start_idx = idx + 1 ;
520+ }
521+ }
522+
523+ if found_labels. len ( ) > 0 {
524+ let spans =
525+ found_labels. into_iter ( ) . filter_map ( find_label_span) . collect :: < Vec < Span > > ( ) ;
526+ // If there were labels but we couldn't find a span, combine the warnings and use the template span
527+ let target_spans: MultiSpan =
528+ if spans. len ( ) > 0 { spans. into ( ) } else { template_sp. into ( ) } ;
529+ ecx. parse_sess ( ) . buffer_lint_with_diagnostic (
530+ lint:: builtin:: NAMED_ASM_LABELS ,
531+ target_spans,
532+ ecx. current_expansion . lint_node_id ,
533+ "avoid using named labels in inline assembly" ,
534+ BuiltinLintDiagnostics :: NamedAsmLabel (
535+ "only local labels of the form `<number>:` should be used in inline asm"
536+ . to_string ( ) ,
537+ ) ,
538+ ) ;
539+ }
540+ }
541+
472542 // Don't treat raw asm as a format string.
473543 if args. options . contains ( ast:: InlineAsmOptions :: RAW ) {
474544 template. push ( ast:: InlineAsmTemplatePiece :: String ( template_str. to_string ( ) ) ) ;
@@ -670,7 +740,7 @@ pub fn expand_asm<'cx>(
670740) -> Box < dyn base:: MacResult + ' cx > {
671741 match parse_args ( ecx, sp, tts, false ) {
672742 Ok ( args) => {
673- let expr = if let Some ( inline_asm) = expand_preparsed_asm ( ecx, args) {
743+ let expr = if let Some ( inline_asm) = expand_preparsed_asm ( ecx, args, true ) {
674744 P ( ast:: Expr {
675745 id : ast:: DUMMY_NODE_ID ,
676746 kind : ast:: ExprKind :: InlineAsm ( P ( inline_asm) ) ,
@@ -697,7 +767,7 @@ pub fn expand_global_asm<'cx>(
697767) -> Box < dyn base:: MacResult + ' cx > {
698768 match parse_args ( ecx, sp, tts, true ) {
699769 Ok ( args) => {
700- if let Some ( inline_asm) = expand_preparsed_asm ( ecx, args) {
770+ if let Some ( inline_asm) = expand_preparsed_asm ( ecx, args, false ) {
701771 MacEager :: items ( smallvec ! [ P ( ast:: Item {
702772 ident: Ident :: invalid( ) ,
703773 attrs: Vec :: new( ) ,
0 commit comments