1use crate::ConfMetadata;
2use crate::de::{DeserializeOrDefault, DiagCtxt, FromDefault, create_value_list_msg, find_closest_match};
3use crate::types::{
4 DisallowedPath, DisallowedPathWithoutReplacement, InherentImplLintScope, MacroMatcher, MatchLintBehaviour,
5 PubUnderscoreFieldsBehaviour, Rename, SourceItemOrdering, SourceItemOrderingModuleItemGroupings,
6 SourceItemOrderingTraitAssocItemKinds, SourceItemOrderingWithinModuleItemGroupings, TraitImplItemOrder,
7};
8use rustc_attr_parsing::parse_version;
9use rustc_data_structures::fx::FxHashSet;
10use rustc_errors::Applicability;
11use rustc_hir::attrs::RustcVersion;
12use rustc_session::Session;
13use rustc_span::{Pos as _, SourceFile, Symbol};
14use std::path::PathBuf;
15use std::sync::{Arc, OnceLock};
16use std::{env, fs, io};
17use toml::de::DeTable;
18
19#[rustfmt::skip]
20static DEFAULT_DOC_VALID_IDENTS: &[&str] = &[
21 "KiB", "MiB", "GiB", "TiB", "PiB", "EiB",
22 "MHz", "GHz", "THz",
23 "AccessKit",
24 "CoAP", "CoreFoundation", "CoreGraphics", "CoreText",
25 "DevOps",
26 "Direct2D", "Direct3D", "DirectWrite", "DirectX",
27 "ECMAScript",
28 "GPLv2", "GPLv3",
29 "GitHub", "GitLab",
30 "IPv4", "IPv6",
31 "InfiniBand", "RoCE",
32 "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript",
33 "PowerPC", "PowerShell", "WebAssembly",
34 "NaN", "NaNs",
35 "OAuth", "GraphQL",
36 "SQLite", "MySQL", "PostgreSQL", "MariaDB", "MongoDB",
37 "OCaml",
38 "OpenAL", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry",
39 "OpenType",
40 "WebGL", "WebGL2", "WebGPU", "WebRTC", "WebSocket", "WebTransport",
41 "WebP", "OpenExr", "YCbCr", "sRGB",
42 "TensorFlow",
43 "TrueType",
44 "iOS", "macOS", "FreeBSD", "NetBSD", "OpenBSD", "NixOS",
45 "TeX", "LaTeX", "BibTeX", "BibLaTeX",
46 "MinGW",
47 "CamelCase",
48];
49static DEFAULT_DISALLOWED_NAMES: &[&str] = &["foo", "baz", "quux"];
50static DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS: &[&str] = &["i", "j", "x", "y", "z", "w", "n"];
51static DEFAULT_ALLOWED_PREFIXES: &[&str] = &["to", "as", "into", "from", "try_into", "try_from"];
52static DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS: &[&str] =
53 &["core::convert::From", "core::convert::TryFrom", "core::str::FromStr"];
54
55static DEFAULT_ALLOWED_SCRIPTS: &[&str] = &["Latin"];
56static DEFAULT_IGNORE_INTERIOR_MUTABILITY: &[&str] = &["bytes::Bytes"];
57
58macro_rules! first_expr {
59 ($e:expr $(,$_e:expr)*) => {
60 $e
61 };
62}
63
64macro_rules! filtered_names {
65 (($($names:literal)*)) => { &[$($names),*] };
66 (($($names:literal)*) $name:literal $($rest:tt)*) => {
67 filtered_names!(($($names)* $name) $($rest)*)
68 };
69 (($($names:literal)*) $new_name:ident $name:literal $($rest:tt)*) => {
70 filtered_names!(($($names)*) $($rest)*)
71 };
72}
73
74macro_rules! define_Conf {
75 (
76 $(
77 $(#[doc = $doc:literal])*
78 $(#[default_text = $default_text:literal])?
79 $(#[rename = $new_name:ident])?
80 $(#[lints($($for_lints:ident),* $(,)?)])?
81 $name:ident($name_str:literal) $(: $ty:ty $(= $default:expr)?)?,
83 )*
84 ) => {
85 #[allow(non_camel_case_types)]
86 #[derive(Clone, Copy)]
87 enum ConfField {
88 $($name,)*
89 ThirdParty,
90 }
91 impl ConfField {
92 const NAMES: &'static [&'static str] = &[$($name_str,)* "third-party"];
93 const FIELDS: &'static [Self] = &[$(first_expr!($(Self::$new_name,)? Self::$name),)* Self::ThirdParty];
94 const SUGG_NAMES: &'static [&'static str] = filtered_names!(() $($($new_name)? $name_str)* "third-party");
95
96 fn name(self) -> &'static str {
97 Self::NAMES[self as usize]
98 }
99
100 fn new_field(self) -> Self {
101 Self::FIELDS[self as usize]
102 }
103
104 fn parse(s: &str) -> Option<Self> {
105 match s {
106 $($name_str => Some(Self::$name),)*
107 "third-party" => Some(Self::ThirdParty),
108 _ => None,
109 }
110 }
111 }
112
113 pub struct Conf {
115 $($(pub $name: $ty,)?)*
117 }
118
119 impl Default for Conf {
120 fn default() -> Self {
121 Self {
122 $($($name: <$ty as FromDefault<_>>::from_default(first_expr!($($default,)? ())),)?)*
123 }
124 }
125 }
126
127 impl Conf {
128 pub fn get_metadata() -> Vec<ConfMetadata> {
129 vec![$(
130 ConfMetadata {
131 name: $name_str,
132 default: first_expr!(
133 $($default_text.into(),)?
134 $(<$ty as FromDefault<_>>::display_default(first_expr!($($default,)? ())).to_string(),)?
135 String::new()
136 ),
137 lints: &[$($(stringify!($for_lints)),*)?],
138 doc: concat!($($doc, '\n',)*),
139 renamed_to: first_expr!($(Some(ConfField::$new_name.name()),)? None),
140 },
141 )*]
142 }
143
144 fn deserialize(dcx: &DiagCtxt<'_>, table: &toml::de::DeTable<'_>) -> Self {
145 $($(let mut $name: Option<$ty> = None;)?)*
146
147 for (key, value) in table.iter() {
148 let Some(mut conf_key) = ConfField::parse(key.get_ref()) else {
149 let sp = dcx.make_sp(key.span());
150 let mut diag = dcx.inner.struct_span_err(sp, "unknown field name");
151 if let Some(sugg) = find_closest_match(key.get_ref(), ConfField::SUGG_NAMES) {
152 diag.span_suggestion(sp, "did you mean", sugg, Applicability::MaybeIncorrect);
153 }
154 diag.note_once(create_value_list_msg(dcx, ConfField::SUGG_NAMES));
155 diag.emit();
156 continue;
157 };
158 loop {
159 match conf_key {
160 $($(ConfField::$name => {
161 $name = Some(
163 <$ty as DeserializeOrDefault<_>>::deserialize_or_default(
164 dcx,
165 value.into(),
166 first_expr!($($default,)? ()),
167 ),
168 );
169 },)?)*
170 ConfField::ThirdParty => {},
171 _ => {
173 let sp = dcx.make_sp(table.get_key_value(key).unwrap().0.span());
174 conf_key = conf_key.new_field();
175 let other_value = table.get_key_value(conf_key.name());
176 dcx.inner.struct_span_warn(sp, format!("use of a deprecated field"))
177 .with_span_suggestion(
178 sp, "use new name", conf_key.name(),
179 if other_value.is_some() {
180 Applicability::MaybeIncorrect
181 } else {
182 Applicability::MachineApplicable
183 }
184 ).emit();
185
186 if let Some((other_key, _)) = other_value {
187 dcx.inner.struct_span_err(sp, format!("duplicate key in document root"))
188 .with_span_note(dcx.make_sp(other_key.span()), "previous definition here")
189 .emit();
190 } else {
191 continue;
192 }
193 },
194 }
195 break;
196 }
197 }
198
199 Self {$($(
200 $name: $name.unwrap_or_else(
201 || <$ty as FromDefault<_>>::from_default(first_expr!($($default,)? ()))
202 ),
203 )?)*}
204 }
205 }
206
207 #[test]
208 fn check_conf_order() {
209 for [x, y] in ConfField::NAMES[..ConfField::NAMES.len() - 1].array_windows::<2>() {
210 assert!(x <= y, "configuration `{x}` and `{y}` are out of order");
211 }
212 }
213
214 #[test]
215 fn check_conf_names() {$(
216 assert_eq!(stringify!($name).replace('_', "-"), $name_str);
217 )*}
218 };
219}
220
221define_Conf! {
222 #[lints(absolute_paths)]
224 absolute_paths_allowed_crates("absolute-paths-allowed-crates"): FxHashSet<Symbol>,
225 #[lints(absolute_paths)]
228 absolute_paths_max_segments("absolute-paths-max-segments"): u64 = 2,
229 #[lints(undocumented_unsafe_blocks)]
231 accept_comment_above_attributes("accept-comment-above-attributes"): bool = true,
232 #[lints(undocumented_unsafe_blocks)]
234 accept_comment_above_statement("accept-comment-above-statement"): bool = true,
235 #[lints(modulo_arithmetic)]
237 allow_comparison_to_zero("allow-comparison-to-zero"): bool = true,
238 #[lints(dbg_macro)]
240 allow_dbg_in_tests("allow-dbg-in-tests"): bool = false,
241 #[lints(module_name_repetitions)]
243 allow_exact_repetitions("allow-exact-repetitions"): bool = true,
244 #[lints(expect_used)]
246 allow_expect_in_consts("allow-expect-in-consts"): bool = true,
247 #[lints(expect_used)]
249 allow_expect_in_tests("allow-expect-in-tests"): bool = false,
250 #[lints(indexing_slicing)]
252 allow_indexing_slicing_in_tests("allow-indexing-slicing-in-tests"): bool = false,
253 #[lints(large_stack_frames)]
255 allow_large_stack_frames_in_tests("allow-large-stack-frames-in-tests"): bool = true,
256 #[lints(uninlined_format_args)]
258 allow_mixed_uninlined_format_args("allow-mixed-uninlined-format-args"): bool = true,
259 #[lints(needless_raw_string_hashes)]
261 allow_one_hash_in_raw_strings("allow-one-hash-in-raw-strings"): bool = false,
262 #[lints(panic)]
264 allow_panic_in_tests("allow-panic-in-tests"): bool = false,
265 #[lints(print_stderr, print_stdout)]
267 allow_print_in_tests("allow-print-in-tests"): bool = false,
268 #[lints(module_inception)]
270 allow_private_module_inception("allow-private-module-inception"): bool = false,
271 #[lints(renamed_function_params)]
285 allow_renamed_params_for("allow-renamed-params-for"): Vec<String> = DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS,
286 #[lints(unwrap_used)]
288 allow_unwrap_in_consts("allow-unwrap-in-consts"): bool = true,
289 #[lints(unwrap_used)]
291 allow_unwrap_in_tests("allow-unwrap-in-tests"): bool = false,
292 #[lints(expect_used, unwrap_used)]
300 allow_unwrap_types("allow-unwrap-types"): Vec<String>,
301 #[lints(useless_vec)]
303 allow_useless_vec_in_tests("allow-useless-vec-in-tests"): bool = false,
304 #[lints(path_ends_with_ext)]
306 allowed_dotfiles("allowed-dotfiles"): Vec<String>,
307 #[lints(multiple_crate_versions)]
309 allowed_duplicate_crates("allowed-duplicate-crates"): FxHashSet<String>,
310 #[lints(min_ident_chars)]
314 allowed_idents_below_min_chars("allowed-idents-below-min-chars"): FxHashSet<String> = DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS,
315 #[lints(module_name_repetitions)]
333 allowed_prefixes("allowed-prefixes"): Vec<String> = DEFAULT_ALLOWED_PREFIXES,
334 #[lints(disallowed_script_idents)]
336 allowed_scripts("allowed-scripts"): Vec<String> = DEFAULT_ALLOWED_SCRIPTS,
337 #[lints(wildcard_imports)]
351 allowed_wildcard_imports("allowed-wildcard-imports"): FxHashSet<String>,
352 #[lints(arithmetic_side_effects)]
367 arithmetic_side_effects_allowed("arithmetic-side-effects-allowed"): Vec<String>,
368 #[lints(arithmetic_side_effects)]
383 arithmetic_side_effects_allowed_binary("arithmetic-side-effects-allowed-binary"): Vec<[String; 2]>,
384 #[lints(arithmetic_side_effects)]
392 arithmetic_side_effects_allowed_unary("arithmetic-side-effects-allowed-unary"): Vec<String>,
393 #[lints(large_const_arrays, large_stack_arrays)]
395 array_size_threshold("array-size-threshold"): u64 = 16 * 1024,
396 #[lints(
398 box_collection,
399 enum_variant_names,
400 large_types_passed_by_value,
401 linkedlist,
402 needless_pass_by_ref_mut,
403 option_option,
404 owned_cow,
405 rc_buffer,
406 rc_mutex,
407 redundant_allocation,
408 ref_option,
409 single_call_fn,
410 trivially_copy_pass_by_ref,
411 unnecessary_box_returns,
412 unnecessary_wraps,
413 unused_self,
414 upper_case_acronyms,
415 vec_box,
416 wrong_self_convention,
417 )]
418 avoid_breaking_exported_api("avoid-breaking-exported-api"): bool = true,
419 #[lints(await_holding_invalid_type)]
421 await_holding_invalid_types("await-holding-invalid-types"): Vec<DisallowedPathWithoutReplacement>,
422 #[rename = disallowed_names]
423 blacklisted_names("blacklisted-names"),
424 #[lints(cargo_common_metadata)]
426 cargo_ignore_publish("cargo-ignore-publish"): bool = false,
427 #[lints(needless_late_init)]
450 check_grouped_late_init("check-grouped-late-init"): bool = true,
451 #[lints(incompatible_msrv)]
453 check_incompatible_msrv_in_tests("check-incompatible-msrv-in-tests"): bool = false,
454 #[lints(inconsistent_struct_constructor)]
473 check_inconsistent_struct_field_initializers("check-inconsistent-struct-field-initializers"): bool = false,
474 #[lints(missing_errors_doc, missing_panics_doc, missing_safety_doc, unnecessary_safety_doc)]
476 check_private_items("check-private-items"): bool = false,
477 #[lints(cognitive_complexity)]
479 cognitive_complexity_threshold("cognitive-complexity-threshold"): u64 = 25,
480 #[lints(excessive_precision)]
482 const_literal_digits_threshold("const-literal-digits-threshold"): u32 = 30,
483 #[rename = cognitive_complexity_threshold]
484 cyclomatic_complexity_threshold("cyclomatic-complexity-threshold"),
485 #[lints(disallowed_fields)]
494 disallowed_fields("disallowed-fields"): Vec<DisallowedPath>,
495 #[lints(disallowed_macros)]
504 disallowed_macros("disallowed-macros"): Vec<DisallowedPath>,
505 #[lints(disallowed_methods)]
514 disallowed_methods("disallowed-methods"): Vec<DisallowedPath>,
515 #[lints(disallowed_names)]
519 disallowed_names("disallowed-names"): Vec<String> = DEFAULT_DISALLOWED_NAMES,
520 #[lints(disallowed_types)]
529 disallowed_types("disallowed-types"): Vec<DisallowedPath>,
530 #[lints(doc_markdown)]
536 doc_valid_idents("doc-valid-idents"): FxHashSet<String> = DEFAULT_DOC_VALID_IDENTS,
537 #[lints(non_send_fields_in_send_ty)]
539 enable_raw_pointer_heuristic_for_send("enable-raw-pointer-heuristic-for-send"): bool = true,
540 #[lints(explicit_iter_loop)]
558 enforce_iter_loop_reborrow("enforce-iter-loop-reborrow"): bool = false,
559 #[lints(missing_enforced_import_renames)]
561 enforced_import_renames("enforced-import-renames"): Vec<Rename>,
562 #[lints(enum_variant_names)]
564 enum_variant_name_threshold("enum-variant-name-threshold"): u64 = 3,
565 #[lints(large_enum_variant)]
567 enum_variant_size_threshold("enum-variant-size-threshold"): u64 = 200,
568 #[lints(excessive_nesting)]
570 excessive_nesting_threshold("excessive-nesting-threshold"): u64 = 0,
571 #[lints(large_futures)]
573 future_size_threshold("future-size-threshold"): u64 = 16 * 1024,
574 #[lints(borrow_interior_mutable_const, declare_interior_mutable_const, ifs_same_cond, mutable_key_type)]
576 ignore_interior_mutability("ignore-interior-mutability"): Vec<String> = DEFAULT_IGNORE_INTERIOR_MUTABILITY,
577 #[lints(multiple_inherent_impl)]
579 inherent_impl_lint_scope("inherent-impl-lint-scope"): InherentImplLintScope = InherentImplLintScope::Crate,
580 #[lints(result_large_err)]
583 large_error_ignored("large-error-ignored"): Vec<String>,
584 #[lints(result_large_err)]
586 large_error_threshold("large-error-threshold"): u64 = 128,
587 #[lints(collapsible_else_if, collapsible_if)]
590 lint_commented_code("lint-commented-code"): bool = false,
591 #[rename = check_inconsistent_struct_field_initializers]
592 lint_inconsistent_struct_field_initializers("lint-inconsistent-struct-field-initializers"): bool = false,
593 #[lints(decimal_literal_representation)]
595 literal_representation_threshold("literal-representation-threshold"): u64 = 16384,
596 #[lints(manual_let_else)]
599 matches_for_let_else("matches-for-let-else"): MatchLintBehaviour = MatchLintBehaviour::WellKnownTypes,
600 #[lints(fn_params_excessive_bools)]
603 max_fn_params_bools("max-fn-params-bools"): u64 = 3,
604 #[lints(large_include_file)]
606 max_include_file_size("max-include-file-size"): u64 = 1_000_000,
607 #[lints(struct_excessive_bools)]
609 max_struct_bools("max-struct-bools"): u64 = 3,
610 #[lints(index_refutable_slice)]
614 max_suggested_slice_pattern_length("max-suggested-slice-pattern-length"): u64 = 3,
615 #[lints(type_repetition_in_bounds)]
617 max_trait_bounds("max-trait-bounds"): u64 = 3,
618 #[lints(min_ident_chars)]
620 min_ident_chars_lint_trait_impl("min-ident-chars-lint-trait-impl"): bool = false,
621 #[lints(min_ident_chars)]
623 min_ident_chars_threshold("min-ident-chars-threshold"): u64 = 1,
624 #[lints(missing_docs_in_private_items)]
626 missing_docs_allow_unused("missing-docs-allow-unused"): bool = false,
627 #[lints(missing_docs_in_private_items)]
630 missing_docs_in_crate_items("missing-docs-in-crate-items"): bool = false,
631 #[lints(arbitrary_source_item_ordering)]
633 module_item_order_groupings("module-item-order-groupings"): SourceItemOrderingModuleItemGroupings,
634 #[lints(arbitrary_source_item_ordering)]
639 module_items_ordered_within_groupings("module-items-ordered-within-groupings"): SourceItemOrderingWithinModuleItemGroupings,
640 #[default_text = "current version"]
642 #[lints(
643 allow_attributes,
644 allow_attributes_without_reason,
645 almost_complete_range,
646 approx_constant,
647 assigning_clones,
648 borrow_as_ptr,
649 cast_abs_to_unsigned,
650 checked_conversions,
651 cloned_instead_of_copied,
652 collapsible_match,
653 collapsible_str_replace,
654 deprecated_cfg_attr,
655 derivable_impls,
656 err_expect,
657 filter_map_next,
658 from_over_into,
659 if_then_some_else_none,
660 implicit_saturating_sub,
661 index_refutable_slice,
662 inefficient_to_string,
663 io_other_error,
664 iter_kv_map,
665 legacy_numeric_constants,
666 len_zero,
667 lines_filter_map_ok,
668 manual_abs_diff,
669 manual_bits,
670 manual_c_str_literals,
671 manual_clamp,
672 manual_div_ceil,
673 manual_flatten,
674 manual_hash_one,
675 manual_is_ascii_check,
676 manual_is_power_of_two,
677 manual_is_variant_and,
678 manual_isolate_lowest_one,
679 manual_let_else,
680 manual_midpoint,
681 manual_non_exhaustive,
682 manual_noop_waker,
683 manual_option_as_slice,
684 manual_pattern_char_comparison,
685 manual_range_contains,
686 manual_rem_euclid,
687 manual_repeat_n,
688 manual_retain,
689 manual_slice_fill,
690 manual_slice_size_calculation,
691 manual_split_once,
692 manual_str_repeat,
693 manual_strip,
694 manual_take,
695 manual_try_fold,
696 map_clone,
697 map_unwrap_or,
698 map_with_unused_argument_over_ranges,
699 match_like_matches_macro,
700 mem_replace_option_with_some,
701 mem_replace_with_default,
702 missing_const_for_fn,
703 needless_borrow,
704 non_std_lazy_statics,
705 nonnull_unchecked_on_box_ptr,
706 option_as_ref_deref,
707 or_fun_call,
708 ptr_as_ptr,
709 question_mark,
710 redundant_field_names,
711 redundant_static_lifetimes,
712 repeat_vec_with_capacity,
713 same_item_push,
714 seek_from_current,
715 to_digit_is_some,
716 transmute_ptr_to_ref,
717 tuple_array_conversions,
718 type_repetition_in_bounds,
719 unchecked_time_subtraction,
720 uninlined_format_args,
721 unnecessary_lazy_evaluations,
722 unnecessary_unwrap,
723 unnested_or_patterns,
724 unused_trait_names,
725 use_self,
726 zero_ptr,
727 )]
728 msrv("msrv"): Option<RustcVersion>,
729 #[lints(large_types_passed_by_value)]
731 pass_by_value_size_limit("pass-by-value-size-limit"): u64 = 256,
732 #[lints(pub_underscore_fields)]
735 pub_underscore_fields_behavior("pub-underscore-fields-behavior"): PubUnderscoreFieldsBehaviour = PubUnderscoreFieldsBehaviour::PubliclyExported,
736 #[lints(use_self)]
738 recursive_self_in_type_definitions("recursive-self-in-type-definitions"): bool = true,
739 #[lints(semicolon_inside_block)]
741 semicolon_inside_block_ignore_singleline("semicolon-inside-block-ignore-singleline"): bool = false,
742 #[lints(semicolon_outside_block)]
744 semicolon_outside_block_ignore_multiline("semicolon-outside-block-ignore-multiline"): bool = false,
745 #[lints(many_single_char_names)]
747 single_char_binding_names_threshold("single-char-binding-names-threshold"): u64 = 4,
748 #[lints(arbitrary_source_item_ordering)]
750 source_item_ordering("source-item-ordering"): SourceItemOrdering,
751 #[lints(large_stack_frames)]
753 stack_size_threshold("stack-size-threshold"): u64 = 512_000,
754 #[lints(nonstandard_macro_braces)]
760 standard_macro_braces("standard-macro-braces"): Vec<MacroMatcher>,
761 #[lints(struct_field_names)]
763 struct_field_name_threshold("struct-field-name-threshold"): u64 = 3,
764 #[lints(indexing_slicing)]
770 suppress_restriction_lint_in_const("suppress-restriction-lint-in-const"): bool = false,
771 #[lints(boxed_local, useless_vec)]
773 too_large_for_stack("too-large-for-stack"): u64 = 200,
774 #[lints(too_many_arguments)]
776 too_many_arguments_threshold("too-many-arguments-threshold"): u64 = 7,
777 #[lints(too_many_lines)]
779 too_many_lines_threshold("too-many-lines-threshold"): u64 = 100,
780 #[lints(arbitrary_source_item_ordering)]
782 trait_assoc_item_kinds_order("trait-assoc-item-kinds-order"): SourceItemOrderingTraitAssocItemKinds,
783 #[lints(arbitrary_source_item_ordering)]
799 trait_impl_item_order("trait-impl-item-order"): TraitImplItemOrder,
800 #[default_text = "target_pointer_width"]
803 #[lints(trivially_copy_pass_by_ref)]
804 trivial_copy_size_limit("trivial-copy-size-limit"): Option<u64>,
805 #[lints(type_complexity)]
807 type_complexity_threshold("type-complexity-threshold"): u64 = 250,
808 #[lints(unnecessary_box_returns)]
810 unnecessary_box_size("unnecessary-box-size"): u64 = 128,
811 #[lints(unreadable_literal)]
813 unreadable_literal_lint_fractions("unreadable-literal-lint-fractions"): bool = true,
814 #[lints(upper_case_acronyms)]
816 upper_case_acronyms_aggressive("upper-case-acronyms-aggressive"): bool = false,
817 #[lints(vec_box)]
819 vec_box_size_threshold("vec-box-size-threshold"): u64 = 4096,
820 #[lints(verbose_bit_mask)]
822 verbose_bit_mask_threshold("verbose-bit-mask-threshold"): u64 = 1,
823 #[lints(wildcard_imports)]
826 warn_on_all_wildcard_imports("warn-on-all-wildcard-imports"): bool = false,
827 #[lints(macro_metavars_in_unsafe)]
829 warn_unsafe_macro_metavars_in_private_macros("warn-unsafe-macro-metavars-in-private-macros"): bool = false,
830}
831
832pub fn sanitize_explanation(raw_docs: &str) -> String {
834 let mut explanation = String::with_capacity(128);
836 let mut in_code = false;
837 for line in raw_docs.lines() {
838 let line = line.strip_prefix(' ').unwrap_or(line);
839
840 if let Some(lang) = line.strip_prefix("```") {
841 let tag = lang.split_once(',').map_or(lang, |(left, _)| left);
842 if !in_code && matches!(tag, "" | "rust" | "ignore" | "should_panic" | "no_run" | "compile_fail") {
843 explanation += "```rust\n";
844 } else {
845 explanation += line;
846 explanation.push('\n');
847 }
848 in_code = !in_code;
849 } else if !(in_code && line.starts_with("# ")) {
850 explanation += line;
851 explanation.push('\n');
852 }
853 }
854
855 explanation
856}
857
858fn load_conf_file(sess: &Session) -> Option<Arc<SourceFile>> {
864 const CONFIG_FILE_NAMES: [&str; 2] = [".clippy.toml", "clippy.toml"];
866
867 const CONFIG_VARS: [(&str, &str); 2] = [
870 ("CLIPPY_CONF_DIR", "failed to read `CLIPPY_CONF_DIR` as a directory"),
871 (
872 "CARGO_MANIFEST_DIR",
873 "failed to read `CARGO_MANIFEST_DIR` as a directory",
874 ),
875 ];
876 let (current, msg) = CONFIG_VARS
877 .into_iter()
878 .find_map(|(var, msg)| env::var_os(var).map(|p| (PathBuf::from(p), msg)))
879 .unwrap_or_else(|| (PathBuf::from("."), "failed to get the current directory"));
880 let mut current = match current.canonicalize() {
881 Ok(x) => x,
882 Err(e) => {
883 sess.dcx().err(format!("{msg}: {e}"));
884 return None;
885 },
886 };
887
888 let mut loaded_config: Option<(PathBuf, Arc<SourceFile>)> = None;
889 loop {
890 for config_file_name in CONFIG_FILE_NAMES {
891 if let Ok(config_path) = current.join(config_file_name).canonicalize() {
892 if let Some((loaded_path, _)) = &loaded_config {
893 if fs::metadata(loaded_path).is_ok_and(|x| x.is_file()) {
894 sess.dcx().warn(format!(
896 "using config file `{}`, `{}` will be ignored",
897 loaded_path.display(),
898 config_path.display(),
899 ));
900 }
901 } else {
902 match sess.source_map().load_file(&config_path) {
903 Ok(src) => loaded_config = Some((config_path, src)),
904 Err(e)
905 if matches!(
906 e.kind(),
907 io::ErrorKind::NotFound | io::ErrorKind::IsADirectory | io::ErrorKind::NotADirectory
908 ) => {},
909 Err(e) => {
910 sess.dcx()
911 .err(format!("error reading `{}`: {e}", config_path.display()));
912 return None;
913 },
914 }
915 }
916 }
917 }
918
919 if let Some((_, src)) = loaded_config {
921 return Some(src);
922 }
923
924 if !current.pop() {
926 return None;
927 }
928 }
929}
930
931impl Conf {
932 pub fn load(sess: &Session) -> &'static Conf {
933 static CONF: OnceLock<Conf> = OnceLock::new();
934 CONF.get_or_init(|| Conf::load_inner(sess))
935 }
936
937 fn load_inner(sess: &Session) -> Conf {
938 let mut conf = if let Some(src) = load_conf_file(sess) {
939 let dcx = DiagCtxt::new(sess, src.start_pos.to_usize());
940 let src = src.src.as_ref().unwrap();
941
942 let (toml, errs) = DeTable::parse_recoverable(src.as_str());
943 for e in errs {
944 match e.span() {
945 Some(sp) => dcx.span_err(sp, e.message().to_owned()),
946 None => {
947 dcx.inner
948 .struct_err(format!("error parsing `clippy.toml`: {}", e.message()))
949 .emit();
950 },
951 }
952 }
953 Conf::deserialize(&dcx, toml.get_ref())
954 } else {
955 Conf::default()
956 };
957
958 let cargo_msrv = env::var("CARGO_PKG_RUST_VERSION")
959 .ok()
960 .and_then(|v| parse_version(Symbol::intern(&v)));
961 match (&conf.msrv, cargo_msrv) {
962 (None, Some(cargo_msrv)) => conf.msrv = Some(cargo_msrv),
963 (Some(clippy_msrv), Some(cargo_msrv)) => {
964 if *clippy_msrv != cargo_msrv {
965 sess.dcx().warn(format!(
966 "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{clippy_msrv}` from `clippy.toml`"
967 ));
968 }
969 },
970 (_, None) => {},
971 }
972
973 conf
974 }
975}
976
977#[cfg(test)]
978mod tests {
979 use rustc_data_structures::fx::FxHashSet;
980 use std::fs;
981 use toml::de::DeTable;
982 use walkdir::WalkDir;
983
984 #[test]
985 fn configs_are_tested() {
986 let mut names: FxHashSet<_> = super::Conf::get_metadata()
987 .into_iter()
988 .filter(|meta| meta.renamed_to.is_none())
989 .map(|meta| meta.name)
990 .collect();
991
992 let toml_files = WalkDir::new("../tests")
993 .into_iter()
994 .map(Result::unwrap)
995 .filter(|entry| entry.file_name() == "clippy.toml");
996
997 for entry in toml_files {
998 let file = fs::read_to_string(entry.path()).unwrap();
999 if let Ok(toml) = DeTable::parse(&file) {
1000 for (key, _) in toml.as_ref() {
1001 names.remove(&**key.get_ref());
1002 }
1003 }
1004 }
1005
1006 assert!(
1007 names.is_empty(),
1008 "Configuration variable lacks test: {names:?}\nAdd a test to `tests/ui-toml`"
1009 );
1010 }
1011}