Metadata collection monster searching for configurations

This commit is contained in:
xFrednet 2021-05-11 20:23:52 +02:00
parent aa15a5442a
commit 210ec728e5
3 changed files with 109 additions and 5 deletions

View File

@ -1009,7 +1009,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
#[cfg(feature = "metadata-collector-lint")]
{
if std::env::var("ENABLE_METADATA_COLLECTION").eq(&Ok("1".to_string())) {
store.register_late_pass(|| box utils::internal_lints::metadata_collector::MetadataCollector::default());
store.register_late_pass(|| box utils::internal_lints::metadata_collector::MetadataCollector::new());
}
}

View File

@ -26,13 +26,13 @@ fn from_error(error: impl Error) -> Self {
macro_rules! define_Conf {
($(
#[$doc:meta]
#[doc = $doc:literal]
$(#[conf_deprecated($dep:literal)])?
($name:ident: $ty:ty = $default:expr),
)*) => {
/// Clippy lint configuration
pub struct Conf {
$(#[$doc] pub $name: $ty,)*
$(#[doc = $doc] pub $name: $ty,)*
}
mod defaults {
@ -89,6 +89,24 @@ fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error> where V: MapA
Ok(TryConf { conf, errors })
}
}
#[cfg(feature = "metadata-collector-lint")]
pub mod metadata {
use crate::utils::internal_lints::metadata_collector::ClippyConfigurationBasicInfo;
pub(crate) fn get_configuration_metadata() -> Vec<ClippyConfigurationBasicInfo> {
vec![
$(
ClippyConfigurationBasicInfo {
name: stringify!($name),
config_type: stringify!($ty),
default: stringify!($default),
doc_comment: $doc,
},
)+
]
}
}
};
}
@ -100,7 +118,7 @@ fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error> where V: MapA
(blacklisted_names: Vec<String> = ["foo", "baz", "quux"].iter().map(ToString::to_string).collect()),
/// Lint: COGNITIVE_COMPLEXITY. The maximum cognitive complexity a function can have
(cognitive_complexity_threshold: u64 = 25),
/// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY. Use the Cognitive Complexity lint instead.
/// Lint: CYCLOMATIC_COMPLEXITY. Use the Cognitive Complexity lint instead.
#[conf_deprecated("Please use `cognitive-complexity-threshold` instead")]
(cyclomatic_complexity_threshold: Option<u64> = None),
/// Lint: DOC_MARKDOWN. The list of words this lint should not consider as identifiers needing ticks

View File

@ -102,13 +102,24 @@
impl_lint_pass!(MetadataCollector => [INTERNAL_METADATA_COLLECTOR]);
#[allow(clippy::module_name_repetitions)]
#[derive(Debug, Clone, Default)]
#[derive(Debug, Clone)]
pub struct MetadataCollector {
/// All collected lints
///
/// We use a Heap here to have the lints added in alphabetic order in the export
lints: BinaryHeap<LintMetadata>,
applicability_info: FxHashMap<String, ApplicabilityInfo>,
config: Vec<ClippyConfiguration>,
}
impl MetadataCollector {
pub fn new() -> Self {
Self {
lints: BinaryHeap::<LintMetadata>::default(),
applicability_info: FxHashMap::<String, ApplicabilityInfo>::default(),
config: collect_configs(),
}
}
}
impl Drop for MetadataCollector {
@ -214,6 +225,81 @@ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
}
}
#[derive(Debug)]
pub(crate) struct ClippyConfigurationBasicInfo {
pub name: &'static str,
pub config_type: &'static str,
pub default: &'static str,
pub doc_comment: &'static str,
}
#[derive(Debug, Clone, Default)]
struct ClippyConfiguration {
name: String,
lints: Vec<String>,
doc: String,
config_type: &'static str,
default: String,
}
// ==================================================================
// Configuration
// ==================================================================
fn collect_configs() -> Vec<ClippyConfiguration> {
let cons = crate::utils::conf::metadata::get_configuration_metadata();
cons.iter()
.map(move |x| {
let (lints, doc) = parse_config_field_doc(x.doc_comment)
.unwrap_or_else(|| (vec![], "[ERROR] MALFORMED DOC COMMENT".to_string()));
ClippyConfiguration {
name: to_kebab(x.name),
lints,
doc,
config_type: x.config_type,
default: x.default.to_string(),
}
})
.collect()
}
/// This parses the field documentation of the config struct.
///
/// ```rust, ignore
/// parse_config_field_doc(cx, "Lint: LINT_NAME_1, LINT_NAME_2. Papa penguin, papa penguin")
/// ```
///
/// Would yield:
/// ```rust, ignore
/// Some(["lint_name_1", "lint_name_2"], "Papa penguin, papa penguin")
/// ```
fn parse_config_field_doc(doc_comment: &str) -> Option<(Vec<String>, String)> {
const DOC_START: &str = " Lint: ";
if_chain! {
if doc_comment.starts_with(DOC_START);
if let Some(split_pos) = doc_comment.find('.');
then {
let mut doc_comment = doc_comment.to_string();
let documentation = doc_comment.split_off(split_pos);
doc_comment.make_ascii_lowercase();
let lints: Vec<String> = doc_comment.split_off(DOC_START.len()).split(", ").map(str::to_string).collect();
Some((lints, documentation))
} else {
None
}
}
}
/// Transforms a given `snake_case_string` to a tasty `kebab-case-string`
fn to_kebab(config_name: &str) -> String {
config_name.replace('_', "-")
}
// ==================================================================
// Lint pass
// ==================================================================
impl<'hir> LateLintPass<'hir> for MetadataCollector {
/// Collecting lint declarations like:
/// ```rust, ignore