@@ -16,8 +16,8 @@ declare_clippy_lint! {
1616/// Checks that common macros are used with consistent bracing. 
1717/// 
1818/// ### Why is this bad? 
19- /// This is mostly a consistency lint although using () or []  
20- /// doesn't give you a semicolon in item position, which can be unexpected . 
19+ /// Having non-conventional braces on well-stablished macros can be confusing  
20+ /// when debugging, and they bring incosistencies with the rest of the ecosystem . 
2121/// 
2222/// ### Example 
2323/// ```no_run 
@@ -33,8 +33,12 @@ declare_clippy_lint! {
3333    "check consistent use of braces in macro" 
3434} 
3535
36- /// The (callsite span, (open brace, close brace), source snippet) 
37- type  MacroInfo  = ( Span ,  ( char ,  char ) ,  SourceText ) ; 
36+ struct  MacroInfo  { 
37+     callsite_span :  Span , 
38+     callsite_snippet :  SourceText , 
39+     old_open_brace :  char , 
40+     braces :  ( char ,  char ) , 
41+ } 
3842
3943pub  struct  MacroBraces  { 
4044    macro_braces :  FxHashMap < String ,  ( char ,  char ) > , 
@@ -54,30 +58,58 @@ impl_lint_pass!(MacroBraces => [NONSTANDARD_MACRO_BRACES]);
5458
5559impl  EarlyLintPass  for  MacroBraces  { 
5660    fn  check_item ( & mut  self ,  cx :  & EarlyContext < ' _ > ,  item :  & ast:: Item )  { 
57-         if  let  Some ( ( span,  braces,  snip) )  = is_offending_macro ( cx,  item. span ,  self )  { 
58-             emit_help ( cx,  & snip,  braces,  span) ; 
59-             self . done . insert ( span) ; 
61+         if  let  Some ( MacroInfo  { 
62+             callsite_span, 
63+             callsite_snippet, 
64+             braces, 
65+             ..
66+         } )  = is_offending_macro ( cx,  item. span ,  self ) 
67+         { 
68+             emit_help ( cx,  & callsite_snippet,  braces,  callsite_span,  false ) ; 
69+             self . done . insert ( callsite_span) ; 
6070        } 
6171    } 
6272
6373    fn  check_stmt ( & mut  self ,  cx :  & EarlyContext < ' _ > ,  stmt :  & ast:: Stmt )  { 
64-         if  let  Some ( ( span,  braces,  snip) )  = is_offending_macro ( cx,  stmt. span ,  self )  { 
65-             emit_help ( cx,  & snip,  braces,  span) ; 
66-             self . done . insert ( span) ; 
74+         if  let  Some ( MacroInfo  { 
75+             callsite_span, 
76+             callsite_snippet, 
77+             braces, 
78+             old_open_brace, 
79+         } )  = is_offending_macro ( cx,  stmt. span ,  self ) 
80+         { 
81+             // if we turn `macro!{}` into `macro!()`/`macro![]`, we'll no longer get the implicit 
82+             // trailing semicolon, see #9913 
83+             // NOTE: `stmt.kind != StmtKind::MacCall` because `EarlyLintPass` happens after macro expansion 
84+             let  add_semi = matches ! ( stmt. kind,  ast:: StmtKind :: Expr ( ..) )  && old_open_brace == '{' ; 
85+             emit_help ( cx,  & callsite_snippet,  braces,  callsite_span,  add_semi) ; 
86+             self . done . insert ( callsite_span) ; 
6787        } 
6888    } 
6989
7090    fn  check_expr ( & mut  self ,  cx :  & EarlyContext < ' _ > ,  expr :  & ast:: Expr )  { 
71-         if  let  Some ( ( span,  braces,  snip) )  = is_offending_macro ( cx,  expr. span ,  self )  { 
72-             emit_help ( cx,  & snip,  braces,  span) ; 
73-             self . done . insert ( span) ; 
91+         if  let  Some ( MacroInfo  { 
92+             callsite_span, 
93+             callsite_snippet, 
94+             braces, 
95+             ..
96+         } )  = is_offending_macro ( cx,  expr. span ,  self ) 
97+         { 
98+             emit_help ( cx,  & callsite_snippet,  braces,  callsite_span,  false ) ; 
99+             self . done . insert ( callsite_span) ; 
74100        } 
75101    } 
76102
77103    fn  check_ty ( & mut  self ,  cx :  & EarlyContext < ' _ > ,  ty :  & ast:: Ty )  { 
78-         if  let  Some ( ( span,  braces,  snip) )  = is_offending_macro ( cx,  ty. span ,  self )  { 
79-             emit_help ( cx,  & snip,  braces,  span) ; 
80-             self . done . insert ( span) ; 
104+         if  let  Some ( MacroInfo  { 
105+             callsite_span, 
106+             braces, 
107+             callsite_snippet, 
108+             ..
109+         } )  = is_offending_macro ( cx,  ty. span ,  self ) 
110+         { 
111+             emit_help ( cx,  & callsite_snippet,  braces,  callsite_span,  false ) ; 
112+             self . done . insert ( callsite_span) ; 
81113        } 
82114    } 
83115} 
@@ -90,39 +122,44 @@ fn is_offending_macro(cx: &EarlyContext<'_>, span: Span, mac_braces: &MacroBrace
90122                . last ( ) 
91123                . is_some_and ( |e| e. macro_def_id . is_some_and ( DefId :: is_local) ) 
92124    } ; 
93-     let  span_call_site  = span. ctxt ( ) . outer_expn_data ( ) . call_site ; 
125+     let  callsite_span  = span. ctxt ( ) . outer_expn_data ( ) . call_site ; 
94126    if  let  ExpnKind :: Macro ( MacroKind :: Bang ,  mac_name)  = span. ctxt ( ) . outer_expn_data ( ) . kind 
95127        && let  name = mac_name. as_str ( ) 
96128        && let  Some ( & braces)  = mac_braces. macro_braces . get ( name) 
97-         && let  Some ( snip)  = span_call_site . get_source_text ( cx) 
129+         && let  Some ( snip)  = callsite_span . get_source_text ( cx) 
98130        // we must check only invocation sites 
99131        // https://github.com/rust-lang/rust-clippy/issues/7422 
100-         && snip. starts_with ( & format ! ( "{name}!" ) ) 
132+         && let  Some ( macro_args_str)  = snip. strip_prefix ( name) . and_then ( |snip| snip. strip_prefix ( '!' ) ) 
133+         && let  Some ( old_open_brace @ ( '{'  | '('  | '[' ) )  = macro_args_str. trim_start ( ) . chars ( ) . next ( ) 
134+         && old_open_brace != braces. 0 
101135        && unnested_or_local ( ) 
102-         // make formatting consistent 
103-         && let  c = snip. replace ( ' ' ,  "" ) 
104-         && !c. starts_with ( & format ! ( "{name}!{}" ,  braces. 0 ) ) 
105-         && !mac_braces. done . contains ( & span_call_site) 
136+         && !mac_braces. done . contains ( & callsite_span) 
106137    { 
107-         Some ( ( span_call_site,  braces,  snip) ) 
138+         Some ( MacroInfo  { 
139+             callsite_span, 
140+             callsite_snippet :  snip, 
141+             old_open_brace, 
142+             braces, 
143+         } ) 
108144    }  else  { 
109145        None 
110146    } 
111147} 
112148
113- fn  emit_help ( cx :  & EarlyContext < ' _ > ,  snip :  & str ,  ( open,  close) :  ( char ,  char ) ,  span :  Span )  { 
149+ fn  emit_help ( cx :  & EarlyContext < ' _ > ,  snip :  & str ,  ( open,  close) :  ( char ,  char ) ,  span :  Span ,  add_semi :  bool )  { 
150+     let  semi = if  add_semi {  ";"  }  else  {  ""  } ; 
114151    if  let  Some ( ( macro_name,  macro_args_str) )  = snip. split_once ( '!' )  { 
115152        let  mut  macro_args = macro_args_str. trim ( ) . to_string ( ) ; 
116153        // now remove the wrong braces 
117-         macro_args. remove ( 0 ) ; 
118154        macro_args. pop ( ) ; 
155+         macro_args. remove ( 0 ) ; 
119156        span_lint_and_sugg ( 
120157            cx, 
121158            NONSTANDARD_MACRO_BRACES , 
122159            span, 
123160            format ! ( "use of irregular braces for `{macro_name}!` macro" ) , 
124161            "consider writing" , 
125-             format ! ( "{macro_name}!{open}{macro_args}{close}" ) , 
162+             format ! ( "{macro_name}!{open}{macro_args}{close}{semi} " ) , 
126163            Applicability :: MachineApplicable , 
127164        ) ; 
128165    } 
0 commit comments