lint: [move-only] Move text related lints to text_format.rs
What changed, and why it matters
This commit simply reorganizes existing code. It moves four text-format lint checks (for trailing whitespace, missing trailing newlines, tab characters, and commit message formatting) from the main Rust file into a new module named lint_text_format.rs. No lint logic, command behavior, or security-sensitive code was changed.
No security action needed. This is a non-functional code-movement refactor of lint tooling.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The diff is a pure refactor of the Bitcoin Core test_runner lint harness. Functions lint_trailing_whitespace, lint_trailing_newline, lint_tabs_whitespace, lint_commit_msg, and their helper get_pathspecs_exclude_whitespace are relocated from test/lint/test_runner/src/main.rs to a new file test/lint/test_runner/src/lint_text_format.rs. main.rs adds mod lint_text_format and imports the moved functions. The function bodies, exclude lists, and error messages are byte-for-byte identical to the originals.
Changed components
test/lint/test_runner/src/main.rstest/lint/test_runner/src/lint_text_format.rsInspect captured patch +165 / −153
diff --git a/test/lint/test_runner/src/lint_text_format.rs b/test/lint/test_runner/src/lint_text_format.rs
new file mode 100644
index 00000000..ca51e6db
--- /dev/null
+++ b/test/lint/test_runner/src/lint_text_format.rs
@@ -0,0 +1,159 @@
+// Copyright (c) The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or https://opensource.org/license/mit/.
+
+use std::fs::File;
+use std::io::{Read, Seek, SeekFrom};
+
+use crate::util::{check_output, commit_range, get_pathspecs_default_excludes, git, LintResult};
+
+/// Return the pathspecs for whitespace related excludes
+fn get_pathspecs_exclude_whitespace() -> Vec<String> {
+ let mut list = get_pathspecs_default_excludes();
+ list.extend(
+ [
+ // Permanent excludes
+ "*.patch",
+ "src/qt/locale",
+ "contrib/windeploy/win-codesign.cert",
+ "doc/README_windows.txt",
+ // Temporary excludes, or existing violations
+ "contrib/init/bitcoind.openrc",
+ "contrib/macdeploy/macdeployqtplus",
+ "src/crypto/sha256_sse4.cpp",
+ "src/qt/res/src/*.svg",
+ "test/functional/test_framework/crypto/ellswift_decode_test_vectors.csv",
+ "test/functional/test_framework/crypto/xswiftec_inv_test_vectors.csv",
+ "contrib/qos/tc.sh",
+ "contrib/verify-commits/gpg.sh",
+ "src/univalue/include/univalue_escapes.h",
+ "src/univalue/test/object.cpp",
+ "test/lint/git-subtree-check.sh",
+ ]
+ .iter()
+ .map(|s| format!(":(exclude){s}")),
+ );
+ list
+}
+
+pub fn lint_trailing_whitespace() -> LintResult {
+ let trailing_space = git()
+ .args(["grep", "-I", "--line-number", "\\s$", "--"])
+ .args(get_pathspecs_exclude_whitespace())
+ .status()
+ .expect("command error")
+ .success();
+ if trailing_space {
+ Err(r#"
+Trailing whitespace (including Windows line endings [CR LF]) is problematic, because git may warn
+about it, or editors may remove it by default, forcing developers in the future to either undo the
+changes manually or spend time on review.
+
+Thus, it is best to remove the trailing space now.
+
+Please add any false positives, such as subtrees, Windows-related files, patch files, or externally
+sourced files to the exclude list.
+ "#
+ .trim()
+ .to_string())
+ } else {
+ Ok(())
+ }
+}
+
+pub fn lint_trailing_newline() -> LintResult {
+ let files = check_output(
+ git()
+ .args([
+ "ls-files", "--", "*.py", "*.cpp", "*.h", "*.md", "*.rs", "*.sh", "*.cmake",
+ ])
+ .args(get_pathspecs_default_excludes()),
+ )?;
+ let mut missing_newline = false;
+ for path in files.lines() {
+ let mut file = File::open(path).expect("must be able to open file");
+ if file.seek(SeekFrom::End(-1)).is_err() {
+ continue; // Allow fully empty files
+ }
+ let mut buffer = [0u8; 1];
+ file.read_exact(&mut buffer)
+ .expect("must be able to read the last byte");
+ if buffer[0] != b'\n' {
+ missing_newline = true;
+ println!("{path}");
+ }
+ }
+ if missing_newline {
+ Err(r#"
+A trailing newline is required, because git may warn about it missing. Also, it can make diffs
+verbose and can break git blame after appending lines.
+
+Thus, it is best to add the trailing newline now.
+
+Please add any false positives to the exclude list.
+ "#
+ .trim()
+ .to_string())
+ } else {
+ Ok(())
+ }
+}
+
+pub fn lint_tabs_whitespace() -> LintResult {
+ let tabs = git()
+ .args(["grep", "-I", "--line-number", "--perl-regexp", "^\\t", "--"])
+ .args(["*.cpp", "*.h", "*.md", "*.py", "*.sh"])
+ .args(get_pathspecs_exclude_whitespace())
+ .status()
+ .expect("command error")
+ .success();
+ if tabs {
+ Err(r#"
+Use of tabs in this codebase is problematic, because existing code uses spaces and tabs will cause
+display issues and conflict with editor settings.
+
+Please remove the tabs.
+
+Please add any false positives, such as subtrees, or externally sourced files to the exclude list.
+ "#
+ .trim()
+ .to_string())
+ } else {
+ Ok(())
+ }
+}
+
+pub fn lint_commit_msg() -> LintResult {
+ let mut good = true;
+ let commit_hashes = check_output(git().args([
+ "-c",
+ "log.showSignature=false",
+ "log",
+ &commit_range(),
+ "--format=%H",
+ ]))?;
+ for hash in commit_hashes.lines() {
+ let commit_info = check_output(git().args([
+ "-c",
+ "log.showSignature=false",
+ "log",
+ "--format=%B",
+ "-n",
+ "1",
+ hash,
+ ]))?;
+ if let Some(line) = commit_info.lines().nth(1) {
+ if !line.is_empty() {
+ println!(
+ "The subject line of commit hash {hash} is followed by a non-empty line. Subject lines should always be followed by a blank line."
+ );
+ good = false;
+ }
+ }
+ }
+ if good {
+ Ok(())
+ } else {
+ Err("".to_string())
+ }
+}
diff --git a/test/lint/test_runner/src/main.rs b/test/lint/test_runner/src/main.rs
index 3a870b94..ddcdd178 100644
--- a/test/lint/test_runner/src/main.rs
+++ b/test/lint/test_runner/src/main.rs
@@ -2,13 +2,17 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://opensource.org/license/mit/.
+mod lint_text_format;
mod util;
use std::env;
-use std::fs::{self, File};
-use std::io::{ErrorKind, Read, Seek, SeekFrom};
+use std::fs;
+use std::io::ErrorKind;
use std::process::{Command, ExitCode, Stdio};
+use lint_text_format::{
+ lint_commit_msg, lint_tabs_whitespace, lint_trailing_newline, lint_trailing_whitespace,
+};
use util::{
check_output, commit_range, get_git_root, get_pathspecs_default_excludes, get_subtrees, git,
LintFn, LintResult,
@@ -187,41 +191,6 @@ fn lint_scripted_diff() -> LintResult {
}
}
-fn lint_commit_msg() -> LintResult {
- let mut good = true;
- let commit_hashes = check_output(git().args([
- "-c",
- "log.showSignature=false",
- "log",
- &commit_range(),
- "--format=%H",
- ]))?;
- for hash in commit_hashes.lines() {
- let commit_info = check_output(git().args([
- "-c",
- "log.showSignature=false",
- "log",
- "--format=%B",
- "-n",
- "1",
- hash,
- ]))?;
- if let Some(line) = commit_info.lines().nth(1) {
- if !line.is_empty() {
- println!(
- "The subject line of commit hash {hash} is followed by a non-empty line. Subject lines should always be followed by a blank line."
- );
- good = false;
- }
- }
- }
- if good {
- Ok(())
- } else {
- Err("".to_string())
- }
-}
-
fn lint_py_lint() -> LintResult {
let bin_name = "ruff";
let checks = format!(
@@ -397,122 +366,6 @@ expected to follow the naming "/doc/release-notes-<PR number>.md".
}
}
-/// Return the pathspecs for whitespace related excludes
-fn get_pathspecs_exclude_whitespace() -> Vec<String> {
- let mut list = get_pathspecs_default_excludes();
- list.extend(
- [
- // Permanent excludes
- "*.patch",
- "src/qt/locale",
- "contrib/windeploy/win-codesign.cert",
- "doc/README_windows.txt",
- // Temporary excludes, or existing violations
- "contrib/init/bitcoind.openrc",
- "contrib/macdeploy/macdeployqtplus",
- "src/crypto/sha256_sse4.cpp",
- "src/qt/res/src/*.svg",
- "test/functional/test_framework/crypto/ellswift_decode_test_vectors.csv",
- "test/functional/test_framework/crypto/xswiftec_inv_test_vectors.csv",
- "contrib/qos/tc.sh",
- "contrib/verify-commits/gpg.sh",
- "src/univalue/include/univalue_escapes.h",
- "src/univalue/test/object.cpp",
- "test/lint/git-subtree-check.sh",
- ]
- .iter()
- .map(|s| format!(":(exclude){s}")),
- );
- list
-}
-
-fn lint_trailing_whitespace() -> LintResult {
- let trailing_space = git()
- .args(["grep", "-I", "--line-number", "\\s$", "--"])
- .args(get_pathspecs_exclude_whitespace())
- .status()
- .expect("command error")
- .success();
- if trailing_space {
- Err(r#"
-Trailing whitespace (including Windows line endings [CR LF]) is problematic, because git may warn
-about it, or editors may remove it by default, forcing developers in the future to either undo the
-changes manually or spend time on review.
-
-Thus, it is best to remove the trailing space now.
-
-Please add any false positives, such as subtrees, Windows-related files, patch files, or externally
-sourced files to the exclude list.
- "#
- .trim()
- .to_string())
- } else {
- Ok(())
- }
-}
-
-fn lint_trailing_newline() -> LintResult {
- let files = check_output(
- git()
- .args([
- "ls-files", "--", "*.py", "*.cpp", "*.h", "*.md", "*.rs", "*.sh", "*.cmake",
- ])
- .args(get_pathspecs_default_excludes()),
- )?;
- let mut missing_newline = false;
- for path in files.lines() {
- let mut file = File::open(path).expect("must be able to open file");
- if file.seek(SeekFrom::End(-1)).is_err() {
- continue; // Allow fully empty files
- }
- let mut buffer = [0u8; 1];
- file.read_exact(&mut buffer)
- .expect("must be able to read the last byte");
- if buffer[0] != b'\n' {
- missing_newline = true;
- println!("{path}");
- }
- }
- if missing_newline {
- Err(r#"
-A trailing newline is required, because git may warn about it missing. Also, it can make diffs
-verbose and can break git blame after appending lines.
-
-Thus, it is best to add the trailing newline now.
-
-Please add any false positives to the exclude list.
- "#
- .trim()
- .to_string())
- } else {
- Ok(())
- }
-}
-
-fn lint_tabs_whitespace() -> LintResult {
- let tabs = git()
- .args(["grep", "-I", "--line-number", "--perl-regexp", "^\\t", "--"])
- .args(["*.cpp", "*.h", "*.md", "*.py", "*.sh"])
- .args(get_pathspecs_exclude_whitespace())
- .status()
- .expect("command error")
- .success();
- if tabs {
- Err(r#"
-Use of tabs in this codebase is problematic, because existing code uses spaces and tabs will cause
-display issues and conflict with editor settings.
-
-Please remove the tabs.
-
-Please add any false positives, such as subtrees, or externally sourced files to the exclude list.
- "#
- .trim()
- .to_string())
- } else {
- Ok(())
- }
-}
-
fn lint_includes_build_config() -> LintResult {
let config_path = "./cmake/bitcoin-build-config.h.in";
let defines_regex = format!(
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.