]> git.lizzy.rs Git - rust.git/blobdiff - clippy_lints/src/nonstandard_macro_braces.rs
Last PR adjustments
[rust.git] / clippy_lints / src / nonstandard_macro_braces.rs
index ca660a9250db15f2332a71b44dfad2340c8d1f06..6c909e5ed73ea6b5faac116b6ca5bb657a3cc21f 100644 (file)
@@ -3,13 +3,16 @@
     hash::{Hash, Hasher},
 };
 
-use clippy_utils::{diagnostics::span_lint_and_help, in_macro, is_direct_expn_of, source::snippet_opt};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_opt;
 use if_chain::if_chain;
 use rustc_ast::ast;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_errors::Applicability;
 use rustc_hir::def_id::DefId;
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::hygiene::{ExpnKind, MacroKind};
 use rustc_span::Span;
 use serde::{de, Deserialize};
 
@@ -29,6 +32,7 @@
     /// ```rust
     /// vec![1, 2, 3];
     /// ```
+    #[clippy::version = "1.55.0"]
     pub NONSTANDARD_MACRO_BRACES,
     nursery,
     "check consistent use of braces in macro"
@@ -36,8 +40,8 @@
 
 const BRACES: &[(&str, &str)] = &[("(", ")"), ("{", "}"), ("[", "]")];
 
