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_ir::RustcVersion;
9use rustc_attr_parsing::parse_version;
10use rustc_data_structures::fx::FxHashSet;
11use rustc_errors::Applicability;
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 "WebAuthn", "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 "x86_64",
49 "CommonMark", "IntelliJ",
50];
51static DEFAULT_DISALLOWED_NAMES: &[&str] = &["foo", "baz", "quux"];
52static DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS: &[&str] = &["i", "j", "x", "y", "z", "w", "n"];
53static DEFAULT_ALLOWED_PREFIXES: &[&str] = &["to", "as", "into", "from", "try_into", "try_from"];
54static DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS: &[&str] =
55 &["core::convert::From", "core::convert::TryFrom", "core::str::FromStr"];
56
57static DEFAULT_ALLOWED_SCRIPTS: &[&str] = &["Latin"];
58static DEFAULT_IGNORE_INTERIOR_MUTABILITY: &[&str] = &["bytes::Bytes"];
59
60macro_rules! first_expr {
61 ($e:expr $(,$_e:expr)*) => {
62 $e
63 };
64}
65
66macro_rules! filtered_names {
67 (($($names:literal)*)) => { &[$($names),*] };
68 (($($names:literal)*) $name:literal $($rest:tt)*) => {
69 filtered_names!(($($names)* $name) $($rest)*)
70 };
71 (($($names:literal)*) $new_name:ident $name:literal $($rest:tt)*) => {
72 filtered_names!(($($names)*) $($rest)*)
73 };
74}
75
76macro_rules! define_Conf {
77 (
78 $(
79 $(#[doc = $doc:literal])*
80 $(#[default_text = $default_text:literal])?
81 $(#[rename = $new_name:ident])?
82 $(#[lints($($for_lints:ident),* $(,)?)])?
83 $name:ident($name_str:literal) $(: $ty:ty $(= $default:expr)?)?,
85 )*
86 ) => {
87 #[allow(non_camel_case_types)]
88 #[derive(Clone, Copy)]
89 enum ConfField {
90 $($name,)*
91 ThirdParty,
92 }
93 impl ConfField {
94 const NAMES: &'static [&'static str] = &[$($name_str,)* "third-party"];
95 const FIELDS: &'static [Self] = &[$(first_expr!($(Self::$new_name,)? Self::$name),)* Self::ThirdParty];
96 const SUGG_NAMES: &'static [&'static str] = filtered_names!(() $($($new_name)? $name_str)* "third-party");
97
98 fn name(self) -> &'static str {
99 Self::NAMES[self as usize]
100 }
101
102 fn new_field(self) -> Self {
103 Self::FIELDS[self as usize]
104 }
105
106 fn parse(s: &str) -> Option<Self> {
107 match s {
108 $($name_str => Some(Self::$name),)*
109 "third-party" => Some(Self::ThirdParty),
110 _ => None,
111 }
112 }
113 }
114
115 pub struct Conf {
117 $($(pub $name: $ty,)?)*
119 }
120
121 impl Default for Conf {
122 fn default() -> Self {
123 Self {
124 $($($name: <$ty as FromDefault<_>>::from_default(first_expr!($($default,)? ())),)?)*
125 }
126 }
127 }
128
129 impl Conf {
130 pub fn get_metadata() -> Vec<ConfMetadata> {
131 vec![$(
132 ConfMetadata {
133 name: $name_str,
134 default: first_expr!(
135 $($default_text.into(),)?
136 $(<$ty as FromDefault<_>>::display_default(first_expr!($($default,)? ())).to_string(),)?
137 String::new()
138 ),
139 lints: &[$($(stringify!($for_lints)),*)?],
140 doc: concat!($($doc, '\n',)*),
141 renamed_to: first_expr!($(Some(ConfField::$new_name.name()),)? None),
142 },
143 )*]
144 }
145
146 fn deserialize(dcx: &DiagCtxt<'_>, table: &toml::de::DeTable<'_>) -> Self {
147 $($(let mut $name: Option<$ty> = None;)?)*
148
149 for (key, value) in table.iter() {
150 let Some(mut conf_key) = ConfField::parse(key.get_ref()) else {
151 let sp = dcx.make_sp(key.span());
152 let mut diag = dcx.inner.struct_span_err(sp, "unknown field name");
153 if let Some(sugg) = find_closest_match(key.get_ref(), ConfField::SUGG_NAMES) {
154 diag.span_suggestion(sp, "did you mean", sugg, Applicability::MaybeIncorrect);
155 }
156 diag.note_once(create_value_list_msg(dcx, ConfField::SUGG_NAMES));
157 diag.emit();
158 continue;
159 };
160 loop {
161 match conf_key {
162 $($(ConfField::$name => {
163 $name = Some(
165 <$ty as DeserializeOrDefault<_>>::deserialize_or_default(
166 dcx,
167 value.into(),
168 first_expr!($($default,)? ()),
169 ),
170 );
171 },)?)*
172 ConfField::ThirdParty => {},
173 _ => {
175 let sp = dcx.make_sp(table.get_key_value(key).unwrap().0.span());
176 conf_key = conf_key.new_field();
177 let other_value = table.get_key_value(conf_key.name());
178 dcx.inner.struct_span_warn(sp, format!("use of a deprecated field"))
179 .with_span_suggestion(
180 sp, "use new name", conf_key.name(),
181 if other_value.is_some() {
182 Applicability::MaybeIncorrect
183 } else {
184 Applicability::MachineApplicable
185 }
186 ).emit();
187
188 if let Some((other_key, _)) = other_value {
189 dcx.inner.struct_span_err(sp, format!("duplicate key in document root"))
190 .with_span_note(dcx.make_sp(other_key.span()), "previous definition here")
191 .emit();
192 } else {
193 continue;
194 }
195 },
196 }
197 break;
198 }
199 }
200
201 Self {$($(
202 $name: $name.unwrap_or_else(
203 || <$ty as FromDefault<_>>::from_default(first_expr!($($default,)? ()))
204 ),
205 )?)*}
206 }
207 }
208
209 macro_rules! test (
214 ($name_:ident) => {};
215 ($name_:ident $new_name_:ident) => {};
216 ($name_:ident $ty_:ty) => {};
217 ($name_:ident $new_name_:ident $ty_:ty) => { compile_error!(concat!("renamed field `", stringify!($name_), "` has a type")); };
218 );
219 $(test!($name $($new_name)? $($ty)?);)*
220
221 #[test]
222 fn check_conf_order() {
223 for [x, y] in ConfField::NAMES[..ConfField::NAMES.len() - 1].array_windows::<2>() {
224 assert!(x <= y, "configuration `{x}` and `{y}` are out of order");
225 }
226 }
227
228 #[test]
229 fn check_conf_names() {$(
230 assert_eq!(stringify!($name).replace('_', "-"), $name_str);
231 )*}
232 };
233}
234
235define_Conf! {
236 #[lints(absolute_paths)]
238 absolute_paths_allowed_crates("absolute-paths-allowed-crates"): FxHashSet<Symbol>,
239 #[lints(absolute_paths)]
242 absolute_paths_max_segments("absolute-paths-max-segments"): u64 = 2,
243 #[lints(undocumented_unsafe_blocks)]
245 accept_comment_above_attributes("accept-comment-above-attributes"): bool = true,
246 #[lints(undocumented_unsafe_blocks)]
248 accept_comment_above_statement("accept-comment-above-statement"): bool = true,
249 #[lints(modulo_arithmetic)]
251 allow_comparison_to_zero("allow-comparison-to-zero"): bool = true,
252 #[lints(dbg_macro)]
254 allow_dbg_in_tests("allow-dbg-in-tests"): bool = false,
255 #[lints(module_name_repetitions)]
257 allow_exact_repetitions("allow-exact-repetitions"): bool = true,
258 #[lints(expect_used)]
260 allow_expect_in_consts("allow-expect-in-consts"): bool = true,
261 #[lints(expect_used)]
263 allow_expect_in_tests("allow-expect-in-tests"): bool = false,
264 #[lints(indexing_slicing)]
266 allow_indexing_slicing_in_tests("allow-indexing-slicing-in-tests"): bool = false,
267 #[lints(large_stack_frames)]
269 allow_large_stack_frames_in_tests("allow-large-stack-frames-in-tests"): bool = true,
270 #[lints(uninlined_format_args)]
272 allow_mixed_uninlined_format_args("allow-mixed-uninlined-format-args"): bool = true,
273 #[lints(needless_raw_string_hashes)]
275 allow_one_hash_in_raw_strings("allow-one-hash-in-raw-strings"): bool = false,
276 #[lints(panic)]
278 allow_panic_in_tests("allow-panic-in-tests"): bool = false,
279 #[lints(print_stderr, print_stdout)]
281 allow_print_in_tests("allow-print-in-tests"): bool = false,
282 #[lints(module_inception)]
284 allow_private_module_inception("allow-private-module-inception"): bool = false,
285 #[lints(renamed_function_params)]
299 allow_renamed_params_for("allow-renamed-params-for"): Vec<String> = DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS,
300 #[lints(unwrap_used)]
302 allow_unwrap_in_consts("allow-unwrap-in-consts"): bool = true,
303 #[lints(unwrap_used)]
305 allow_unwrap_in_tests("allow-unwrap-in-tests"): bool = false,
306 #[lints(expect_used, unwrap_used)]
314 allow_unwrap_types("allow-unwrap-types"): Vec<String>,
315 #[lints(useless_vec)]
317 allow_useless_vec_in_tests("allow-useless-vec-in-tests"): bool = false,
318 #[lints(path_ends_with_ext)]
320 allowed_dotfiles("allowed-dotfiles"): Vec<String>,
321 #[lints(multiple_crate_versions)]
323 allowed_duplicate_crates("allowed-duplicate-crates"): FxHashSet<String>,
324 #[lints(min_ident_chars)]
328 allowed_idents_below_min_chars("allowed-idents-below-min-chars"): FxHashSet<String> = DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS,
329 #[lints(module_name_repetitions)]
347 allowed_prefixes("allowed-prefixes"): Vec<String> = DEFAULT_ALLOWED_PREFIXES,
348 #[lints(disallowed_script_idents)]
350 allowed_scripts("allowed-scripts"): Vec<String> = DEFAULT_ALLOWED_SCRIPTS,
351 #[lints(wildcard_imports)]
365 allowed_wildcard_imports("allowed-wildcard-imports"): FxHashSet<String>,
366 #[lints(arithmetic_side_effects)]
381 arithmetic_side_effects_allowed("arithmetic-side-effects-allowed"): Vec<String>,
382 #[lints(arithmetic_side_effects)]
397 arithmetic_side_effects_allowed_binary("arithmetic-side-effects-allowed-binary"): Vec<[String; 2]>,
398 #[lints(arithmetic_side_effects)]
406 arithmetic_side_effects_allowed_unary("arithmetic-side-effects-allowed-unary"): Vec<String>,
407 #[lints(large_const_arrays, large_stack_arrays)]
409 array_size_threshold("array-size-threshold"): u64 = 16 * 1024,
410 #[lints(
412 box_collection,
413 enum_variant_names,
414 large_types_passed_by_value,
415 linkedlist,
416 needless_pass_by_ref_mut,
417 option_option,
418 owned_cow,
419 rc_buffer,
420 rc_mutex,
421 redundant_allocation,
422 ref_option,
423 single_call_fn,
424 trivially_copy_pass_by_ref,
425 unnecessary_box_returns,
426 unnecessary_wraps,
427 unused_self,
428 upper_case_acronyms,
429 vec_box,
430 wrong_self_convention,
431 )]
432 avoid_breaking_exported_api("avoid-breaking-exported-api"): bool = true,
433 #[lints(await_holding_invalid_type)]
435 await_holding_invalid_types("await-holding-invalid-types"): Vec<DisallowedPathWithoutReplacement>,
436 #[rename = disallowed_names]
437 blacklisted_names("blacklisted-names"),
438 #[lints(cargo_common_metadata)]
440 cargo_ignore_publish("cargo-ignore-publish"): bool = false,
441 #[lints(needless_late_init)]
464 check_grouped_late_init("check-grouped-late-init"): bool = true,
465 #[lints(incompatible_msrv)]
467 check_incompatible_msrv_in_tests("check-incompatible-msrv-in-tests"): bool = false,
468 #[lints(inconsistent_struct_constructor)]
487 check_inconsistent_struct_field_initializers("check-inconsistent-struct-field-initializers"): bool = false,
488 #[lints(
490 missing_errors_doc,
491 missing_panics_doc,
492 missing_safety_doc,
493 too_long_first_doc_paragraph,
494 unnecessary_safety_doc,
495 )]
496 check_private_items("check-private-items"): bool = false,
497 #[lints(cognitive_complexity)]
499 cognitive_complexity_threshold("cognitive-complexity-threshold"): u64 = 25,
500 #[lints(excessive_precision)]
502 const_literal_digits_threshold("const-literal-digits-threshold"): u32 = 30,
503 #[rename = cognitive_complexity_threshold]
504 cyclomatic_complexity_threshold("cyclomatic-complexity-threshold"),
505 #[lints(disallowed_fields)]
514 disallowed_fields("disallowed-fields"): Vec<DisallowedPath>,
515 #[lints(disallowed_macros)]
524 disallowed_macros("disallowed-macros"): Vec<DisallowedPath>,
525 #[lints(disallowed_methods)]
534 disallowed_methods("disallowed-methods"): Vec<DisallowedPath>,
535 #[lints(disallowed_names)]
539 disallowed_names("disallowed-names"): Vec<String> = DEFAULT_DISALLOWED_NAMES,
540 #[lints(disallowed_types)]
549 disallowed_types("disallowed-types"): Vec<DisallowedPath>,
550 #[lints(doc_markdown)]
556 doc_valid_idents("doc-valid-idents"): FxHashSet<String> = DEFAULT_DOC_VALID_IDENTS,
557 #[lints(non_send_fields_in_send_ty)]
559 enable_raw_pointer_heuristic_for_send("enable-raw-pointer-heuristic-for-send"): bool = true,
560 #[lints(explicit_iter_loop)]
578 enforce_iter_loop_reborrow("enforce-iter-loop-reborrow"): bool = false,
579 #[lints(missing_enforced_import_renames)]
581 enforced_import_renames("enforced-import-renames"): Vec<Rename>,
582 #[lints(enum_variant_names)]
584 enum_variant_name_threshold("enum-variant-name-threshold"): u64 = 3,
585 #[lints(large_enum_variant)]
587 enum_variant_size_threshold("enum-variant-size-threshold"): u64 = 200,
588 #[lints(excessive_nesting)]
590 excessive_nesting_threshold("excessive-nesting-threshold"): u64 = 0,
591 #[lints(large_futures)]
593 future_size_threshold("future-size-threshold"): u64 = 16 * 1024,
594 #[lints(borrow_interior_mutable_const, declare_interior_mutable_const, ifs_same_cond, mutable_key_type)]
596 ignore_interior_mutability("ignore-interior-mutability"): Vec<String> = DEFAULT_IGNORE_INTERIOR_MUTABILITY,
597 #[lints(multiple_inherent_impl)]
599 inherent_impl_lint_scope("inherent-impl-lint-scope"): InherentImplLintScope = InherentImplLintScope::Crate,
600 #[lints(result_large_err)]
603 large_error_ignored("large-error-ignored"): Vec<String>,
604 #[lints(result_large_err)]
606 large_error_threshold("large-error-threshold"): u64 = 128,
607 #[lints(collapsible_else_if, collapsible_if)]
610 lint_commented_code("lint-commented-code"): bool = false,
611 #[rename = check_inconsistent_struct_field_initializers]
612 lint_inconsistent_struct_field_initializers("lint-inconsistent-struct-field-initializers"),
613 #[lints(decimal_literal_representation)]
615 literal_representation_threshold("literal-representation-threshold"): u64 = 16384,
616 #[lints(manual_let_else)]
619 matches_for_let_else("matches-for-let-else"): MatchLintBehaviour = MatchLintBehaviour::WellKnownTypes,
620 #[lints(fn_params_excessive_bools)]
623 max_fn_params_bools("max-fn-params-bools"): u64 = 3,
624 #[lints(large_include_file)]
626 max_include_file_size("max-include-file-size"): u64 = 1_000_000,
627 #[lints(struct_excessive_bools)]
629 max_struct_bools("max-struct-bools"): u64 = 3,
630 #[lints(index_refutable_slice)]
634 max_suggested_slice_pattern_length("max-suggested-slice-pattern-length"): u64 = 3,
635 #[lints(type_repetition_in_bounds)]
637 max_trait_bounds("max-trait-bounds"): u64 = 3,
638 #[lints(min_ident_chars)]
640 min_ident_chars_lint_trait_impl("min-ident-chars-lint-trait-impl"): bool = false,
641 #[lints(min_ident_chars)]
643 min_ident_chars_threshold("min-ident-chars-threshold"): u64 = 1,
644 #[lints(missing_docs_in_private_items)]
646 missing_docs_allow_unused("missing-docs-allow-unused"): bool = false,
647 #[lints(missing_docs_in_private_items)]
650 missing_docs_in_crate_items("missing-docs-in-crate-items"): bool = false,
651 #[lints(arbitrary_source_item_ordering)]
653 module_item_order_groupings("module-item-order-groupings"): SourceItemOrderingModuleItemGroupings,
654 #[lints(arbitrary_source_item_ordering)]
659 module_items_ordered_within_groupings("module-items-ordered-within-groupings"): SourceItemOrderingWithinModuleItemGroupings,
660 #[default_text = "current version"]
662 #[lints(
663 allow_attributes,
664 allow_attributes_without_reason,
665 almost_complete_range,
666 approx_constant,
667 assigning_clones,
668 borrow_as_ptr,
669 cast_abs_to_unsigned,
670 checked_conversions,
671 cloned_instead_of_copied,
672 collapsible_match,
673 collapsible_str_replace,
674 deprecated_cfg_attr,
675 derivable_impls,
676 err_expect,
677 filter_map_next,
678 from_over_into,
679 if_then_some_else_none,
680 implicit_saturating_sub,
681 index_refutable_slice,
682 inefficient_to_string,
683 io_other_error,
684 iter_kv_map,
685 legacy_numeric_constants,
686 len_zero,
687 lines_filter_map_ok,
688 manual_abs_diff,
689 manual_bits,
690 manual_c_str_literals,
691 manual_clamp,
692 manual_div_ceil,
693 manual_flatten,
694 manual_hash_one,
695 manual_is_ascii_check,
696 manual_is_power_of_two,
697 manual_is_variant_and,
698 manual_isolate_lowest_one,
699 manual_let_else,
700 manual_midpoint,
701 manual_non_exhaustive,
702 manual_noop_waker,
703 manual_option_as_slice,
704 manual_pattern_char_comparison,
705 manual_range_contains,
706 manual_rem_euclid,
707 manual_repeat_n,
708 manual_retain,
709 manual_slice_fill,
710 manual_slice_size_calculation,
711 manual_split_once,
712 manual_str_repeat,
713 manual_strip,
714 manual_take,
715 manual_try_fold,
716 map_clone,
717 map_unwrap_or,
718 map_with_unused_argument_over_ranges,
719 match_like_matches_macro,
720 mem_replace_option_with_some,
721 mem_replace_with_default,
722 missing_const_for_fn,
723 needless_borrow,
724 non_std_lazy_statics,
725 nonnull_unchecked_on_box_ptr,
726 option_as_ref_deref,
727 or_fun_call,
728 ptr_as_ptr,
729 question_mark,
730 redundant_field_names,
731 redundant_static_lifetimes,
732 repeat_vec_with_capacity,
733 same_item_push,
734 seek_from_current,
735 swap_ptr_to_ref,
736 to_digit_is_some,
737 transmute_ptr_to_ref,
738 tuple_array_conversions,
739 type_repetition_in_bounds,
740 unchecked_time_subtraction,
741 uninlined_format_args,
742 unnecessary_box_pin,
743 unnecessary_lazy_evaluations,
744 unnecessary_unwrap,
745 unnested_or_patterns,
746 unused_trait_names,
747 use_self,
748 zero_ptr,
749 )]
750 msrv("msrv"): Option<RustcVersion>,
751 #[lints(large_types_passed_by_value)]
753 pass_by_value_size_limit("pass-by-value-size-limit"): u64 = 256,
754 #[lints(pub_underscore_fields)]
757 pub_underscore_fields_behavior("pub-underscore-fields-behavior"): PubUnderscoreFieldsBehaviour = PubUnderscoreFieldsBehaviour::PubliclyExported,
758 #[lints(use_self)]
760 recursive_self_in_type_definitions("recursive-self-in-type-definitions"): bool = true,
761 #[lints(semicolon_inside_block)]
763 semicolon_inside_block_ignore_singleline("semicolon-inside-block-ignore-singleline"): bool = false,
764 #[lints(semicolon_outside_block)]
766 semicolon_outside_block_ignore_multiline("semicolon-outside-block-ignore-multiline"): bool = false,
767 #[lints(many_single_char_names)]
769 single_char_binding_names_threshold("single-char-binding-names-threshold"): u64 = 4,
770 #[lints(arbitrary_source_item_ordering)]
772 source_item_ordering("source-item-ordering"): SourceItemOrdering,
773 #[lints(large_stack_frames)]
775 stack_size_threshold("stack-size-threshold"): u64 = 512_000,
776 #[lints(nonstandard_macro_braces)]
782 standard_macro_braces("standard-macro-braces"): Vec<MacroMatcher>,
783 #[lints(struct_field_names)]
785 struct_field_name_threshold("struct-field-name-threshold"): u64 = 3,
786 #[lints(indexing_slicing)]
792 suppress_restriction_lint_in_const("suppress-restriction-lint-in-const"): bool = false,
793 #[lints(boxed_local, useless_vec)]
795 too_large_for_stack("too-large-for-stack"): u64 = 200,
796 #[lints(too_many_arguments)]
798 too_many_arguments_threshold("too-many-arguments-threshold"): u64 = 7,
799 #[lints(too_many_lines)]
801 too_many_lines_threshold("too-many-lines-threshold"): u64 = 100,
802 #[lints(arbitrary_source_item_ordering)]
804 trait_assoc_item_kinds_order("trait-assoc-item-kinds-order"): SourceItemOrderingTraitAssocItemKinds,
805 #[lints(arbitrary_source_item_ordering)]
821 trait_impl_item_order("trait-impl-item-order"): TraitImplItemOrder,
822 #[default_text = "target_pointer_width"]
825 #[lints(trivially_copy_pass_by_ref)]
826 trivial_copy_size_limit("trivial-copy-size-limit"): Option<u64>,
827 #[lints(type_complexity)]
829 type_complexity_threshold("type-complexity-threshold"): u64 = 250,
830 #[lints(unnecessary_box_returns)]
832 unnecessary_box_size("unnecessary-box-size"): u64 = 128,
833 #[lints(unreadable_literal)]
835 unreadable_literal_lint_fractions("unreadable-literal-lint-fractions"): bool = true,
836 #[lints(upper_case_acronyms)]
838 upper_case_acronyms_aggressive("upper-case-acronyms-aggressive"): bool = false,
839 #[lints(vec_box)]
841 vec_box_size_threshold("vec-box-size-threshold"): u64 = 4096,
842 #[lints(verbose_bit_mask)]
844 verbose_bit_mask_threshold("verbose-bit-mask-threshold"): u64 = 1,
845 #[lints(wildcard_imports)]
848 warn_on_all_wildcard_imports("warn-on-all-wildcard-imports"): bool = false,
849 #[lints(macro_metavars_in_unsafe)]
851 warn_unsafe_macro_metavars_in_private_macros("warn-unsafe-macro-metavars-in-private-macros"): bool = false,
852}
853
854pub fn sanitize_explanation(raw_docs: &str) -> String {
856 let mut explanation = String::with_capacity(128);
858 let mut in_code = false;
859 for line in raw_docs.lines() {
860 let line = line.strip_prefix(' ').unwrap_or(line);
861
862 if let Some(lang) = line.strip_prefix("```") {
863 let tag = lang.split_once(',').map_or(lang, |(left, _)| left);
864 if !in_code && matches!(tag, "" | "rust" | "ignore" | "should_panic" | "no_run" | "compile_fail") {
865 explanation += "```rust\n";
866 } else {
867 explanation += line;
868 explanation.push('\n');
869 }
870 in_code = !in_code;
871 } else if !(in_code && line.starts_with("# ")) {
872 explanation += line;
873 explanation.push('\n');
874 }
875 }
876
877 explanation
878}
879
880fn load_conf_file(sess: &Session) -> Option<Arc<SourceFile>> {
886 const CONFIG_FILE_NAMES: [&str; 2] = [".clippy.toml", "clippy.toml"];
888
889 const CONFIG_VARS: [(&str, &str); 2] = [
892 ("CLIPPY_CONF_DIR", "failed to read `CLIPPY_CONF_DIR` as a directory"),
893 (
894 "CARGO_MANIFEST_DIR",
895 "failed to read `CARGO_MANIFEST_DIR` as a directory",
896 ),
897 ];
898 let (current, msg) = CONFIG_VARS
899 .into_iter()
900 .find_map(|(var, msg)| env::var_os(var).map(|p| (PathBuf::from(p), msg)))
901 .unwrap_or_else(|| (PathBuf::from("."), "failed to get the current directory"));
902 let mut current = match current.canonicalize() {
903 Ok(x) => x,
904 Err(e) => {
905 sess.dcx().err(format!("{msg}: {e}"));
906 return None;
907 },
908 };
909
910 let mut loaded_config: Option<(PathBuf, Arc<SourceFile>)> = None;
911 loop {
912 for config_file_name in CONFIG_FILE_NAMES {
913 if let Ok(config_path) = current.join(config_file_name).canonicalize() {
914 if let Some((loaded_path, _)) = &loaded_config {
915 if fs::metadata(loaded_path).is_ok_and(|x| x.is_file()) {
916 sess.dcx().warn(format!(
918 "using config file `{}`, `{}` will be ignored",
919 loaded_path.display(),
920 config_path.display(),
921 ));
922 }
923 } else {
924 match sess.source_map().load_file(&config_path) {
925 Ok(src) => loaded_config = Some((config_path, src)),
926 Err(e)
927 if matches!(
928 e.kind(),
929 io::ErrorKind::NotFound | io::ErrorKind::IsADirectory | io::ErrorKind::NotADirectory
930 ) => {},
931 Err(e) => {
932 sess.dcx()
933 .err(format!("error reading `{}`: {e}", config_path.display()));
934 return None;
935 },
936 }
937 }
938 }
939 }
940
941 if let Some((_, src)) = loaded_config {
943 return Some(src);
944 }
945
946 if !current.pop() {
948 return None;
949 }
950 }
951}
952
953impl Conf {
954 pub fn load(sess: &Session) -> &'static Conf {
955 static CONF: OnceLock<Conf> = OnceLock::new();
956 CONF.get_or_init(|| Conf::load_inner(sess))
957 }
958
959 fn load_inner(sess: &Session) -> Conf {
960 let mut conf = if let Some(src) = load_conf_file(sess) {
961 let dcx = DiagCtxt::new(sess, src.start_pos.to_usize());
962 let src = src.src.as_ref().unwrap();
963
964 let (toml, errs) = DeTable::parse_recoverable(src.as_str());
965 for e in errs {
966 match e.span() {
967 Some(sp) => dcx.span_err(sp, e.message().to_owned()),
968 None => {
969 dcx.inner
970 .struct_err(format!("error parsing `clippy.toml`: {}", e.message()))
971 .emit();
972 },
973 }
974 }
975 Conf::deserialize(&dcx, toml.get_ref())
976 } else {
977 Conf::default()
978 };
979
980 let cargo_msrv = env::var("CARGO_PKG_RUST_VERSION")
981 .ok()
982 .and_then(|v| parse_version(Symbol::intern(&v)));
983 match (&conf.msrv, cargo_msrv) {
984 (None, Some(cargo_msrv)) => conf.msrv = Some(cargo_msrv),
985 (Some(clippy_msrv), Some(cargo_msrv)) => {
986 if *clippy_msrv != cargo_msrv {
987 sess.dcx().warn(format!(
988 "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{clippy_msrv}` from `clippy.toml`"
989 ));
990 }
991 },
992 (_, None) => {},
993 }
994
995 conf
996 }
997}
998
999#[cfg(test)]
1000mod tests {
1001 use rustc_data_structures::fx::FxHashSet;
1002 use std::fs;
1003 use toml::de::DeTable;
1004 use walkdir::WalkDir;
1005
1006 #[test]
1007 fn configs_are_tested() {
1008 let mut names: FxHashSet<_> = super::Conf::get_metadata()
1009 .into_iter()
1010 .filter(|meta| meta.renamed_to.is_none())
1011 .map(|meta| meta.name)
1012 .collect();
1013
1014 let toml_files = WalkDir::new("../tests")
1015 .into_iter()
1016 .map(Result::unwrap)
1017 .filter(|entry| entry.file_name() == "clippy.toml");
1018
1019 for entry in toml_files {
1020 let file = fs::read_to_string(entry.path()).unwrap();
1021 if let Ok(toml) = DeTable::parse(&file) {
1022 for (key, _) in toml.as_ref() {
1023 names.remove(&**key.get_ref());
1024 }
1025 }
1026 }
1027
1028 assert!(
1029 names.is_empty(),
1030 "Configuration variable lacks test: {names:?}\nAdd a test to `tests/ui-toml`"
1031 );
1032 }
1033}