|
| 1 | +//! Checks that no Fluent messages or attributes end in periods (except ellipses) |
| 2 | +
|
| 3 | +use fluent_syntax::ast::{Entry, PatternElement}; |
| 4 | + |
| 5 | +use crate::walk::{filter_dirs, walk}; |
| 6 | +use std::path::Path; |
| 7 | + |
| 8 | +fn filter_fluent(path: &Path) -> bool { |
| 9 | + if let Some(ext) = path.extension() { ext.to_str() != Some("ftl") } else { true } |
| 10 | +} |
| 11 | + |
| 12 | +/// Messages allowed to have `.` at their end. |
| 13 | +/// |
| 14 | +/// These should probably be reworked eventually. |
| 15 | +const ALLOWLIST: &[&str] = &[ |
| 16 | + "const_eval_long_running", |
| 17 | + "const_eval_validation_failure_note", |
| 18 | + "driver_impl_ice", |
| 19 | + "incremental_corrupt_file", |
| 20 | + "mir_build_pointer_pattern", |
| 21 | +]; |
| 22 | + |
| 23 | +fn check_period(filename: &str, contents: &str, bad: &mut bool) { |
| 24 | + if filename.contains("codegen") { |
| 25 | + // FIXME: Too many codegen messages have periods right now... |
| 26 | + return; |
| 27 | + } |
| 28 | + |
| 29 | + let (Ok(parse) | Err((parse, _))) = fluent_syntax::parser::parse(contents); |
| 30 | + for entry in &parse.body { |
| 31 | + if let Entry::Message(m) = entry { |
| 32 | + if ALLOWLIST.contains(&m.id.name) { |
| 33 | + continue; |
| 34 | + } |
| 35 | + |
| 36 | + if let Some(pat) = &m.value { |
| 37 | + if let Some(PatternElement::TextElement { value }) = pat.elements.last() { |
| 38 | + // We don't care about ellipses. |
| 39 | + if value.ends_with(".") && !value.ends_with("...") { |
| 40 | + let ll = find_line(contents, *value); |
| 41 | + let name = m.id.name; |
| 42 | + tidy_error!(bad, "{filename}:{ll}: message `{name}` ends in a period"); |
| 43 | + } |
| 44 | + } |
| 45 | + } |
| 46 | + |
| 47 | + for attr in &m.attributes { |
| 48 | + // Teach notes usually have long messages. |
| 49 | + if attr.id.name == "teach_note" { |
| 50 | + continue; |
| 51 | + } |
| 52 | + |
| 53 | + if let Some(PatternElement::TextElement { value }) = attr.value.elements.last() { |
| 54 | + if value.ends_with(".") && !value.ends_with("...") { |
| 55 | + let ll = find_line(contents, *value); |
| 56 | + let name = attr.id.name; |
| 57 | + tidy_error!(bad, "{filename}:{ll}: attr `{name}` ends in a period"); |
| 58 | + } |
| 59 | + } |
| 60 | + } |
| 61 | + } |
| 62 | + } |
| 63 | +} |
| 64 | + |
| 65 | +/// Evil cursed bad hack. Requires that `value` be a substr (in memory) of `contents`. |
| 66 | +fn find_line(haystack: &str, needle: &str) -> usize { |
| 67 | + for (ll, line) in haystack.lines().enumerate() { |
| 68 | + if line.as_ptr() > needle.as_ptr() { |
| 69 | + return ll; |
| 70 | + } |
| 71 | + } |
| 72 | + |
| 73 | + 1 |
| 74 | +} |
| 75 | + |
| 76 | +pub fn check(path: &Path, bad: &mut bool) { |
| 77 | + walk( |
| 78 | + path, |
| 79 | + |path, is_dir| filter_dirs(path) || (!is_dir && filter_fluent(path)), |
| 80 | + &mut |ent, contents| { |
| 81 | + check_period(ent.path().to_str().unwrap(), contents, bad); |
| 82 | + }, |
| 83 | + ); |
| 84 | +} |
0 commit comments