use std::collections::BTreeSet;
use clippy_utils::diagnostics::span_lint_and_help;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass, LintStore};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Symbol;
use crate::ascii_letter::AsciiLetter;
use crate::common::{
DefaultState, hir_in_external_macro, is_single_ascii_letter, resolve_symbol_set_from_chars,
resolved_state,
};
declare_tool_lint! {
pub perfectionist::SINGLE_LETTER_CONST_GENERIC,
Warn,
"const generic parameter has a single-letter name",
report_in_external_macro: false
}
const CONFIG_KEY: &str = "perfectionist::single_letter_const_generic";
#[derive(Debug, Default, serde::Deserialize)]
#[serde(default, deny_unknown_fields, rename_all = "snake_case")]
struct Config {
allowed_idents: Vec<AsciiLetter>,
}
pub struct SingleLetterConstGeneric {
allowed_idents: BTreeSet<Symbol>,
}
impl SingleLetterConstGeneric {
fn new() -> Self {
let config: Config = dylint_linting::config_or_default(CONFIG_KEY);
let allowed_idents =
resolve_symbol_set_from_chars(&[], config.allowed_idents, Vec::<AsciiLetter>::new());
Self { allowed_idents }
}
}
impl_lint_pass!(SingleLetterConstGeneric => [SINGLE_LETTER_CONST_GENERIC]);
pub fn register_lint(lint_store: &mut LintStore) {
lint_store.register_lints(&[SINGLE_LETTER_CONST_GENERIC]);
}
pub fn register_pass(lint_store: &mut LintStore) {
if let DefaultState::Inactive =
resolved_state("single_letter_const_generic", DefaultState::Active)
{
return;
}
lint_store.register_late_pass(|_| Box::new(SingleLetterConstGeneric::new()));
}
impl<'tcx> LateLintPass<'tcx> for SingleLetterConstGeneric {
fn check_generic_param(
&mut self,
lint_context: &LateContext<'tcx>,
param: &'tcx hir::GenericParam<'tcx>,
) {
if !matches!(param.kind, hir::GenericParamKind::Const { .. }) {
return;
}
if hir_in_external_macro(lint_context, param.hir_id, param.span) {
return;
}
let ident = param.name.ident();
if !is_single_ascii_letter(ident.name.as_str()) {
return;
}
if self.allowed_idents.contains(&ident.name) {
return;
}
span_lint_and_help(
lint_context,
SINGLE_LETTER_CONST_GENERIC,
param.span,
format!(
"const generic parameter `{}` has a single-letter name",
ident.name,
),
None,
"rename to a descriptive identifier (e.g. `LEN`, `COLS`, `LANES`)",
);
}
}