|
3 | 3 | // file COPYING or https://opensource.org/license/mit/. |
4 | 4 |
|
5 | 5 | use std::env; |
6 | | -use std::fs; |
7 | | -use std::io::ErrorKind; |
| 6 | +use std::fs::{self, File}; |
| 7 | +use std::io::{ErrorKind, Read, Seek, SeekFrom}; |
8 | 8 | use std::path::PathBuf; |
9 | 9 | use std::process::{Command, ExitCode, Stdio}; |
10 | 10 |
|
@@ -88,6 +88,11 @@ fn get_linter_list() -> Vec<&'static Linter> { |
88 | 88 | name: "trailing_whitespace", |
89 | 89 | lint_fn: lint_trailing_whitespace |
90 | 90 | }, |
| 91 | + &Linter { |
| 92 | + description: "Check for trailing newline", |
| 93 | + name: "trailing_newline", |
| 94 | + lint_fn: lint_trailing_newline |
| 95 | + }, |
91 | 96 | &Linter { |
92 | 97 | description: "Run all linters of the form: test/lint/lint-*.py", |
93 | 98 | name: "all_python_linters", |
@@ -514,6 +519,44 @@ sourced files to the exclude list. |
514 | 519 | } |
515 | 520 | } |
516 | 521 |
|
| 522 | +fn lint_trailing_newline() -> LintResult { |
| 523 | + let files = check_output( |
| 524 | + git() |
| 525 | + .args([ |
| 526 | + "ls-files", "--", "*.py", "*.cpp", "*.h", "*.md", "*.rs", "*.sh", "*.cmake", |
| 527 | + ]) |
| 528 | + .args(get_pathspecs_default_excludes()), |
| 529 | + )?; |
| 530 | + let mut missing_newline = false; |
| 531 | + for path in files.lines() { |
| 532 | + let mut file = File::open(path).expect("must be able to open file"); |
| 533 | + if file.seek(SeekFrom::End(-1)).is_err() { |
| 534 | + continue; // Allow fully empty files |
| 535 | + } |
| 536 | + let mut buffer = [0u8; 1]; |
| 537 | + file.read_exact(&mut buffer) |
| 538 | + .expect("must be able to read the last byte"); |
| 539 | + if buffer[0] != b'\n' { |
| 540 | + missing_newline = true; |
| 541 | + println!("{path}"); |
| 542 | + } |
| 543 | + } |
| 544 | + if missing_newline { |
| 545 | + Err(r#" |
| 546 | +A trailing newline is required, because git may warn about it missing. Also, it can make diffs |
| 547 | +verbose and can break git blame after appending lines. |
| 548 | +
|
| 549 | +Thus, it is best to add the trailing newline now. |
| 550 | +
|
| 551 | +Please add any false positives to the exclude list. |
| 552 | + "# |
| 553 | + .trim() |
| 554 | + .to_string()) |
| 555 | + } else { |
| 556 | + Ok(()) |
| 557 | + } |
| 558 | +} |
| 559 | + |
517 | 560 | fn lint_tabs_whitespace() -> LintResult { |
518 | 561 | let tabs = git() |
519 | 562 | .args(["grep", "-I", "--line-number", "--perl-regexp", "^\\t", "--"]) |
|
0 commit comments