//! Tests for internal code checks. #![allow(clippy::all)] use std::fs; #[test] fn check_forbidden_code() { // Do not use certain macros, functions, etc. if !cargo_util::is_ci() { // Only check these on CI, otherwise it could be annoying. use std::io::Write; writeln!( std::io::stderr(), "\nSkipping check_forbidden_code test, set CI=1 to enable" ) .unwrap(); return; } let root_path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src"); for entry in walkdir::WalkDir::new(&root_path) .into_iter() .filter_entry(|e| e.path() != root_path.join("doc")) .filter_map(|e| e.ok()) { let path = entry.path(); if !entry .file_name() .to_str() .map(|s| s.ends_with(".rs")) .unwrap_or(false) { continue; } eprintln!("checking {}", path.display()); let c = fs::read_to_string(path).unwrap(); for (line_index, line) in c.lines().enumerate() { if line.trim().starts_with("//") { continue; } if line_has_print(line) { if entry.file_name().to_str().unwrap() == "cargo_new.rs" && line.contains("Hello") { // An exception. continue; } panic!( "found print macro in {}:{}\n\n{}\n\n\ print! macros should not be used in Cargo because they can panic.\n\ Use one of the drop_print macros instead.\n\ ", path.display(), line_index, line ); } if line_has_macro(line, "dbg") { panic!( "found dbg! macro in {}:{}\n\n{}\n\n\ dbg! should not be used outside of debugging.", path.display(), line_index, line ); } } } } fn line_has_print(line: &str) -> bool { line_has_macro(line, "print") || line_has_macro(line, "eprint") || line_has_macro(line, "println") || line_has_macro(line, "eprintln") } #[test] fn line_has_print_works() { assert!(line_has_print("print!")); assert!(line_has_print("println!")); assert!(line_has_print("eprint!")); assert!(line_has_print("eprintln!")); assert!(line_has_print("(print!(\"hi!\"))")); assert!(!line_has_print("print")); assert!(!line_has_print("i like to print things")); assert!(!line_has_print("drop_print!")); assert!(!line_has_print("drop_println!")); assert!(!line_has_print("drop_eprint!")); assert!(!line_has_print("drop_eprintln!")); } fn line_has_macro(line: &str, mac: &str) -> bool { for (i, _) in line.match_indices(mac) { if line.get(i + mac.len()..i + mac.len() + 1) != Some("!") { continue; } if i == 0 { return true; } // Check for identifier boundary start. let prev1 = line.get(i - 1..i).unwrap().chars().next().unwrap(); if prev1.is_alphanumeric() || prev1 == '_' { continue; } return true; } false }