-/// The (name, (open brace, close brace), source snippet)
-type MacroInfo<'a> = (&'a str, &'a (String, String), String);
+/// The (callsite span, (open brace, close brace), source snippet)
+type MacroInfo<'a> = (Span, &'a (String, String), String);
 
 #[derive(Clone, Debug, Default)]
 pub struct MacroBraces {
@@ -59,33 +63,29 @@ pub fn new(conf: &FxHashSet<MacroMatcher>) -> Self {
 
 impl EarlyLintPass for MacroBraces {
     fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
-        if let Some((name, braces, snip)) = is_offending_macro(cx, item.span, self) {
-            let span = item.span.ctxt().outer_expn_data().call_site;
-            emit_help(cx, snip, braces, name, span);
+        if let Some((span, braces, snip)) = is_offending_macro(cx, item.span, self) {
+            emit_help(cx, &snip, braces, span);
             self.done.insert(span);
         }
     }
 
     fn check_stmt(&mut self, cx: &EarlyContext<'_>, stmt: &ast::Stmt) {
-        if let Some((name, braces, snip)) = is_offending_macro(cx, stmt.span, self) {
-            let span = stmt.span.ctxt().outer_expn_data().call_site;
-            emit_help(cx, snip, braces, name, span);
+        if let Some((span, braces, snip)) = is_offending_macro(cx, stmt.span, self) {
+            emit_help(cx, &snip, braces, span);
             self.done.insert(span);
         }
     }
 
     fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
-        if let Some((name, braces, snip)) = is_offending_macro(cx, expr.span, self) {
-            let span = expr.span.ctxt().outer_expn_data().call_site;
-            emit_help(cx, snip, braces, name, span);
+        if let Some((span, braces, snip)) = is_offending_macro(cx, expr.span, self) {
+            emit_help(cx, &snip, braces, span);
             self.done.insert(span);
         }
     }
 
     fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) {
-        if let Some((name, braces, snip)) = is_offending_macro(cx, ty.span, self) {
-            let span = ty.span.ctxt().outer_expn_data().call_site;
-            emit_help(cx, snip, braces, name, span);
+        if let Some((span, braces, snip)) = is_offending_macro(cx, ty.span, self) {
+            emit_help(cx, &snip, braces, span);
             self.done.insert(span);
         }
     }
@@ -93,64 +93,50 @@ fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) {
 
 fn is_offending_macro<'a>(cx: &EarlyContext<'_>, span: Span, mac_braces: &'a MacroBraces) -> Option<MacroInfo<'a>> {
     let unnested_or_local = || {
-        let nested = in_macro(span.ctxt().outer_expn_data().call_site);
-        !nested
+        !span.ctxt().outer_expn_data().call_site.from_expansion()
             || span
                 .macro_backtrace()
                 .last()
                 .map_or(false, |e| e.macro_def_id.map_or(false, DefId::is_local))
     };
+    let span_call_site = span.ctxt().outer_expn_data().call_site;
     if_chain! {
-        // Make sure we are only one level deep otherwise there are to many FP's
-        if in_macro(span);
-        if let Some((name, braces)) = find_matching_macro(span, &mac_braces.macro_braces);
-        if let Some(snip) = snippet_opt(cx, span.ctxt().outer_expn_data().call_site);
+        if let ExpnKind::Macro(MacroKind::Bang, mac_name) = span.ctxt().outer_expn_data().kind;
+        let name = mac_name.as_str();
+        if let Some(braces) = mac_braces.macro_braces.get(name);
+        if let Some(snip) = snippet_opt(cx, span_call_site);
         // we must check only invocation sites
         // https://github.com/rust-lang/rust-clippy/issues/7422
-        if snip.starts_with(&format!("{}!", name));
+        if snip.starts_with(&format!("{name}!"));
         if unnested_or_local();
         // make formatting consistent
-        let c = snip.replace(" ", "");
-        if !c.starts_with(&format!("{}!{}", name, braces.0));
-        if !mac_braces.done.contains(&span.ctxt().outer_expn_data().call_site);
+        let c = snip.replace(' ', "");
+        if !c.starts_with(&format!("{name}!{}", braces.0));
+        if !mac_braces.done.contains(&span_call_site);
         then {
-            Some((name, braces, snip))
+            Some((span_call_site, braces, snip))
         } else {
             None
         }
     }
 }
 
-fn emit_help(cx: &EarlyContext<'_>, snip: String, braces: &(String, String), name: &str, span: Span) {
-    let with_space = &format!("! {}", braces.0);
-    let without_space = &format!("!{}", braces.0);
-    let mut help = snip;
-    for b in BRACES.iter().filter(|b| b.0 != braces.0) {
-        help = help.replace(b.0, &braces.0).replace(b.1, &braces.1);
-        // Only `{` traditionally has space before the brace
-        if braces.0 != "{" && help.contains(with_space) {
-            help = help.replace(with_space, without_space);
-        } else if braces.0 == "{" && help.contains(without_space) {
-            help = help.replace(without_space, with_space);
-        }
+fn emit_help(cx: &EarlyContext<'_>, snip: &str, braces: &(String, String), span: Span) {
+    if let Some((macro_name, macro_args_str)) = snip.split_once('!') {
+        let mut macro_args = macro_args_str.trim().to_string();
+        // now remove the wrong braces
+        macro_args.remove(0);
+        macro_args.pop();
+        span_lint_and_sugg(
+            cx,
+            NONSTANDARD_MACRO_BRACES,
+            span,
+            &format!("use of irregular braces for `{macro_name}!` macro"),
+            "consider writing",
+            format!("{macro_name}!{}{macro_args}{}", braces.0, braces.1),
+            Applicability::MachineApplicable,
+        );
     }
-    span_lint_and_help(
-        cx,
-        NONSTANDARD_MACRO_BRACES,
-        span,
-        &format!("use of irregular braces for `{}!` macro", name),
-        Some(span),
-        &format!("consider writing `{}`", help),
-    );
-}
-
-fn find_matching_macro(
-    span: Span,
-    braces: &FxHashMap<String, (String, String)>,
-) -> Option<(&String, &(String, String))> {
-    braces
-        .iter()
-        .find(|(macro_name, _)| is_direct_expn_of(span, macro_name).is_some())
 }
 
 fn macro_braces(conf: FxHashSet<MacroMatcher>) -> FxHashMap<String, (String, String)> {
@@ -191,6 +177,10 @@ fn macro_braces(conf: FxHashSet<MacroMatcher>) -> FxHashMap<String, (String, Str
             name: "vec",
             braces: ("[", "]"),
         ),
+        macro_matcher!(
+            name: "matches",
+            braces: ("(", ")"),
+        ),
     ]
     .into_iter()
     .collect::<FxHashMap<_, _>>();
@@ -276,9 +266,7 @@ fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
                         .iter()
                         .find(|b| b.0 == brace)
                         .map(|(o, c)| ((*o).to_owned(), (*c).to_owned()))
-                        .ok_or_else(|| {
-                            de::Error::custom(&format!("expected one of `(`, `{{`, `[` found `{}`", brace))
-                        })?,
+                        .ok_or_else(|| de::Error::custom(format!("expected one of `(`, `{{`, `[` found `{brace}`")))?,
                 })
             }
         }