Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7138,6 +7138,7 @@ Released 2018-09-13
[`module-items-ordered-within-groupings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#module-items-ordered-within-groupings
[`msrv`]: https://doc.rust-lang.org/clippy/lint_configuration.html#msrv
[`pass-by-value-size-limit`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pass-by-value-size-limit
[`profiles`]: https://doc.rust-lang.org/clippy/lint_configuration.html#profiles
[`pub-underscore-fields-behavior`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pub-underscore-fields-behavior
[`recursive-self-in-type-definitions`]: https://doc.rust-lang.org/clippy/lint_configuration.html#recursive-self-in-type-definitions
[`semicolon-inside-block-ignore-singleline`]: https://doc.rust-lang.org/clippy/lint_configuration.html#semicolon-inside-block-ignore-singleline
Expand Down
22 changes: 22 additions & 0 deletions book/src/lint_configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -927,6 +927,28 @@ The minimum size (in bytes) to consider a type for passing by reference instead
* [`large_types_passed_by_value`](https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value)


## `profiles`
Named profiles of disallowed items (unrelated to Cargo build profiles).

#### Example

```toml
[profiles.persistent]
disallowed-methods = [{ path = "std::env::temp_dir" }]
disallowed-types = [{ path = "std::time::Instant", reason = "use our custom time API" }]

[profiles.single_threaded]
disallowed-methods = [{ path = "std::thread::spawn" }]
```

**Default Value:** `{}`

---
**Affected lints:**
* [`disallowed_methods`](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_methods)
* [`disallowed_types`](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types)


## `pub-underscore-fields-behavior`
Lint "public" fields in a struct that are prefixed with an underscore based on their
exported visibility, or whether they are marked as "pub".
Expand Down
217 changes: 213 additions & 4 deletions clippy_config/src/conf.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use crate::ClippyConfiguration;
use crate::types::{
DisallowedPath, DisallowedPathWithoutReplacement, InherentImplLintScope, MacroMatcher, MatchLintBehaviour,
PubUnderscoreFieldsBehaviour, Rename, SourceItemOrdering, SourceItemOrderingCategory,
DisallowedPath, DisallowedPathWithoutReplacement, DisallowedProfile, InherentImplLintScope, MacroMatcher,
MatchLintBehaviour, PubUnderscoreFieldsBehaviour, Rename, SourceItemOrdering, SourceItemOrderingCategory,
SourceItemOrderingModuleItemGroupings, SourceItemOrderingModuleItemKind, SourceItemOrderingTraitAssocItemKind,
SourceItemOrderingTraitAssocItemKinds, SourceItemOrderingWithinModuleItemGroupings,
};
use clippy_utils::msrvs::Msrv;
use itertools::Itertools;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Applicability;
use rustc_session::Session;
use rustc_span::edit_distance::edit_distance;
Expand Down Expand Up @@ -221,12 +222,80 @@ macro_rules! deserialize {
}};
}

macro_rules! parse_conf_value {
(
$map:expr,
$ty:ty,
$errors:expr,
$file:expr,
$field_span:expr,
profiles @[$($profiles:expr)?],
disallowed @[$($disallowed:expr)?]
) => {
parse_conf_value_impl!(
$map,
$ty,
$errors,
$file,
$field_span,
($($profiles)?),
($($disallowed)?)
)
};
}

macro_rules! parse_conf_value_impl {
($map:expr, $ty:ty, $errors:expr, $file:expr, $field_span:expr, (), ()) => {{
let _ = &$field_span;
deserialize!($map, $ty, $errors, $file)
}};
($map:expr, $ty:ty, $errors:expr, $file:expr, $field_span:expr, ($profiles:expr), ()) => {
deserialize_profiles!($map, $errors, $file, $field_span)
};
($map:expr, $ty:ty, $errors:expr, $file:expr, $field_span:expr, (), ($disallowed:expr)) => {{
let _ = &$field_span;
deserialize!($map, $ty, $errors, $file, $disallowed)
}};
(
$map:expr,
$ty:ty,
$errors:expr,
$file:expr,
$field_span:expr,
($profiles:expr),
($disallowed:expr)
) => {
compile_error!("field cannot specify both profiles and disallowed-paths attributes")
};
}

macro_rules! deserialize_profiles {
($map:expr, $errors:expr, $file:expr, $field_span:expr) => {{
let raw_value = $map.next_value::<toml::Value>()?;
let value_span = $field_span.clone();
let toml::Value::Table(table) = raw_value else {
$errors.push(ConfError::spanned(
$file,
"expected table with named profiles",
None,
value_span.clone(),
));
continue;
};

let map = parse_profiles(table, $file, value_span.clone(), &mut $errors);

(map, value_span)
}};
}

macro_rules! define_Conf {
($(
$(#[doc = $doc:literal])+
$(#[conf_deprecated($dep:literal, $new_conf:ident)])?
$(#[default_text = $default_text:expr])?
$(#[disallowed_paths_allow_replacements = $replacements_allowed:expr])?
$(#[profiles = $profiles:expr])?
$(#[lints($($for_lints:ident),* $(,)?)])?
$name:ident: $ty:ty = $default:expr,
)*) => {
Expand Down Expand Up @@ -281,10 +350,20 @@ macro_rules! define_Conf {

match field {
$(Field::$name => {
let field_span = name.span();
// Is this a deprecated field, i.e., is `$dep` set? If so, push a warning.
$(warnings.push(ConfError::spanned(self.0, format!("deprecated field `{}`. {}", name.get_ref(), $dep), None, name.span()));)?
let (value, value_span) =
deserialize!(map, $ty, errors, self.0 $(, $replacements_allowed)?);
let (value, value_span) = parse_conf_value!(
map,
$ty,
errors,
self.0,
field_span,
// Disallowed-profile table parsing is special-cased to preserve spans for
// diagnostics in disallowed-path entries.
profiles @[$($profiles)?],
disallowed @[$($replacements_allowed)?]
);
// Was this field set previously?
if $name.is_some() {
errors.push(ConfError::spanned(self.0, format!("duplicate field `{}`", name.get_ref()), None, name.span()));
Expand Down Expand Up @@ -341,6 +420,121 @@ fn span_from_toml_range(file: &SourceFile, span: Range<usize>) -> Span {
)
}

fn parse_profiles(
table: toml::value::Table,
file: &SourceFile,
value_span: Range<usize>,
errors: &mut Vec<ConfError>,
) -> FxHashMap<String, DisallowedProfile> {
let mut profiles = FxHashMap::default();
let config_span = span_from_toml_range(file, value_span.clone());

for (profile_name, profile_value) in table {
let toml::Value::Table(mut profile_table) = profile_value else {
errors.push(ConfError::spanned(
file,
format!("invalid profile `{profile_name}`: expected table"),
None,
value_span.clone(),
));
continue;
};

let disallowed_methods = match profile_table
.remove("disallowed-methods")
.or_else(|| profile_table.remove("disallowed_methods"))
{
Some(value) => parse_profile_list(
file,
&profile_name,
"disallowed-methods",
value,
value_span.clone(),
config_span,
errors,
),
None => Vec::new(),
};

let disallowed_types = match profile_table
.remove("disallowed-types")
.or_else(|| profile_table.remove("disallowed_types"))
{
Some(value) => parse_profile_list(
file,
&profile_name,
"disallowed-types",
value,
value_span.clone(),
config_span,
errors,
),
None => Vec::new(),
};

if !profile_table.is_empty() {
let keys = profile_table.keys().map(String::as_str).collect::<Vec<_>>().join(", ");
errors.push(ConfError::spanned(
file,
format!("profile `{profile_name}` has unknown keys: {keys}"),
None,
value_span.clone(),
));
}

profiles.insert(
profile_name,
DisallowedProfile {
disallowed_methods,
disallowed_types,
},
);
}

profiles
}

fn parse_profile_list(
file: &SourceFile,
profile_name: &str,
key_name: &str,
value: toml::Value,
value_span: Range<usize>,
config_span: Span,
errors: &mut Vec<ConfError>,
) -> Vec<DisallowedPath> {
let toml::Value::Array(entries) = value else {
errors.push(ConfError::spanned(
file,
format!("profile `{profile_name}`: `{key_name}` must be an array"),
None,
value_span,
));
return Vec::new();
};

let mut disallowed = Vec::with_capacity(entries.len());
for entry in entries {
match DisallowedPath::deserialize(entry.clone()) {
Ok(mut path) => {
path.set_span(config_span);
disallowed.push(path);
},
Err(err) => errors.push(ConfError::spanned(
file,
format!(
"profile `{profile_name}`: {}",
err.to_string().replace('\n', " ").trim()
),
None,
value_span.clone(),
)),
}
}

disallowed
}

define_Conf! {
/// Which crates to allow absolute paths from
#[lints(absolute_paths)]
Expand Down Expand Up @@ -809,6 +1003,21 @@ define_Conf! {
/// The minimum size (in bytes) to consider a type for passing by reference instead of by value.
#[lints(large_types_passed_by_value)]
pass_by_value_size_limit: u64 = 256,
/// Named profiles of disallowed items (unrelated to Cargo build profiles).
///
/// #### Example
///
/// ```toml
/// [profiles.persistent]
/// disallowed-methods = [{ path = "std::env::temp_dir" }]
/// disallowed-types = [{ path = "std::time::Instant", reason = "use our custom time API" }]
///
/// [profiles.single_threaded]
/// disallowed-methods = [{ path = "std::thread::spawn" }]
/// ```
#[profiles = true]
#[lints(disallowed_methods, disallowed_types)]
profiles: FxHashMap<String, DisallowedProfile> = FxHashMap::default(),
/// Lint "public" fields in a struct that are prefixed with an underscore based on their
/// exported visibility, or whether they are marked as "pub".
#[lints(pub_underscore_fields)]
Expand Down
9 changes: 9 additions & 0 deletions clippy_config/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,15 @@ impl<'de, const REPLACEMENT_ALLOWED: bool> Deserialize<'de> for DisallowedPath<R
}
}

#[derive(Debug, Default, Deserialize, Serialize)]
#[serde(deny_unknown_fields, rename_all = "kebab-case")]
pub struct DisallowedProfile {
#[serde(default, alias = "disallowed_methods")]
pub disallowed_methods: Vec<DisallowedPath>,
#[serde(default, alias = "disallowed_types")]
pub disallowed_types: Vec<DisallowedPath>,
}

// `DisallowedPathEnum` is an implementation detail to enable the `Deserialize` implementation just
// above. `DisallowedPathEnum` is not meant to be used outside of this file.
#[derive(Debug, Deserialize, Serialize)]
Expand Down
Loading