lint_cpp.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::process::Command; 6 7 use crate::util::{check_output, get_pathspecs_default_excludes, git, LintResult}; 8 9 pub fn lint_includes_build_config() -> LintResult { 10 let config_path = "./cmake/bitcoin-build-config.h.in"; 11 let defines_regex = format!( 12 r"^\s*(?!//).*({})", 13 check_output(Command::new("grep").args(["define", "--", config_path])) 14 .expect("grep failed") 15 .lines() 16 .map(|line| { 17 line.split_whitespace() 18 .nth(1) 19 .unwrap_or_else(|| panic!("Could not extract name in line: {line}")) 20 }) 21 .collect::<Vec<_>>() 22 .join("|") 23 ); 24 let print_affected_files = |mode: bool| { 25 // * mode==true: Print files which use the define, but lack the include 26 // * mode==false: Print files which lack the define, but use the include 27 let defines_files = check_output( 28 git() 29 .args([ 30 "grep", 31 "--perl-regexp", 32 if mode { 33 "--files-with-matches" 34 } else { 35 "--files-without-match" 36 }, 37 &defines_regex, 38 "--", 39 "*.cpp", 40 "*.h", 41 ]) 42 .args(get_pathspecs_default_excludes()), 43 ) 44 .expect("grep failed"); 45 git() 46 .args([ 47 "grep", 48 if mode { 49 "--files-without-match" 50 } else { 51 "--files-with-matches" 52 }, 53 if mode { 54 "^#include <bitcoin-build-config.h> // IWYU pragma: keep$" 55 } else { 56 "#include <bitcoin-build-config.h>" // Catch redundant includes with and without the IWYU pragma 57 }, 58 "--", 59 ]) 60 .args(defines_files.lines()) 61 .status() 62 .expect("command error") 63 .success() 64 }; 65 let missing = print_affected_files(true); 66 if missing { 67 return Err(format!( 68 r#" 69 One or more files use a symbol declared in the bitcoin-build-config.h header. However, they are not 70 including the header. This is problematic, because the header may or may not be indirectly 71 included. If the indirect include were to be intentionally or accidentally removed, the build could 72 still succeed, but silently be buggy. For example, a slower fallback algorithm could be picked, 73 even though bitcoin-build-config.h indicates that a faster feature is available and should be used. 74 75 If you are unsure which symbol is used, you can find it with this command: 76 git grep --perl-regexp '{defines_regex}' -- file_name 77 78 Make sure to include it with the IWYU pragma. Otherwise, IWYU may falsely instruct to remove the 79 include again. 80 81 #include <bitcoin-build-config.h> // IWYU pragma: keep 82 "# 83 ) 84 .trim() 85 .to_string()); 86 } 87 let redundant = print_affected_files(false); 88 if redundant { 89 return Err(r#" 90 None of the files use a symbol declared in the bitcoin-build-config.h header. However, they are including 91 the header. Consider removing the unused include. 92 "# 93 .to_string()); 94 } 95 Ok(()) 96 } 97 98 pub fn lint_std_filesystem() -> LintResult { 99 let found = git() 100 .args([ 101 "grep", 102 "--line-number", 103 "std::filesystem", 104 "--", 105 "./src/", 106 ":(exclude)src/ipc/libmultiprocess/", 107 ":(exclude)src/util/fs.h", 108 ":(exclude)src/test/kernel/test_kernel.cpp", 109 ":(exclude)src/bitcoin-chainstate.cpp", 110 ]) 111 .status() 112 .expect("command error") 113 .success(); 114 if found { 115 Err(r#" 116 Direct use of std::filesystem may be dangerous and buggy. Please include <util/fs.h> and use the 117 fs:: namespace, which has unsafe filesystem functions marked as deleted. 118 "# 119 .trim() 120 .to_string()) 121 } else { 122 Ok(()) 123 } 124 } 125 126 pub fn lint_rpc_assert() -> LintResult { 127 let found = git() 128 .args([ 129 "grep", 130 "--line-number", 131 "--extended-regexp", 132 r"\<(A|a)ss(ume|ert)\(", 133 "--", 134 "src/rpc/", 135 "src/wallet/rpc*", 136 ":(exclude)src/rpc/server.cpp", 137 // src/rpc/server.cpp is excluded from this check since it's mostly meta-code. 138 ]) 139 .status() 140 .expect("command error") 141 .success(); 142 if found { 143 Err(r#" 144 CHECK_NONFATAL(condition) or NONFATAL_UNREACHABLE should be used instead of assert for RPC code. 145 146 Aborting the whole process is undesirable for RPC code. So nonfatal 147 checks should be used over assert. See: src/util/check.h 148 "# 149 .trim() 150 .to_string()) 151 } else { 152 Ok(()) 153 } 154 } 155 156 pub fn lint_boost_assert() -> LintResult { 157 let found = git() 158 .args([ 159 "grep", 160 "--line-number", 161 "--extended-regexp", 162 r"BOOST_ASSERT\(", 163 "--", 164 "*.cpp", 165 "*.h", 166 ]) 167 .status() 168 .expect("command error") 169 .success(); 170 if found { 171 Err(r#" 172 BOOST_ASSERT must be replaced with Assert, BOOST_REQUIRE, or BOOST_CHECK to avoid an unnecessary 173 include of the boost/assert.hpp dependency. 174 "# 175 .trim() 176 .to_string()) 177 } else { 178 Ok(()) 179 } 180 }