1//! Tidy check to ensure that unstable features are all in order.2//!3//! This check will ensure properties like:4//!5//! * All stability attributes look reasonably well formed.6//! * The set of library features is disjoint from the set of language features.7//! * Library features have at most one stability level.8//! * Library features have at most one `since` value.9//! * All unstable lang features have tests to ensure they are actually unstable.10//! * Language features in a group are sorted by feature name.1112use std::collections::BTreeSet;13use std::collections::hash_map::{Entry, HashMap};14use std::ffi::OsStr;15use std::num::NonZeroU32;16use std::path::{Path, PathBuf};17use std::{fmt, fs};1819use crate::diagnostics::{RunningCheck, TidyCtx};20use crate::walk::{filter_dirs, filter_not_rust, walk, walk_many};2122#[cfg(test)]23mod tests;2425mod version;26use regex::Regex;27use version::Version;2829const FEATURE_GROUP_START_PREFIX: &str = "// feature-group-start";30const FEATURE_GROUP_END_PREFIX: &str = "// feature-group-end";3132#[derive(Debug, PartialEq, Clone)]33#[cfg_attr(feature = "build-metrics", derive(serde::Serialize))]34pub enum Status {35 Accepted,36 Removed,37 Unstable,38}3940impl fmt::Display for Status {41 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {42 let as_str = match *self {43 Status::Accepted => "accepted",44 Status::Unstable => "unstable",45 Status::Removed => "removed",46 };47 fmt::Display::fmt(as_str, f)48 }49}5051#[derive(Debug, Clone)]52#[cfg_attr(feature = "build-metrics", derive(serde::Serialize))]53pub struct Feature {54 pub level: Status,55 pub since: Option<Version>,56 pub has_gate_test: bool,57 pub tracking_issue: Option<NonZeroU32>,58 pub file: PathBuf,59 pub line: usize,60 pub description: Option<String>,61}62impl Feature {63 fn tracking_issue_display(&self) -> impl fmt::Display {64 match self.tracking_issue {65 None => "none".to_string(),66 Some(x) => x.to_string(),67 }68 }69}7071pub type Features = HashMap<String, Feature>;7273pub struct CollectedFeatures {74 pub lib: Features,75 pub lang: Features,76}7778// Currently only used for unstable book generation79pub fn collect_lib_features(base_src_path: &Path) -> Features {80 let mut lib_features = Features::new();8182 map_lib_features(base_src_path, &mut |res, _, _| {83 if let Ok((name, feature)) = res {84 lib_features.insert(name.to_owned(), feature);85 }86 });87 lib_features88}8990pub fn check(91 src_path: &Path,92 tests_path: &Path,93 compiler_path: &Path,94 lib_path: &Path,95 tidy_ctx: TidyCtx,96) -> CollectedFeatures {97 let mut check = tidy_ctx.start_check("features");9899 let mut features = collect_lang_features(compiler_path, &mut check);100 assert!(!features.is_empty());101102 let lib_features = get_and_check_lib_features(lib_path, &mut check, &features);103 assert!(!lib_features.is_empty());104105 walk_many(106 &[107 &tests_path.join("ui"),108 &tests_path.join("ui-fulldeps"),109 &tests_path.join("rustdoc-ui"),110 &tests_path.join("rustdoc-html"),111 ],112 |path, _is_dir| {113 filter_dirs(path)114 || filter_not_rust(path)115 || path.file_name() == Some(OsStr::new("features.rs"))116 || path.file_name() == Some(OsStr::new("diagnostic_list.rs"))117 },118 &mut |entry, contents| {119 let file = entry.path();120 let filename = file.file_name().unwrap().to_string_lossy();121 let filen_underscore = filename.replace('-', "_").replace(".rs", "");122 let filename_gate = test_filen_gate(&filen_underscore, &mut features);123124 for (i, line) in contents.lines().enumerate() {125 let mut err = |msg: &str| {126 check.error(format!("{}:{}: {}", file.display(), i + 1, msg));127 };128129 let gate_test_str = "gate-test-";130131 let feature_name = match line.find(gate_test_str) {132 // `split` always contains at least 1 element, even if the delimiter is not present.133 Some(i) => line[i + gate_test_str.len()..].split(' ').next().unwrap(),134 None => continue,135 };136 match features.get_mut(feature_name) {137 Some(f) => {138 if filename_gate == Some(feature_name) {139 err(&format!(140 "The file is already marked as gate test \141 through its name, no need for a \142 'gate-test-{feature_name}' comment"143 ));144 }145 f.has_gate_test = true;146 }147 None => {148 err(&format!(149 "gate-test test found referencing a nonexistent feature '{feature_name}'"150 ));151 }152 }153 }154 },155 );156157 // Only check the number of lang features.158 // Obligatory testing for library features is dumb.159 let gate_untested = features160 .iter()161 .filter(|&(_, f)| f.level == Status::Unstable)162 .filter(|&(_, f)| !f.has_gate_test)163 .collect::<Vec<_>>();164165 for &(name, _) in gate_untested.iter() {166 println!("Expected a gate test for the feature '{name}'.");167 println!(168 "Hint: create a failing test file named 'tests/ui/feature-gates/feature-gate-{}.rs',\169 \n with its failures due to missing usage of `#![feature({})]`.",170 name.replace("_", "-"),171 name172 );173 println!(174 "Hint: If you already have such a test and don't want to rename it,\175 \n you can also add a // gate-test-{name} line to the test file."176 );177 }178179 if !gate_untested.is_empty() {180 check.error(format!("Found {} features without a gate test.", gate_untested.len()));181 }182183 let (version, channel) = get_version_and_channel(src_path);184185 let all_features_iter = features186 .iter()187 .map(|feat| (feat, "lang"))188 .chain(lib_features.iter().map(|feat| (feat, "lib")));189 for ((feature_name, feature), kind) in all_features_iter {190 let since = if let Some(since) = feature.since { since } else { continue };191 let file = feature.file.display();192 let line = feature.line;193 if since > version && since != Version::CurrentPlaceholder {194 check.error(format!(195 "{file}:{line}: The stabilization version {since} of {kind} feature `{feature_name}` is newer than the current {version}"196 ));197 }198 if channel == "nightly" && since == version {199 check.error(format!(200 "{file}:{line}: The stabilization version {since} of {kind} feature `{feature_name}` is written out but should be {}",201 version::VERSION_PLACEHOLDER202 ));203 }204 if channel != "nightly" && since == Version::CurrentPlaceholder {205 check.error(format!(206 "{file}:{line}: The placeholder use of {kind} feature `{feature_name}` is not allowed on the {channel} channel",207 ));208 }209 }210211 if !check.is_bad() && check.is_verbose_enabled() {212 let mut lines = Vec::new();213 lines.extend(format_features(&features, "lang"));214 lines.extend(format_features(&lib_features, "lib"));215 lines.sort();216217 check.verbose_msg(218 lines.into_iter().map(|l| format!("* {l}")).collect::<Vec<String>>().join("\n"),219 );220 }221222 CollectedFeatures { lib: lib_features, lang: features }223}224225fn get_version_and_channel(src_path: &Path) -> (Version, String) {226 let version_str = t!(std::fs::read_to_string(src_path.join("version")));227 let version_str = version_str.trim();228 let version = t!(std::str::FromStr::from_str(version_str).map_err(|e| format!("{e:?}")));229 let channel_str = t!(std::fs::read_to_string(src_path.join("ci").join("channel")));230 (version, channel_str.trim().to_owned())231}232233fn format_features<'a>(234 features: &'a Features,235 family: &'a str,236) -> impl Iterator<Item = String> + 'a {237 features.iter().map(move |(name, feature)| {238 format!(239 "{:<32} {:<8} {:<12} {:<8}",240 name,241 family,242 feature.level,243 feature.since.map_or("None".to_owned(), |since| since.to_string())244 )245 })246}247248fn find_attr_val<'a>(line: &'a str, attr: &str) -> Option<&'a str> {249 let r = match attr {250 "issue" => static_regex!(r#"issue\s*=\s*"([^"]*)""#),251 "feature" => static_regex!(r#"feature\s*=\s*"([^"]*)""#),252 "since" => static_regex!(r#"since\s*=\s*"([^"]*)""#),253 _ => unimplemented!("{attr} not handled"),254 };255256 r.captures(line).and_then(|c| c.get(1)).map(|m| m.as_str())257}258259fn test_filen_gate<'f>(filen_underscore: &'f str, features: &mut Features) -> Option<&'f str> {260 let prefix = "feature_gate_";261 if let Some(suffix) = filen_underscore.strip_prefix(prefix) {262 for (n, f) in features.iter_mut() {263 // Equivalent to filen_underscore == format!("feature_gate_{n}")264 if suffix == n {265 f.has_gate_test = true;266 return Some(suffix);267 }268 }269 }270 None271}272273pub fn collect_lang_features(base_compiler_path: &Path, check: &mut RunningCheck) -> Features {274 let mut features = Features::new();275 collect_lang_features_in(&mut features, base_compiler_path, "accepted.rs", check);276 collect_lang_features_in(&mut features, base_compiler_path, "removed.rs", check);277 collect_lang_features_in(&mut features, base_compiler_path, "unstable.rs", check);278 features279}280281fn collect_lang_features_in(282 features: &mut Features,283 base: &Path,284 file: &str,285 check: &mut RunningCheck,286) {287 let path = base.join("rustc_feature").join("src").join(file);288 let contents = t!(fs::read_to_string(&path));289290 // We allow rustc-internal features to omit a tracking issue.291 // To make tidy accept omitting a tracking issue, group the list of features292 // without one inside `// no-tracking-issue` and `// no-tracking-issue-end`.293 let mut next_feature_omits_tracking_issue = false;294295 let mut in_feature_group = false;296 let mut prev_names = vec![];297298 let lines = contents.lines().zip(1..);299 let mut doc_comments: Vec<String> = Vec::new();300 for (line, line_number) in lines {301 let line = line.trim();302303 // Within -start and -end, the tracking issue can be omitted.304 match line {305 "// no-tracking-issue-start" => {306 next_feature_omits_tracking_issue = true;307 continue;308 }309 "// no-tracking-issue-end" => {310 next_feature_omits_tracking_issue = false;311 continue;312 }313 _ => {}314 }315316 if line.starts_with(FEATURE_GROUP_START_PREFIX) {317 if in_feature_group {318 check.error(format!(319 "{}:{line_number}: \320 new feature group is started without ending the previous one",321 path.display()322 ));323 }324325 in_feature_group = true;326 prev_names = vec![];327 continue;328 } else if line.starts_with(FEATURE_GROUP_END_PREFIX) {329 in_feature_group = false;330 prev_names = vec![];331 continue;332 }333334 if in_feature_group && let Some(doc_comment) = line.strip_prefix("///") {335 doc_comments.push(doc_comment.trim().to_string());336 continue;337 }338339 let mut parts = line.split(',');340 let level = match parts.next().map(|l| l.trim().trim_start_matches('(')) {341 Some("unstable") => Status::Unstable,342 Some("incomplete") => Status::Unstable,343 Some("internal") => Status::Unstable,344 Some("removed") => Status::Removed,345 Some("accepted") => Status::Accepted,346 _ => continue,347 };348 let name = parts.next().unwrap().trim();349350 let since_str = parts.next().unwrap().trim().trim_matches('"');351 let since = match since_str.parse() {352 Ok(since) => Some(since),353 Err(err) => {354 check.error(format!(355 "{}:{line_number}: failed to parse since: {since_str} ({err:?})",356 path.display()357 ));358 None359 }360 };361 if in_feature_group {362 if prev_names.last() > Some(&name) {363 // This assumes the user adds the feature name at the end of the list, as we're364 // not looking ahead.365 let correct_index = match prev_names.binary_search(&name) {366 Ok(_) => {367 // This only occurs when the feature name has already been declared.368 check.error(format!(369 "{}:{line_number}: duplicate feature {name}",370 path.display()371 ));372 // skip any additional checks for this line373 continue;374 }375 Err(index) => index,376 };377378 let correct_placement = if correct_index == 0 {379 "at the beginning of the feature group".to_owned()380 } else if correct_index == prev_names.len() {381 // I don't believe this is reachable given the above assumption, but it382 // doesn't hurt to be safe.383 "at the end of the feature group".to_owned()384 } else {385 format!(386 "between {} and {}",387 prev_names[correct_index - 1],388 prev_names[correct_index],389 )390 };391392 check.error(format!(393 "{}:{line_number}: feature {name} is not sorted by feature name (should be {correct_placement})",394 path.display(),395 ));396 }397 prev_names.push(name);398 }399400 let issue_str = parts.next().unwrap().trim();401 let tracking_issue = if issue_str.starts_with("None") {402 if level == Status::Unstable && !next_feature_omits_tracking_issue {403 check.error(format!(404 "{}:{line_number}: no tracking issue for feature {name}",405 path.display(),406 ));407 }408 None409 } else {410 let s = issue_str.split('(').nth(1).unwrap().split(')').next().unwrap();411 Some(s.parse().unwrap())412 };413 match features.entry(name.to_owned()) {414 Entry::Occupied(e) => {415 check.error(format!(416 "{}:{line_number} feature {name} already specified with status '{}'",417 path.display(),418 e.get().level,419 ));420 }421 Entry::Vacant(e) => {422 e.insert(Feature {423 level,424 since,425 has_gate_test: false,426 tracking_issue,427 file: path.to_path_buf(),428 line: line_number,429 description: if doc_comments.is_empty() {430 None431 } else {432 Some(doc_comments.join(" "))433 },434 });435 }436 }437 doc_comments.clear();438 }439}440441fn get_and_check_lib_features(442 base_src_path: &Path,443 check: &mut RunningCheck,444 lang_features: &Features,445) -> Features {446 let mut lib_features = Features::new();447 map_lib_features(base_src_path, &mut |res, file, line| match res {448 Ok((name, f)) => {449 let mut check_features = |f: &Feature, list: &Features, display: &str| {450 if let Some(s) = list.get(name)451 && f.tracking_issue != s.tracking_issue452 && f.level != Status::Accepted453 {454 check.error(format!(455 "{}:{line}: feature gate {name} has inconsistent `issue`: \"{}\" mismatches the {display} `issue` of \"{}\"",456 file.display(),457 f.tracking_issue_display(),458 s.tracking_issue_display(),459 ));460 }461 };462 check_features(&f, lang_features, "corresponding lang feature");463 check_features(&f, &lib_features, "previous");464 lib_features.insert(name.to_owned(), f);465 }466 Err(msg) => {467 check.error(format!("{}:{line}: {msg}", file.display()));468 }469 });470 lib_features471}472473fn map_lib_features(474 base_src_path: &Path,475 mf: &mut (dyn Send + Sync + FnMut(Result<(&str, Feature), &str>, &Path, usize)),476) {477 walk(478 base_src_path,479 |path, _is_dir| filter_dirs(path) || path.ends_with("tests"),480 &mut |entry, contents| {481 let file = entry.path();482 let filename = file.file_name().unwrap().to_string_lossy();483 if !filename.ends_with(".rs")484 || filename == "features.rs"485 || filename == "diagnostic_list.rs"486 || filename == "error_codes.rs"487 {488 return;489 }490491 // This is an early exit -- all the attributes we're concerned with must contain this:492 // * rustc_const_unstable(493 // * unstable(494 // * stable(495 if !contents.contains("stable(") {496 return;497 }498499 let handle_issue_none = |s| match s {500 "none" => None,501 issue => {502 let n = issue.parse().expect("issue number is not a valid integer");503 assert_ne!(n, 0, "\"none\" should be used when there is no issue, not \"0\"");504 NonZeroU32::new(n)505 }506 };507 let mut becoming_feature: Option<(&str, Feature)> = None;508 let mut iter_lines = contents.lines().enumerate().peekable();509 while let Some((i, line)) = iter_lines.next() {510 macro_rules! err {511 ($msg:expr) => {{512 mf(Err($msg), file, i + 1);513 continue;514 }};515 }516517 // exclude commented out lines518 if static_regex!(r"^\s*//").is_match(line) {519 continue;520 }521522 if let Some((name, ref mut f)) = becoming_feature {523 if f.tracking_issue.is_none() {524 f.tracking_issue = find_attr_val(line, "issue").and_then(handle_issue_none);525 }526 if line.ends_with(']') {527 mf(Ok((name, f.clone())), file, i + 1);528 } else if !line.ends_with(',') && !line.ends_with('\\') && !line.ends_with('"')529 {530 // We need to bail here because we might have missed the531 // end of a stability attribute above because the ']'532 // might not have been at the end of the line.533 // We could then get into the very unfortunate situation that534 // we continue parsing the file assuming the current stability535 // attribute has not ended, and ignoring possible feature536 // attributes in the process.537 err!("malformed stability attribute");538 } else {539 continue;540 }541 }542 becoming_feature = None;543 if line.contains("rustc_const_unstable(") {544 // `const fn` features are handled specially.545 let feature_name = match find_attr_val(line, "feature").or_else(|| {546 iter_lines.peek().and_then(|next| find_attr_val(next.1, "feature"))547 }) {548 Some(name) => name,549 None => err!("malformed stability attribute: missing `feature` key"),550 };551 let feature = Feature {552 level: Status::Unstable,553 since: None,554 has_gate_test: false,555 tracking_issue: find_attr_val(line, "issue").and_then(handle_issue_none),556 file: file.to_path_buf(),557 line: i + 1,558 description: None,559 };560 mf(Ok((feature_name, feature)), file, i + 1);561 continue;562 }563 let level = if line.contains("[unstable(") {564 Status::Unstable565 } else if line.contains("[stable(") {566 Status::Accepted567 } else {568 continue;569 };570 let feature_name = match find_attr_val(line, "feature")571 .or_else(|| iter_lines.peek().and_then(|next| find_attr_val(next.1, "feature")))572 {573 Some(name) => name,574 None => err!("malformed stability attribute: missing `feature` key"),575 };576 let since = match find_attr_val(line, "since").map(|x| x.parse()) {577 Some(Ok(since)) => Some(since),578 Some(Err(_err)) => {579 err!("malformed stability attribute: can't parse `since` key");580 }581 None if level == Status::Accepted => {582 err!("malformed stability attribute: missing the `since` key");583 }584 None => None,585 };586 let tracking_issue = find_attr_val(line, "issue").and_then(handle_issue_none);587588 let feature = Feature {589 level,590 since,591 has_gate_test: false,592 tracking_issue,593 file: file.to_path_buf(),594 line: i + 1,595 description: None,596 };597 if line.contains(']') {598 mf(Ok((feature_name, feature)), file, i + 1);599 } else {600 becoming_feature = Some((feature_name, feature));601 }602 }603 },604 );605}606607fn should_document(var: &str) -> bool {608 if var.starts_with("RUSTC_") || var.starts_with("RUST_") || var.starts_with("UNSTABLE_RUSTDOC_")609 {610 return true;611 }612 ["SDKROOT", "QNX_TARGET", "COLORTERM", "TERM"].contains(&var)613}614615pub fn collect_env_vars(compiler: &Path) -> BTreeSet<String> {616 let env_var_regex: Regex = Regex::new(r#"env::var(_os)?\("([^"]+)"#).unwrap();617618 let mut vars = BTreeSet::new();619 walk(620 compiler,621 // skip build scripts, tests, and non-rust files622 |path, _is_dir| {623 filter_dirs(path)624 || filter_not_rust(path)625 || path.ends_with("build.rs")626 || path.ends_with("tests.rs")627 },628 &mut |_entry, contents| {629 for env_var in env_var_regex.captures_iter(contents).map(|c| c.get(2).unwrap().as_str())630 {631 if should_document(env_var) {632 vars.insert(env_var.to_owned());633 }634 }635 },636 );637 vars638}
Code quality findings 24
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning
correctness
unwrap-usage
let filename = file.file_name().unwrap().to_string_lossy();
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning
correctness
unwrap-usage
Some(i) => line[i + gate_test_str.len()..].split(' ').next().unwrap(),
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning
correctness
unchecked-indexing
Some(i) => line[i + gate_test_str.len()..].split(' ').next().unwrap(),
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning
correctness
unwrap-usage
let name = parts.next().unwrap().trim();
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning
correctness
unwrap-usage
let since_str = parts.next().unwrap().trim().trim_matches('"');
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning
correctness
unchecked-indexing
prev_names[correct_index - 1],
Warning: Direct indexing (e.g., `vec[i]`, `slice[i]`) panics on out-of-bounds access. Prefer using `.get(index)` or `.get_mut(index)` which return Option<&T>/Option<&mut T>.
warning
correctness
unchecked-indexing
prev_names[correct_index],
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning
correctness
unwrap-usage
let issue_str = parts.next().unwrap().trim();
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning
correctness
unwrap-usage
let s = issue_str.split('(').nth(1).unwrap().split(')').next().unwrap();
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning
correctness
unwrap-usage
Some(s.parse().unwrap())
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning
correctness
unwrap-usage
let filename = file.file_name().unwrap().to_string_lossy();
Warning: '.expect()' will panic with a custom message on None/Err. While better than unwrap() for debugging, prefer non-panicking error handling in production code (match, if let, ?).
warning
correctness
expect-usage
let n = issue.parse().expect("issue number is not a valid integer");
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning
correctness
unwrap-usage
let env_var_regex: Regex = Regex::new(r#"env::var(_os)?\("([^"]+)"#).unwrap();
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning
correctness
unwrap-usage
for env_var in env_var_regex.captures_iter(contents).map(|c| c.get(2).unwrap().as_str())
Info: Direct printing to stdout/stderr. For application logging, prefer using a logging facade like `log` or `tracing` for better control over levels, formatting, and output destinations.
info
maintainability
println-macro
println!("Expected a gate test for the feature '{name}'.");
Info: Direct printing to stdout/stderr. For application logging, prefer using a logging facade like `log` or `tracing` for better control over levels, formatting, and output destinations.
info
maintainability
println-macro
println!(
Info: Direct printing to stdout/stderr. For application logging, prefer using a logging facade like `log` or `tracing` for better control over levels, formatting, and output destinations.
info
maintainability
println-macro
println!(
Info: This standard library function returns a Result. Ensure the Result is handled properly (e.g., using '?', match, if let) rather than potentially panicking with .unwrap() or .expect().
info
correctness
unhandled-result
let version_str = t!(std::fs::read_to_string(src_path.join("version")));
Info: This standard library function returns a Result. Ensure the Result is handled properly (e.g., using '?', match, if let) rather than potentially panicking with .unwrap() or .expect().
info
correctness
unhandled-result
let channel_str = t!(std::fs::read_to_string(src_path.join("ci").join("channel")));
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info
correctness
match-wildcard
let r = match attr {
Maintainability Info: `todo!()` or `unimplemented!()` macros indicate incomplete code paths that will panic at runtime if reached. Ensure these are replaced with actual logic before production use.
info
correctness
todo-unimplemented
_ => unimplemented!("{attr} not handled"),
Info: This standard library function returns a Result. Ensure the Result is handled properly (e.g., using '?', match, if let) rather than potentially panicking with .unwrap() or .expect().
info
correctness
unhandled-result
let contents = t!(fs::read_to_string(&path));
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info
correctness
match-wildcard
match line {
Info: Ensure 'match' statements are exhaustive. If matching on enums, consider adding a wildcard arm `_ => {}` only if necessary and intentional, as it suppresses warnings about unhandled variants.
info
correctness
match-wildcard
let level = match parts.next().map(|l| l.trim().trim_start_matches('(')) {