lint_text_format.rs
1 // Copyright (c) The Bitcoin Core developers 2 // Distributed under the MIT software license, see the accompanying 3 // file COPYING or https://opensource.org/license/mit/. 4 5 use std::fs::File; 6 use std::io::{Read, Seek, SeekFrom}; 7 8 use crate::util::{check_output, commit_range, get_pathspecs_default_excludes, git, LintResult}; 9 10 /// Return the pathspecs for whitespace related excludes 11 fn get_pathspecs_exclude_whitespace() -> Vec<String> { 12 let mut list = get_pathspecs_default_excludes(); 13 list.extend( 14 [ 15 // Permanent excludes 16 "*.patch", 17 "src/qt/locale", 18 "contrib/windeploy/win-codesign.cert", 19 "doc/README_windows.txt", 20 // Temporary excludes, or existing violations 21 "src/qt/res/src/*.svg", 22 "test/functional/test_framework/crypto/ellswift_decode_test_vectors.csv", 23 "test/functional/test_framework/crypto/xswiftec_inv_test_vectors.csv", 24 ] 25 .iter() 26 .map(|s| format!(":(exclude){s}")), 27 ); 28 list 29 } 30 31 pub fn lint_trailing_whitespace() -> LintResult { 32 let trailing_space = git() 33 .args(["grep", "-I", "--line-number", "\\s$", "--"]) 34 .args(get_pathspecs_exclude_whitespace()) 35 .status() 36 .expect("command error") 37 .success(); 38 if trailing_space { 39 Err(r#" 40 Trailing whitespace (including Windows line endings [CR LF]) is problematic, because git may warn 41 about it, or editors may remove it by default, forcing developers in the future to either undo the 42 changes manually or spend time on review. 43 44 Thus, it is best to remove the trailing space now. 45 46 Please add any false positives, such as subtrees, Windows-related files, patch files, or externally 47 sourced files to the exclude list. 48 "# 49 .trim() 50 .to_string()) 51 } else { 52 Ok(()) 53 } 54 } 55 56 pub fn lint_trailing_newline() -> LintResult { 57 let files = check_output( 58 git() 59 .args([ 60 "ls-files", "--", "*.py", "*.cpp", "*.h", "*.md", "*.rs", "*.sh", "*.cmake", 61 ]) 62 .args(get_pathspecs_default_excludes()), 63 )?; 64 let mut missing_newline = false; 65 for path in files.lines() { 66 let mut file = File::open(path).expect("must be able to open file"); 67 if file.seek(SeekFrom::End(-1)).is_err() { 68 continue; // Allow fully empty files 69 } 70 let mut buffer = [0u8; 1]; 71 file.read_exact(&mut buffer) 72 .expect("must be able to read the last byte"); 73 if buffer[0] != b'\n' { 74 missing_newline = true; 75 println!("{path}"); 76 } 77 } 78 if missing_newline { 79 Err(r#" 80 A trailing newline is required, because git may warn about it missing. Also, it can make diffs 81 verbose and can break git blame after appending lines. 82 83 Thus, it is best to add the trailing newline now. 84 85 Please add any false positives to the exclude list. 86 "# 87 .trim() 88 .to_string()) 89 } else { 90 Ok(()) 91 } 92 } 93 94 pub fn lint_tabs_whitespace() -> LintResult { 95 let tabs = git() 96 .args(["grep", "-I", "--line-number", "--perl-regexp", "^\\t", "--"]) 97 .args(["*.cpp", "*.h", "*.md", "*.py", "*.sh"]) 98 .args(get_pathspecs_exclude_whitespace()) 99 .status() 100 .expect("command error") 101 .success(); 102 if tabs { 103 Err(r#" 104 Use of tabs in this codebase is problematic, because existing code uses spaces and tabs will cause 105 display issues and conflict with editor settings. 106 107 Please remove the tabs. 108 109 Please add any false positives, such as subtrees, or externally sourced files to the exclude list. 110 "# 111 .trim() 112 .to_string()) 113 } else { 114 Ok(()) 115 } 116 } 117 118 pub fn lint_commit_msg() -> LintResult { 119 let mut good = true; 120 let commit_hashes = check_output(git().args([ 121 "-c", 122 "log.showSignature=false", 123 "log", 124 &commit_range(), 125 "--format=%H", 126 ]))?; 127 for hash in commit_hashes.lines() { 128 let commit_info = check_output(git().args([ 129 "-c", 130 "log.showSignature=false", 131 "log", 132 "--format=%B", 133 "-n", 134 "1", 135 hash, 136 ]))?; 137 if let Some(line) = commit_info.lines().nth(1) { 138 if !line.is_empty() { 139 println!( 140 "The subject line of commit hash {hash} is followed by a non-empty line. Subject lines should always be followed by a blank line." 141 ); 142 good = false; 143 } 144 } 145 } 146 if good { 147 Ok(()) 148 } else { 149 Err("".to_string()) 150 } 151 }