summaryrefslogtreecommitdiffstats
path: root/src/tools/cargo/tests/internal.rs
blob: c42cfa8f07b409e98c19acf46e6fd20354bcf05a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
//! 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
}