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_help;
+use clippy_utils::source::snippet_opt;
use if_chain::if_chain;
use rustc_ast::ast;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_hir::def_id::DefId;
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::Span;
+use rustc_span::hygiene::{ExpnKind, MacroKind};
+use rustc_span::{Span, Symbol};
use serde::{de, Deserialize};
declare_clippy_lint! {
/// ```rust
/// vec![1, 2, 3];
/// ```
+ #[clippy::version = "1.55.0"]
pub NONSTANDARD_MACRO_BRACES,
nursery,
"check consistent use of braces in macro"
const BRACES: &[(&str, &str)] = &[("(", ")"), ("{", "}"), ("[", "]")];
/// The (name, (open brace, close brace), source snippet)
-type MacroInfo<'a> = (&'a str, &'a (String, String), String);
+type MacroInfo<'a> = (Symbol, &'a (String, String), String);
#[derive(Clone, Debug, Default)]
pub struct MacroBraces {
}
fn is_offending_macro<'a>(cx: &EarlyContext<'_>, span: Span, mac_braces: &'a MacroBraces) -> Option<MacroInfo<'a>> {
+ let unnested_or_local = || {
+ !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))
+ };
if_chain! {
- if in_macro(span);
- if let Some((name, braces)) = find_matching_macro(span, &mac_braces.macro_braces);
+ 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.ctxt().outer_expn_data().call_site);
// we must check only invocation sites
// https://github.com/rust-lang/rust-clippy/issues/7422
- if snip.starts_with(name);
+ if snip.starts_with(&format!("{}!", name));
+ if unnested_or_local();
// make formatting consistent
- let c = snip.replace(" ", "");
+ let c = snip.replace(' ', "");
if !c.starts_with(&format!("{}!{}", name, braces.0));
if !mac_braces.done.contains(&span.ctxt().outer_expn_data().call_site);
then {
- Some((name, braces, snip))
+ Some((mac_name, braces, snip))
} else {
None
}
}
}
-fn emit_help(cx: &EarlyContext<'_>, snip: String, braces: &(String, String), name: &str, span: Span) {
+fn emit_help(cx: &EarlyContext<'_>, snip: String, braces: &(String, String), name: Symbol, span: Span) {
let with_space = &format!("! {}", braces.0);
let without_space = &format!("!{}", braces.0);
let mut help = snip;
);
}
-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)> {
let mut braces = vec![
macro_matcher!(