@@ -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