@@ -16,8 +16,8 @@ declare_clippy_lint! {
16
16
/// Checks that common macros are used with consistent bracing.
17
17
///
18
18
/// ### 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 .
21
21
///
22
22
/// ### Example
23
23
/// ```no_run
@@ -33,8 +33,12 @@ declare_clippy_lint! {
33
33
"check consistent use of braces in macro"
34
34
}
35
35
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
+ }
38
42
39
43
pub struct MacroBraces {
40
44
macro_braces : FxHashMap < String , ( char , char ) > ,
@@ -54,30 +58,58 @@ impl_lint_pass!(MacroBraces => [NONSTANDARD_MACRO_BRACES]);
54
58
55
59
impl EarlyLintPass for MacroBraces {
56
60
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) ;
60
70
}
61
71
}
62
72
63
73
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) ;
67
87
}
68
88
}
69
89
70
90
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) ;
74
100
}
75
101
}
76
102
77
103
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) ;
81
113
}
82
114
}
83
115
}
@@ -90,39 +122,44 @@ fn is_offending_macro(cx: &EarlyContext<'_>, span: Span, mac_braces: &MacroBrace
90
122
. last ( )
91
123
. is_some_and ( |e| e. macro_def_id . is_some_and ( DefId :: is_local) )
92
124
} ;
93
- let span_call_site = span. ctxt ( ) . outer_expn_data ( ) . call_site ;
125
+ let callsite_span = span. ctxt ( ) . outer_expn_data ( ) . call_site ;
94
126
if let ExpnKind :: Macro ( MacroKind :: Bang , mac_name) = span. ctxt ( ) . outer_expn_data ( ) . kind
95
127
&& let name = mac_name. as_str ( )
96
128
&& 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)
98
130
// we must check only invocation sites
99
131
// 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
101
135
&& 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)
106
137
{
107
- Some ( ( span_call_site, braces, snip) )
138
+ Some ( MacroInfo {
139
+ callsite_span,
140
+ callsite_snippet : snip,
141
+ old_open_brace,
142
+ braces,
143
+ } )
108
144
} else {
109
145
None
110
146
}
111
147
}
112
148
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 { "" } ;
114
151
if let Some ( ( macro_name, macro_args_str) ) = snip. split_once ( '!' ) {
115
152
let mut macro_args = macro_args_str. trim ( ) . to_string ( ) ;
116
153
// now remove the wrong braces
117
- macro_args. remove ( 0 ) ;
118
154
macro_args. pop ( ) ;
155
+ macro_args. remove ( 0 ) ;
119
156
span_lint_and_sugg (
120
157
cx,
121
158
NONSTANDARD_MACRO_BRACES ,
122
159
span,
123
160
format ! ( "use of irregular braces for `{macro_name}!` macro" ) ,
124
161
"consider writing" ,
125
- format ! ( "{macro_name}!{open}{macro_args}{close}" ) ,
162
+ format ! ( "{macro_name}!{open}{macro_args}{close}{semi} " ) ,
126
163
Applicability :: MachineApplicable ,
127
164
) ;
128
165
}
0 commit comments