use std::collections::BTreeSet;
use rustc_ast::Path;
use crate::common::resolve_string_set;
use crate::macro_path::{matches_any, merge_with_builtins, parse_path_list};
const CONFIG_KEY: &str = "perfectionist::macro_argument_binding";
const BUILTIN_DENY: &[&str] = &["debug_assert", "debug_assert_eq", "debug_assert_ne"];
const BUILTIN_ALLOW: &[&str] = &[
"format",
"format_args",
"print",
"println",
"eprint",
"eprintln",
"write",
"writeln",
"vec",
"panic",
"unimplemented",
"todo",
"unreachable",
"assert",
"assert_eq",
"assert_ne",
"matches",
"dbg",
"anyhow",
"cfg",
"column",
"compile_error",
"concat",
"env",
"file",
"include",
"include_bytes",
"include_str",
"is_x86_feature_detected",
"line",
"module_path",
"option_env",
"stringify",
"assert_binary_snapshot",
"assert_compact_debug_snapshot",
"assert_compact_json_snapshot",
"assert_csv_snapshot",
"assert_debug_snapshot",
"assert_display_snapshot",
"assert_json_snapshot",
"assert_op_expr",
"assert_ron_snapshot",
"assert_snapshot",
"assert_toml_snapshot",
"assert_yaml_snapshot",
"bail",
"btreemap",
"btreeset",
"ensure",
"hashmap",
"hashset",
"json",
];
const BUILTIN_PURE_MACROS: &[&str] = &[
"cfg",
"column",
"concat",
"env",
"file",
"include_bytes",
"include_str",
"line",
"module_path",
"option_env",
"stringify",
];
const BUILTIN_PURE_METHODS: &[&str] = &[
"as_bytes", "as_deref", "as_mut", "as_ref", "as_slice", "as_str", "is_empty", "len",
];
#[derive(Debug, Clone, Copy, Default, serde::Deserialize)]
#[serde(rename_all = "snake_case")]
pub(super) enum Mode {
DenyOnly,
Blanket,
#[default]
AllowAndDeny,
}
#[derive(Debug, Default, serde::Deserialize)]
#[serde(default, deny_unknown_fields, rename_all = "snake_case")]
pub(super) struct Config {
pub mode: Mode,
pub deny_extra: Vec<String>,
pub allow_extra: Vec<String>,
pub ignore: Vec<String>,
pub extra_pure_methods: Vec<String>,
pub ignore_pure_methods: Vec<String>,
pub extra_pure_macros: Vec<String>,
pub ignore_pure_macros: Vec<String>,
}
pub(super) struct MacroArgumentBinding {
mode: Mode,
deny: BTreeSet<Vec<String>>,
allow: BTreeSet<Vec<String>>,
allow_extra: BTreeSet<Vec<String>>,
ignore: BTreeSet<Vec<String>>,
pure_methods: BTreeSet<String>,
pure_macros: BTreeSet<String>,
}
impl MacroArgumentBinding {
pub(super) fn new() -> Self {
let config: Config = dylint_linting::config_or_default(CONFIG_KEY);
let extra_deny = parse_path_list(&config.deny_extra);
let extra_allow = parse_path_list(&config.allow_extra);
let deny = merge_with_builtins(BUILTIN_DENY, &extra_deny);
let allow = merge_with_builtins(BUILTIN_ALLOW, &extra_allow);
let ignore = parse_path_list(&config.ignore);
let pure_methods = resolve_string_set(
BUILTIN_PURE_METHODS,
config.extra_pure_methods,
config.ignore_pure_methods,
);
let pure_macros = resolve_string_set(
BUILTIN_PURE_MACROS,
config.extra_pure_macros,
config.ignore_pure_macros,
);
Self {
mode: config.mode,
deny,
allow,
allow_extra: extra_allow,
ignore,
pure_methods,
pure_macros,
}
}
pub(super) fn pure_methods(&self) -> &BTreeSet<String> {
&self.pure_methods
}
pub(super) fn pure_macros(&self) -> &BTreeSet<String> {
&self.pure_macros
}
pub(super) fn should_check_path(&self, path: &Path) -> bool {
!matches_any(path, &self.ignore) && self.arguments_should_be_checked(path)
}
fn arguments_should_be_checked(&self, path: &Path) -> bool {
let on_deny = matches_any(path, &self.deny);
match self.mode {
Mode::DenyOnly => on_deny,
Mode::Blanket => !matches_any(path, &self.allow_extra),
Mode::AllowAndDeny => on_deny || !matches_any(path, &self.allow),
}
}
}