Skip to main content

ruff_python_trivia/
pragmas.rs

1/// Returns `true` if a comment appears to be a pragma comment.
2///
3/// ```
4/// assert!(ruff_python_trivia::is_pragma_comment("# type: ignore"));
5/// assert!(ruff_python_trivia::is_pragma_comment("# noqa: F401"));
6/// assert!(ruff_python_trivia::is_pragma_comment("# noqa"));
7/// assert!(ruff_python_trivia::is_pragma_comment("# NoQA"));
8/// assert!(ruff_python_trivia::is_pragma_comment("# nosec"));
9/// assert!(ruff_python_trivia::is_pragma_comment("# nosec B602, B607"));
10/// assert!(ruff_python_trivia::is_pragma_comment("# isort: off"));
11/// assert!(ruff_python_trivia::is_pragma_comment("# isort: skip"));
12/// assert!(ruff_python_trivia::is_pragma_comment("# pyrefly: ignore[missing-attribute]"));
13/// ```
14pub fn is_pragma_comment(comment: &str) -> bool {
15    let Some(content) = comment.strip_prefix('#') else {
16        return false;
17    };
18    let trimmed = content.trim_start();
19
20    // Case-insensitive match against `noqa` (which doesn't require a trailing colon).
21    if matches!(
22        trimmed.as_bytes(),
23        [b'n' | b'N', b'o' | b'O', b'q' | b'Q', b'a' | b'A', ..]
24    ) {
25        return true;
26    }
27
28    // Case-insensitive match against pragmas that don't require a trailing colon.
29    if trimmed.starts_with("nosec") {
30        return true;
31    }
32
33    // Case-sensitive match against a variety of pragmas that _do_ require a trailing colon.
34    trimmed.split_once(':').is_some_and(|(maybe_pragma, _)| {
35        matches!(
36            maybe_pragma,
37            "isort" | "type" | "pyright" | "pyrefly" | "pylint" | "flake8" | "ruff" | "ty"
38        )
39    })
40}
41
42/// Returns the byte offset within `comment` where a trailing pragma comment starts,
43/// or `None` if no pragma is found.
44///
45/// For a plain pragma like `# noqa: F401`, returns `Some(0)`.
46/// For a nested pragma like `# some text # noqa: F401`, returns the offset of the
47/// trailing `#` that begins the pragma (i.e., the start of `# noqa: F401`).
48///
49/// ```
50/// assert_eq!(ruff_python_trivia::find_trailing_pragma_offset("# noqa: F401"), Some(0));
51/// assert_eq!(ruff_python_trivia::find_trailing_pragma_offset("# type: ignore"), Some(0));
52/// assert_eq!(ruff_python_trivia::find_trailing_pragma_offset("# some comment # noqa: F401"), Some(15));
53/// assert_eq!(ruff_python_trivia::find_trailing_pragma_offset("## noqa: F401"), Some(1));
54/// assert_eq!(ruff_python_trivia::find_trailing_pragma_offset("# just a comment"), None);
55/// ```
56pub fn find_trailing_pragma_offset(comment: &str) -> Option<usize> {
57    comment.match_indices('#').find_map(|(offset, _)| {
58        let sub_comment = &comment[offset..];
59        if is_pragma_comment(sub_comment) {
60            Some(offset)
61        } else {
62            None
63        }
64    })
65}