cpplint 分析筆記 · [前提得看下google規范]
@2022-1-13 20:44:48
error message formate:
[filename] [linenum] [message] [category] [confidence]
cpplint [option]
-
輸出格式
--output=vs7 -
冗長度設置(0-5)
--verbose=# -
靜默輸出
--quiet -
類別過濾器,優先級是從左到右,設置'+FOO'輸出該類別,設置'-FOO'&'FOO'不輸出該類別
--filter= -
錯誤計數報告樣式,total:總數;toplevel:頂級類別;detailed:詳細類別
--counting=total|toplevel|detailed -
header防重包含所用變量名的參考配置,詳見源碼--root'Examples'
--root=subdir -
行長度設置
--linelength=120 -
擴展文件類型'.c',這樣指定只會識別.c文件,非.c文件均不識別,為了識別多種類型文件在后綴表中添加最方便,
但這種指定的應用場景是只處理指定格式的文件,本質還是該選項會覆蓋默認的后綴表中的內容
--extensions=c -
擴展headers類型
--headers=hpp -
忽略文件,支持正則表達式
--exclude_files=regex
cpplint支持逐級目錄都有不同的選項配置,配置文件是<CPPLINT.cfg>
- 父級配置影響子級,排除檢查目錄或文件可通過該配置文件搞事情
- 搞事情:排除檢查目錄Dir,在Dir父目錄創建配置文件,寫入屬性exclude_files=Dir
- key=value pairs
set noparent -- 不再向上查找配置文件
filter=+filter1,-filter2,...
exclude_files=regex
linelength=80
root=subdir
headers=x,y,...
檢查類別解釋
做實例驗證以分析錯誤類型意義
_ERROR_CATEGORIES = [
'build/class', # 編譯類:
'build/c++11',
'build/c++14',
'build/c++tr1',
'build/deprecated', # 廢棄的
'build/endif_comment', # endif后注釋
'build/explicit_make_pair', # 明確的配對使用
'build/forward_decl',
'build/header_guard', # 頭文件缺少防重包含 '#ifn>def'
'build/include',
'build/include_alpha',
'build/include_order', # '#ifn>def'包含順序
'build/include_what_you_use', # 缺少頭文件
'build/namespaces',
'build/printf_format',
'build/storage_class',
'legal/copyright', # 版權信息
'readability/alt_tokens', # 可讀性:
'readability/braces',
'readability/casting',
'readability/check',
'readability/constructors',
'readability/fn_size',
'readability/inheritance', # 繼承
'readability/multiline_comment', # 多行注釋
'readability/multiline_string', # 多行字符串
'readability/namespace',
'readability/nolint',
'readability/nul',
'readability/strings',
'readability/todo',
'readability/utf8',
'runtime/arrays', # 運行時:數組
'runtime/casting', # _cast相關轉換
'runtime/explicit',
'runtime/int',
'runtime/init',
'runtime/invalid_increment', # 無效自增
'runtime/member_string_references',
'runtime/memset', # memset
'runtime/indentation_namespace', # 命名空間-縮進
'runtime/operator', # 操作符
'runtime/printf', # printf
'runtime/printf_format', # printf-格式
'runtime/references', # 引用
'runtime/string', # 字符串
'runtime/threadsafe_fn', # 線程安全函數
'runtime/vlog', # VLOG()函數是否用於設置日志級別
'whitespace/blank_line', # 空白:空行
'whitespace/braces', # 大括號
'whitespace/comma', # 逗號
'whitespace/comments', # 注釋
'whitespace/empty_conditional_body', # 空條件,如if()
'whitespace/empty_if_body', # 空的if語句
'whitespace/empty_loop_body', # 空循環體
'whitespace/end_of_line', # 行末
'whitespace/ending_newline', # 文末新行
'whitespace/forcolon', # 冒號
'whitespace/indent', # 縮進
'whitespace/line_length', # 行長度
'whitespace/newline', # 新行
'whitespace/operators', # 操作符
'whitespace/parens', # 小括號
'whitespace/semicolon', # 分號
'whitespace/tab', # TAB
'whitespace/todo', # TODO
]
新增檢查類別
- 直接加入上表中即可
- 使用處:
- 抑制檢查,在上表中的類別都受管控
ParseNolintSuppressions()
- 類別信息輸出
PrintCategories() <- ParseArguments(args)
默認過類別濾器
- 默認是檢查所有類別的,所以只在這添加要關閉的類別即可,當然默認類別會被
'--filter= flag
'覆蓋
_DEFAULT_FILTERS = ['-build/include_alpha']
支持C非C++的默認類別列表
_DEFAULT_C_SUPPRESSED_CATEGORIES = [
'readability/casting',
]
支持linux-kernel的默認類別列表
_DEFAULT_KERNEL_SUPPRESSED_CATEGORIES = [
'whitespace/tab',
]
C++ headers
- 解釋 ?
_CPP_HEADERS = frozenset([
])
合法的類型名
_TYPES = re.compile(
)
類別'[build/include] and [build/include_order]'
之外的headers檢查類別
- 不遵守google文件名命名規范的,如帶有大寫字母'Headers.h'
- Lua 相關的headers
_THIRD_PARTY_HEADERS_PATTERN = re.compile(
r'^(?:[^/]*[A-Z][^/]*\.h|lua\.h|lauxlib\.h|lualib\.h)$')
針對測試文件名的匹配模式
- 后綴樣式
_TEST_FILE_SUFFIX = r'(_test|_unittest|_regtest)$'
只匹配完整的空白模式,可能涉及多行
_EMPTY_CONDITIONAL_BODY_PATTERN = re.compile(r'^\s*$', re.DOTALL)
檢查宏
_CHECK_MACROS = [
'DCHECK', 'CHECK',
'EXPECT_TRUE', 'ASSERT_TRUE',
'EXPECT_FALSE', 'ASSERT_FALSE',
]
宏替換
_CHECK_REPLACEMENT = dict([(m, {}) for m in _CHECK_MACROS])
運算符替代
_ALT_TOKEN_REPLACEMENT = {
'and': '&&',
'bitor': '|',
'or': '||',
'xor': '^',
'compl': '~',
'bitand': '&',
'and_eq': '&=',
'or_eq': '|=',
'xor_eq': '^=',
'not': '!',
'not_eq': '!='
}
Type Constants,用於檢查headers order是否正確
_C_SYS_HEADER = 1
_CPP_SYS_HEADER = 2
_LIKELY_MY_HEADER = 3 ` header this file implements
_POSSIBLE_MY_HEADER = 4 ` header this file may implement
_OTHER_HEADER = 5
標記內嵌匯編代碼
_NO_ASM = 0 ` Outside of inline assembly block
_INSIDE_ASM = 1 ` Inside inline assembly block
_END_ASM = 2 ` Last line of inline assembly block
_BLOCK_ASM = 3 ` The whole block is an inline assembly block
匹配匯編代碼
_MATCH_ASM = re.compile(r'^\s*(?:asm|_asm|__asm|__asm__)'
r'(?:\s+(volatile|__volatile__))?'
r'\s*[{(]')
匹配字符串以標識是C文件非C++文件
_SEARCH_C_FILE = re.compile(r'\b(?:LINT_C_FILE|'
r'vim?:\s*.*(\s*|:)filetype=c(\s*|:|$))')
匹配字符串以標識是linux-kernel文件
_SEARCH_KERNEL_FILE = re.compile(r'\b(?:LINT_KERNEL_FILE)')
默認行長度
_line_length = 80
默認的文件后綴名,加入'c'
_valid_extensions = set(['cc', 'h', 'cpp', 'cu', 'cuh', 'c'])
默認的頭文件格式,以.h
開頭的都認為是.h
_hpp_headers = set(['h'])
函數實現
def ProcessHppHeadersOption(val):
處理頭文件格式
- 以分割符','分割字符串val,若存在帶有逗號的字符串,則更新字符到集合_valid_extensions
- 若不存在則拋異常提示
def IsHeaderExtension(file_extension):
頭文件尾綴檢查
def ParseNolintSuppressions(filename, raw_line, linenum, error):
解析NOLINT,更新錯誤抑制表_error_suppressions
- 抑制方式:
- NOLINT
- NOLINT(*)
- NOLINT(category) # 此方式中的category必須要在錯誤抑制列表中,且會檢查注釋規則(//NOLINT(category))
def ProcessGlobalSuppresions(lines):
解析lint,更新錯誤抑制表_global_error_suppressions
def ResetNolintSuppressions():
將NOLINT抑制集合清空
def IsErrorSuppressedByNolint(category, linenum):
檢查指定類別是否被抑制,是則返回true
def Match(pattern, s):
模式匹配字符串[s]
def ReplaceAll(pattern, rep, s):
模式替換[rep]->[s]
def Search(pattern, s):
模式檢索[s]
def _IsSourceExtension(s):
源文件尾綴判斷
class _IncludeState ->SOT
class _IncludeState(object):
-
追蹤
'include'
出現的行號,以及'include'
的順序 -
'include_list'
是列表[header, line-number]
的列表 -
為文件中的每個
header
執行一次'CheckNextIncludeOrder()'
,傳入上面定義的Type Constants
參數,順序非法即與定義不否,則生成錯誤信息 -
一個關於
'_IncludeError'
相關的錯誤信息 -
section set order:
Section Order Value _INITIAL_SECTION 0 _MY_H_SECTION 1 _C_SECTION 2 _CPP_SECTION 3 _OTHER_H_SECTION 4
def init(self):
初始化'include'列表和'section'
def FindHeader(self, header):
檢查header是否已經包含了
- 包含header則返回前一次出現的行號,否則返回-1
def ResetSection(self, directive):
重置預處理器指令的section check
- 更新include-list
- [directive]: 'if', 'if>def', 'ifn>def', 'else', 'elif'
def SetLastHeader(self, header_path):
最后找到header的路徑
def CanonicalizeAlphabeticalOrder(self, header_path):
按小寫字母序規范化header-path
'-'
=>'_'
, 刪除'-inl'
def IsInAlphabeticalOrder(self, clean_lines, linenum, header_path):
- 檢查header與前一個header是否按字母序
def CheckNextIncludeOrder(self, header_type):
- 檢查下一個header是否按section order序
- header以Type Constants分類,以section set order排序
- 非法序則輸出錯誤信息
->EOT // end of type
class _CppLintState ->SOT
class _CppLintState(object):
- 保持整個模塊的狀態
def init(self):
初始化lint的全局配置
- 置信度設置為 1
- 錯誤數設置為 0
- 報告錯誤的類別過濾器設置為默認的過濾器
- 備份過濾器,用於處理每個文件時恢復狀態
- 報錯數方式為 total
- 字符串化錯誤數到 int-dictionary,按類別分錯
- 取消靜默輸出錯誤信息
- 輸出錯誤信息的格式設置為 emacs 可解析
def SetOutputFormat(self, output_format):
設置輸出錯誤信息格式
def SetQuiet(self, quiet):
設置是否靜默輸出
- 返回的是上一次的設置
def SetVerboseLevel(self, level):
設置冗余等級
- 返回的是上一次的設置
def SetCountingStyle(self, counting_style):
設置報錯數的方式
def SetFilters(self, filters):
設置錯誤信息過濾器
- 過濾類別必須以'+'或'-'開頭,否則拋異常
- 默認過濾器的優先級小於 --filter= 設置的
def AddFilters(self, filters):
增加過濾類別
- 逗號分隔類別
- 類別必須以'+'或'-'開頭否則拋異常
- strip(): 刪除頭尾指定字符,默認是空格和換行符
def BackupFilters(self):
備份過濾器
def RestoreFilters(self):
恢復備份的過濾器
def ResetErrorCounts(self):
重置錯誤計數
def IncrementErrorCount(self, category):
增加類別的錯誤計數
def PrintErrorCounts(self):
輸出類別的錯誤摘要和總數
_cpplint_state = _CppLintState()
類實例化對象
- 下面方法是類對象的封裝方法
def _OutputFormat():
獲取輸出格式
def _Quiet():
獲取靜默設置
def _Quiet():
設置是否靜默
- 返回先前的配置
def _SetVerboseLevel(level):
獲取冗長度設置
- 返回先前的配置
def _SetCountingStyle(level):
設置錯誤計數模式
def _Filters():
獲取過濾器
def _SetFilters(filters):
設置過濾器
def _AddFilters(filters):
增加過濾器
def _BackupFilters():
備份過濾器
def _RestoreFilters():
恢復過濾器
-> EOT
class _FunctionState ->SOT
class _FunctionState(object):
- 追蹤函數名和函數體行數
- 函數體行數觸發置信度錯誤
- 正常觸發行數,測試觸發行數
_NORMAL_TRIGGER = 250 # for --v=0, 500 for --v=1, etc.
_TEST_TRIGGER = 400 # about 50% more than _NORMAL_TRIGGER.
def init(self):
初始化
- 默認不在函數中
- 默認函數體行數 0
- 默認行數名為空
def Begin(self, function_name):
開始分析函數體
- 標記在函數中
- 函數體行數 0
- 記錄函數名
def Count(self):
計數函數體行數
def Check(self, error, filename, linenum):
檢查函數體行數是否太多
- 行數超過觸發數(內部計算)則error輸出信息
- 原則是函數體實現小而功能聚焦
def End(self):
停止分析函數體
- 標記不在函數中
->EOT
class FileInfo ->SOT
class FileInfo(object):
- 針對文件名提供工具函數
- 提供了易於訪問相對於項目根路徑的文件路徑
def init(self, filename):
初始化文件名
def FullName(self):
將Windows路徑轉換為Unix路徑
def RepositoryName(self):
刪除倉中檢出的項目的本地路徑
def Split(self):
分割文件為目錄、文件名、擴展名
def BaseName(self):
獲取文件名
def Extension(self):
獲取擴展名
def NoExtension(self):
無擴展名
def IsSource(self):
檢查是否為源文件
->EOT
def _ShouldPrintError(category, confidence, linenum):
檢查是否輸出錯誤信息
- 如果置信度 >= 冗長度,類別通過過濾器不被抑制
- 三種方式可決定不輸出錯誤信息
-
- 'NOLINT'源碼注釋
-
- 冗長度不夠高
-
- 過濾器將其過濾掉
def Error(filename, linenum, category, confidence, message):
錯誤信息輸出
- 記錄了錯誤發生地,及錯誤置信度
- 誤報可以使用"cpplint(category)"注釋誤報行,這樣就會解析為錯誤抑制
- 置信度數越高意味着該錯誤越確定
- 輸出錯誤信息樣式(3種):
-
- 'vs7': filename(linenum): error cpplint:
[category]
message[confidence]
- 'vs7': filename(linenum): error cpplint:
-
- 'eclipse': filename:linenum: warning: message
[category]
[confidence]
- 'eclipse': filename:linenum: warning: message
-
- 'other': filename:linenum: message
[category]
[confidence]
- 'other': filename:linenum: message
C++轉義序列
匹配C風格單行注釋
匹配C風格多行注釋
def IsCppString(line):
c++字符串判斷
def CleanseRawStrings(raw_lines):
刪除C++11原始字符串
Before:
static const char kData[] = R"(
multi-line string
)";
After:
static const char kData[] = ""
(replaced by blank line)
"";
def FindNextMultiLineCommentStart(lines, lineix):
查找多行注釋的起始標記 '/*'
def FindNextMultiLineCommentEnd(lines, lineix):
查找多行注釋的結束標記 '*/'
def RemoveMultiLineCommentsFromRange(lines, begin, end):
清除行范圍的多行注釋
def RemoveMultiLineComments(filename, lines, error):
刪除C風格多行注釋
def CleanseComments(line):
刪除注釋 "//" "/**/"
class CleansedLines ->SOT
class CleansedLines(object):
- 保存所有行的4份變種,並進行不同的預處理
-
- elided member:刪除字符串和注釋的行
-
- lines member:刪除注釋的行
-
- raw_lines member:未進行處理的所有行
-
- lines_without_raw_strings member:刪除C++11字符串的行
def init(self, lines):
初始化4份拷貝
def NumLines(self):
返回所表示的行數
def _CollapseStrings(elided):
簡化字符串和字符,簡化為 "" or ''
- 簡化后就不會被像'"http://"'這種字符串迷惑
- 檢查若是header則不處理直接返回
- 首先刪除轉義字符,處理成最基本的引號或單引號的樣式
- 替換引號字符串和數字分隔符,單引號和雙引號在同一循環中處理,否則嵌套的引號將無法工作
->EOT
def FindEndOfExpressionInLine(line, startpos, stack):
查找當前括號中表達式結束的位置
def CloseExpression(clean_lines, linenum, pos):
查找表達式的結束位置
- 如果輸入點是
'('
or'{'
or'['
or'<'
則找出相應的結束位置
def FindStartOfExpressionInLine(line, endpos, stack):
查找當前表達式開始的位置
def ReverseCloseExpression(clean_lines, linenum, pos):
查找表達式的開始位置
- 如果輸入點是
')'
or'}'
or']'
or'>'
則找出相應的開始位置
def CheckForCopyright(filename, lines, error):
檢查版權信息,在文件頂部
- 使用關鍵字'Copyright'匹配,查找范圍前10行
def GetIndentLevel(line):
獲取行前導空格的數量
def PathSplitToList(path):
分割路徑成列表
- 如
'/a/b/c/
' ->['a', 'b', 'c]
def GetHeaderGuardCPPVariable(filename):
獲取保護header的C++變量
- 從filename(c++ header file)中找出保護header的c++變量
def CheckForHeaderGuard(filename, clean_lines, error):
檢查文件是否包含header保護
- 檢查頭文件是否使用了'#ifn>def'以做保護
def CheckHeaderFileIncluded(filename, include_state, error):
檢查文件是否包含自己的header
def CheckForBadCharacters(filename, lines, error):
檢查行中是否包含壞字符(字符編碼問題)
- unicode替換字符
- NUL bytes
def CheckForNewlineAtEOF(filename, lines, error):
文件底部檢查是否有新行
- 因處理文件時,前后各加了一行輔助信息,故查行數<3行或倒數第2行不為空,則認定為沒有新行
def CheckForMultilineCommentsAndStrings(filename, clean_lines, linenum, error):
檢查多行注釋和字符串
- 注釋風格可在此接口中據需求而修改
"/* */"
需配對使用,'/*'
不可多於'*/'
- 字符串標記符[""]檢查
def CheckPosixThreading(filename, clean_lines, linenum, error):
檢查線程不安全函數的調用情況
def CheckVlogArguments(filename, clean_lines, linenum, error):
檢查VLOG()是否只用於定義日志級別
- VLOG(2)是正確的,LOG(INFO), VLOG(WARNING), VLOG(ERROR), and VLOG(FATAL) 是錯誤的
def CheckInvalidIncrement(filename, clean_lines, linenum, error):
檢查自增是否無效
def IsMacroDefinition(clean_lines, linenum):
檢查是否是宏定義
def IsForwardClassDeclaration(clean_lines, linenum):
是否是前置類聲明
class _BlockInfo ->SOT
class _BlockInfo(object):
- 存儲通用代碼塊信息
def init(self, linenum, seen_open_brace):
代碼塊信息初始化
- 設置起始行號
- 大括號起始設置
- 小括號起始設置為 0
- 內聯匯編設置為 無
- 檢查命名空間縮進設置為 否
def IsBlockInfo(self):
檢查是否是塊信息
->EOT
class _ExternCInfo ->SOT
class _ExternCInfo(_BlockInfo):
- 存儲
'extern "C"'
塊信息
def init(self, linenum):
調用代碼塊信息初始化
->EOT
class _ClassInfo ->SOT
class _ClassInfo(_BlockInfo):
- 存儲類信息
def init(self, name, class_or_struct, clean_lines, linenum):
類信息初始化
- 調用代碼塊信息初始化
- 類名設置
- 派生類設置為 否
- 檢查命名空間縮進設置為 是
- 判斷是類還是結構:
- 結構:訪問權限設置為 public,標記結構為 是
- 類:訪問權限設置為 private,標記結構為 否
- 類初始縮進級別設置
- 最后行設置為 0
def CheckBegin(self, filename, clean_lines, linenum, error):
檢查類開始
def CheckEnd(self, filename, clean_lines, linenum, error):
檢查類結束
->EOT
class _NamespaceInfo ->SOT
class _NamespaceInfo(_BlockInfo):
- 存儲命名空間信息
def init(self, name, linenum):
命名空間初始化配置
- 代碼塊信息初始化
- 命名空間名字設置
- 檢查命名空間縮進設置為 是
def CheckEnd(self, filename, clean_lines, linenum, error):
檢查命名空間注釋結束
->EOT
class _PreprocessorInfo ->SOT
class _PreprocessorInfo(object):
- 遇見"#if/#else"存儲嵌套堆棧的查看點
def init(self, stack_before_if):
->EOT
class NestingState ->SOT
class NestingState(object):
- 保存與解析大括號相關的狀態
def init(self):
- stack:用於跟蹤所有大括號的堆棧,遇見'{'入棧,遇見'}'出棧,主要由3類對象:類或結構,命名空間,塊
- previous_stack_top:之前的棧頂
- pp_stack:預處理器信息的棧
def SeenOpenBrace(self):
查找最內層的大括號
def InNamespaceBody(self):
檢查是否處於命名空間這一級別
def InExternC(self):
檢查是否處於extern "C"這一級別
def InClassDeclaration(self):
檢查是否處於類或結構聲明這一級別
def InAsmBlock(self):
檢查是否處於asm塊這一級別
def InTemplateArgumentList(self, clean_lines, linenum, pos):
檢查是否屬於模板參數列表這一級別
def UpdatePreprocessor(self, line):
更新預處理器信息棧
def Update(self, filename, clean_lines, linenum, error):
更新當前行的嵌套狀態
def InnermostClass(self):
獲取頂級棧的類信息
def CheckCompletedBlocks(self, filename, error):
檢查所有類和命名空間是否解析完畢
->EOT
def CheckForNonStandardConstructs(filename, clean_lines, linenum, nesting_state, error):
檢查是否符合標准結構
- 符合gcc-2要求,但不是c++標准,non-ANSI
def CheckSpacingForFunctionCall(filename, clean_lines, linenum, error):
檢查函數調用周圍空格是否合規
- 函數調用通常在 if/for/while/switch 中
- 除了在 if/for/while/switch 中,其他的括號兩邊不允許出現空格
def IsBlankLine(line):
判斷空行
- 只有空格也算空行
def CheckForNamespaceIndentation(filename, nesting_state, clean_lines, line, error):
檢查命名空間縮進
def CheckForFunctionLengths(filename, clean_lines, linenum, function_state, error):
檢查函數體長度
- 只檢查未縮進的函數,如類成員函數不檢查
- 帶有很多初始化列表的構造函數不檢查
- 空行和注釋行不計入在行數統計
- 函數最后一行有 'NOLINT' 則不檢查
def CheckComment(line, filename, linenum, next_line_start, error):
檢查注釋中的常見錯誤,注釋符'//'
- 注釋符
'//'
后要跟1個空格 - 代碼行尾注釋需留2個空格
- TODO相關的空格
def CheckSpacing(filename, clean_lines, linenum, nesting_state, error):
檢查代碼中間距的正確性
- 運算符周圍的空格
- 函數調用括號周圍無空格
- 代碼塊起始位置的冗余空行應該刪除
- 代碼塊結束位置的冗余空行應該刪除
- public/protected/private 后不要加空行
- 注釋空格檢查
'['
前不許有空格- for循環中基於范圍的冒號周圍需要空格
- 不要有太多空行
- 命名空間主體中不檢查空行
- 不檢查 extern "C" 主體中的空行
def CheckOperatorSpacing(filename, clean_lines, linenum, error):
檢查操作符周圍的間距
- 允許if條件中
'='
兩側無空格 - 比較運算符
'=='
,'!='
,'<='
,'>='
兩側須有空格 - 比較運算符
'<'
,'>'
兩側須有空格 - 移位操作符
'<<'
,'>>'
兩側須有空格 - 一元運算符兩側不能有空格
def CheckParenthesisSpacing(filename, clean_lines, linenum, error):
檢查括號周圍的間距
- if/for/while/switch 后與括號間的空格
- if/for/while/switch 后括號內緊挨的空格要匹配
def CheckCommaSpacing(filename, clean_lines, linenum, error):
檢查逗號和分號附近的間距
- 逗號后缺空格
- 分號后缺空格
def _IsType(clean_lines, nesting_state, expr):
判斷表達式是否是類型名
def CheckBracesSpacing(filename, clean_lines, linenum, nesting_state, error):
檢查花括號附近的間距
'{'
前要有1個空格'}'
與'else'
間要有1各位空格- 分號所在的空語句必須使用花括號
def IsDecltype(clean_lines, linenum, column):
def CheckSectionSpacing(filename, clean_lines, class_info, linenum, error):
檢查與section相關的間距
- 當public/protected/private之前須有1個空行
- 若類實現行數少於25(終端的通常高度)行,認為small class,則不檢查
def GetPreviousNonBlankLine(clean_lines, linenum):
獲取摸最近的非空行及其行號
def CheckBraces(filename, clean_lines, linenum, error):
查找錯位的花括號
- 樣式:
'else {'
'{'
應該在上一代碼行尾'else'
應該在上一行'}'
后邊'else'
兩側都應有花括號- 同一行之只能有一個
'else'
子句 'do while'
不許在同一行'if/else'
多行語句需要使用花括號
def CheckTrailingSemicolon(filename, clean_lines, linenum, error):
查找尾部冗余的分號
- 具體示例詳見函數說明
- 塊體后不應該出現分號
def CheckEmptyBlockBody(filename, clean_lines, linenum, error):
查找只有一個分號的空循環體和條件體
- for/while/if (exp);
- for/while/if (exp) {
} - 空循環體和空條件體應使用
'{}'
'if'
沒有body、沒有else子句,報錯
def FindCheckMacro(line):
查找'CHECK'宏
def CheckCheck(filename, clean_lines, linenum, error):
查找'CHECK'和'EXPECT'宏
def CheckAltTokens(filename, clean_lines, linenum, error):
檢查布爾表達式中使用的備選關鍵字
- 詳見列表_ALT_TOKEN_REPLACEMENT
- 如'and' -> '&&'
def GetLineWidth(line):
獲取行長度
def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state, error):
檢查規則 from 'C++ style rules' section of cppguide.html
- 如縮進2空格,行長度,制表符,代碼內空格等
- 檢查使用的行是CleansedLines->lines_without_raw_strings
- 檢查是否使用了TAB
【LINT-縮進TAB】
- 檢查縮進空格數
【LINT-縮進空格】
- 檢查行尾空格
【LINT-行尾空格】
- 檢查header防重包含
- 檢查行長度
【LINT-行長度】
- 檢查一行存在多條指令
【LINT-一行多指令】
- 檢查錯位的花括號
【LINT-花括號】
- 檢查尾部的分號
【LINT-塊體冗余分號】
- 檢查空塊
【LINT-空塊體檢查】
- 檢查代碼間距
【LINT-空行空格】
- 檢查操作符周圍的間距
【LINT-運算符周圍的空格】
- 檢查括號周圍的間距
【LINT-括號周圍的空格】
- 檢查逗號和分號附近的間距
【LINT-逗號分號周圍的空格】
- 檢查花括號附近的間距
【LINT-花括號周圍的空格】
- 檢查函數調用周圍的間距
【LINT-函數調用括號周圍空格】
- 檢查'CHECK'和'EXPECT'宏
【LINT-運算符token替換】
- 檢查表達式中是否使用了備選關鍵字
【LINT-運算符token替換】
- 檢查(public|protected|private)前一行是否是空行,除前一行出現(class|struct)
【LINT-C++屬性標記符前一行空行】
- 可以在此添加自定義功能,如4空格縮進
def _DropCommonSuffixes(filename):
刪除常見的后綴名,如['-'/'_']'test.cc', 'regtest.cc', 'unittest.cc', 'inl.h', 'impl.h', 'internal.h'
def _ClassifyInclude(fileinfo, include, is_system):
找出header類別,one of [Type Constants]
def CheckIncludeLine(filename, clean_lines, linenum, include_state, error):
檢查header出現順序
- header包含方式需跟父目錄,如'#include "sub_dir/foo.h"'
【LINT-header需要父目錄】
- 同一header不允許多次包含
- 不許包含源文件
- header出現的順序規則是:
-
- for foo.cc, foo.h (preferred location)
-
- c system files
-
- cpp system files
-
- for foo.cc, foo.h (deprecated location)
-
- other google headers
-
def _GetTextInside(text, start_pattern):
檢索匹配括號中的所有文本
- matching_punctuation = {'(': ')', '{': '}', '[': ']'}
def CheckLanguage(filename, clean_lines, linenum, file_extension, include_state, nesting_state, error):
檢查規則 from 'C++ language rules' section of cppguide.html
- 不檢查空行和注釋
- 檢查'#include'行
【LINT-include相關】
- 匹配條件包含預處理指令加入列表 #if|if>def|ifn>def|elif|else|endif
- 將Windows路徑轉換為Unix路徑
- _cast相關轉換檢查
【LINT-_cast相關】
- static/const相關檢查
【LINT-C++ static/const】
- snprintf相關檢查
【LINT-snprintf相關】
- 檢查數據類型,要求使用<stdint.h>中定義的類型,如int16_t
【LINT-數據類型】
- 一元運算符
'&'
重載是危險的【LINT-'&'重載】
- 檢查if可疑用法 '} if (...) {'
【LINT-'} if'】
- printf輸出的內容需要使用格式符'%'
【LINT-printf(object)】
- memset用法錯誤
【LINT-memset參數順序】
- 命名空間用法錯誤,使用時無需using
【LINT-namespace用法】
- 不允許使用可變長度數組
【LINT-變長數組】
- 檢查頭文件中匿名空間的使用
【LINT-匿名空間】
def CheckGlobalStatic(filename, clean_lines, linenum, error):
檢查不安全的全局或靜態對象
- 'static', 'const'
def CheckPrintf(filename, clean_lines, linenum, error):
檢查'printf'相關的問題
- 使用
'snprintf'
時第2個參數請使用'sizeof()'
'snprintf'
用法,用'snprintf'
替代'sprintf'
- 請使用
'snprintf'
替代'strcpy'
,'strcat'
def IsDerivedFunction(clean_lines, linenum):
檢查當前行是否包含繼承函數
def IsOutOfLineMethodDefinition(clean_lines, linenum):
def IsInitializerList(clean_lines, linenum):
檢查當前行是否在構造函數初始化列表中
def CheckForNonConstReference(filename, clean_lines, linenum, nesting_state, error):
檢查non-const引用
def CheckCasts(filename, clean_lines, linenum, error):
檢查類型轉換
- (static|dynamic|down|reinterpret)_cast使用合法檢查,C語言替代
def CheckCStyleCast(filename, clean_lines, linenum, cast_type, pattern, error):
檢查C-Style類型轉換
- sizeof|alignof|alignas
- ' operator++'/' operator--'
- const|throw|final|override
def ExpectingFunctionArgs(clean_lines, linenum):
檢查是否需要函數類型的參數
def FilesBelongToSameModule(filename_cc, filename_h):
檢查文件是否屬於同一個模塊
- The concept of a 'module' here is a as follows:
- foo.h, foo-inl.h, foo.cc, foo_test.cc and foo_unittest.cc belong to the
- same 'module' if they are in the same directory.
- some/path/public/xyzzy and some/path/internal/xyzzy are also considered
- to belong to the same module here.
def UpdateIncludeState(filename, include_dict, io=codecs):
將新找到的'include'更新"include_dict"
def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error, io=codecs):
檢查代碼是否忘記包含STL相關的header
def CheckMakePairUsesDeduction(filename, clean_lines, linenum, error):
檢查是否導出了make_pair的模板參數
def CheckRedundantVirtual(filename, clean_lines, linenum, error):
檢查行是否包含了冗余的虛函數描述符"Virtual"
def CheckRedundantOverrideOrFinal(filename, clean_lines, linenum, error):
檢查行是否包含了冗余的虛函數描述符"override" or "final"
def IsBlockInNameSpace(nesting_state, is_forward_declaration):
檢查塊是否位於命名空間中
def ShouldCheckNamespaceIndentation(nesting_state, is_namespace_indent_item, raw_lines_no_comments, linenum):
判斷是否進行命名空間的縮進檢查
def CheckItemIndentationInNamespace(filename, raw_lines_no_comments, linenum, error):
檢查命名空間中的縮進問題
- '^\s+'匹配行首一個或多個空格字符
def ProcessLine(filename, file_extension, clean_lines, line, include_state, function_state, nesting_state, error, extra_check_functions=[]):
處理文件中的單行
- 在原始行內容中檢查NOLINT注釋,不進行lint檢查
【LINT-NOLINT】
- 更新嵌套狀態
- 檢查name space縮進
【LINT-namespace縮進】
- 若處在asm塊,則退出檢查該行
- 檢查函數體長度
【LINT-函數體長度】
- 檢查多行注釋及其字符串
- 檢查c++風格規則 ->>
- 檢查c++語言規則 ->>
- 檢查non-const引用
- 檢查非標准結構
- 檢查VLOG()函數是否只用於設置日志級別
- 檢查POSIX在線程安全下的函數調用
- 檢查無效的自增
- 檢查是否導出了make_pair模板參數
- 檢查冗余的函數修飾符'virtual'
- 檢查冗余的函數修飾符'override', 'final'
- 若存在額外的功能檢查則繼續檢查
def FlagCxx11Features(filename, clean_lines, linenum, error):
標記那些只允許在某些地方使用的c++11特性
def FlagCxx14Features(filename, clean_lines, linenum, error):
標記我們限制的c++ 14個特性
def ProcessFileData(filename, file_extension, lines, error, extra_check_functions=[]):
處理文件數據
- 執行lint檢查,並將檢查出的error信息輸出到error函數
- 實例化class _IncludeState,獲取'include'行號和出現順序
- 實例化class _FunctionState,獲取函數名和函數體行數
- 實例化class NestingState,解析花括號
- 重置NOLINT錯誤抑制集合
- 檢查版權信息
【LINT-版權】
- 更新全局錯誤抑制集合_global_error_suppressions
- 刪除多行注釋,'/* ... */' -> '/**/'
- 處理行CleansedLines(),產生4份清理后的變種
- 若檢查的是頭文件,則檢查防重包含預處理宏有無
【LINT-頭文件防重包含】
- 遍歷行,處理行 ->>
- 檢查所有的class和name space被完全解析
【LINT-塊完整性檢查】
- 檢查你使用了卻未include的header,stl標准庫
【LINT-頭文件未包含】
- 若檢查的是源文件('c', 'cc', 'cpp', 'cxx'),則檢查是否包含其自身header
【LINT-包含自身頭文件】
- 檢查原始內容中的壞字符
【LINT-壞字符】
- 檢查文件尾是否有新行
【LINT-文件尾新行】
def ProcessConfigOverrides(filename):
解析配置文件CPPLINT.cfg,更新文件檢查配置選項
- 獲取待檢查文件的絕對路徑(帶文件名)
- 獲取待檢查的文件名和路徑
- 獲取待檢查文件同路徑下的配置文件CPPLINT.cfg
- 遞歸直系目錄結構按規則選項解析配置文件CPPLINT.cfg
def ProcessFile(filename, vlevel, extra_check_functions=[]):
處理單個文件
- 入參:待處理文件,報錯級別,額外檢查功能
- 設置冗余等級,備份過濾器配置
- 緩存之前的錯誤數
- 解析lint配置文件CPPLINT.cfg,當前文件若無需處理則恢復過濾器配置
- 檢查行尾符,['\r', '\n', '\r\n'],規則傾向'\n',統計CRLF和LF的數量
【LINT-行尾符】
- 處理文件數據 ->>
- 當CRLF與LF行尾符在一個文件中同時存在時則告警CRLF所在行
- 輸出文件處理完成信息
def PrintUsage(message):
輸出用法說明
def PrintCategories():
輸出錯誤類別
def ParseArguments(args):
解析命令行參數
- 獲取選項參數和文件名:getopt(args, options[, long_options]) -> opts, args,opts=(option, value)
- 參數選項校驗與配置
- 返回文件名
def main():
處理邏輯入口
- 解析命令輸入參數
- 錯誤編碼修改為'utf8'
- 遍歷文件集合處理文件 ->>
- 輸出錯誤數
檢查項梳理
[新增]
文件名檢查[原有]
文件開頭版權檢查[原有]
header防重包含檢查[原有]
NOLINT檢查[原有]
namespace縮進檢查[原有]
函數體行數檢查(非空非注釋行)[原有]
注釋符檢查'/**/' '//'[原有]
字符串標記符'""'檢查[原有]
風格規則[原有]
語言規則[原有]
非常量引用[原有]
非標准結構[原有]
VLOG()接口使用檢查[原有]
線程安全函數檢查[原有]
指針無效自增檢查[原有]
make_pair模板參數省略檢查[原有]
virtual關鍵字冗余檢查[原有]
override和final關鍵字冗余檢查[原有]
STL相關頭文件未包含檢查[原有]
源文件自包含頭文件檢查[原有]
行內容壞字符(編碼)檢查[原有]
行尾符 LF/CRLF 檢查[原有]
文件結尾新行檢查
檢查項補充或修改
基於華為C/C++編碼規范
- 代碼行縮進均為配置選項的整數倍空格
[DONE]
CheckStyle():
if SpaceIndent() != 0:
if (not Search(r'[",=><] *$', prev) and (initial_spaces % SpaceIndent() != 0) and
not Match(scope_or_label_pattern, cleansed_line) and not (clean_lines.raw_lines[linenum] != line and
Match(r'^\s*""', line))):
error(filename, linenum, 'whitespace/indent', 3,
'Weird number of spaces at line-start. Are you using a %d-space indent?' % SpaceIndent())
- 預處理指令頂格縮進檢查
'#'
[DONE]
CheckPreprocessWhitespace():
if Match(r'^\s+#', line):
error(filename, linenum, 'whitespace/preprocess', 4,
'Preprocessor directives are not allowed to start with Spaces')
- 預處理單空格檢查,如'#include'/'#>define'后只能有1個空格
[DONE]
CheckPreprocessWhitespace(filename, linenum, cleansed_line, error):
# r'(#include|#>define)\s{2,}':#include或#>define后空格數>=2
if Match(r'(#include|#>define)\s{2,}', cleansed_line):
error(filename, linenum, 'whitespace/preprocess', 4,
'Only one space is allowed after a preprocessing instruction')
- 函數體行數檢查
[DONE]
class _FunctionState
_NORMAL_TRIGGER = 50
- 文件名由小寫字母、數字和下划線組成
[DONE]
>def CheckFileName(filename, error):
# 去尾綴獲取文件名,查找'/'解決帶目錄的文件,提取'/'與'.'之間的字符串
file_delete_extension = filename[filename.rfind('/') + 1:filename.rfind('.')]
# print(file_delete_extension)
# r'[^a-z_0-9]:非小寫字母和下划線匹配
if Search(r'[^a-z_0-9]', file_delete_extension):
error(filename, "name", 'build/filename', 4,
'The file name consists of lowercase letters, digits, and underscores')
- 魔鬼數字,
'=|=='
后的'[1-9]|0x--'
[DONE]
- 依據:行尾或上一行有注釋,檢查規則:上行首是
'//'
或'/*'
(不能判斷上行尾是'*/'
,因為這可能是上行代碼行的尾注釋),代碼行尾是'//'
或'/*'
def CheckDevilFigure(filename, cleansed_line, line, prev_line, linenum, error):
# '\d'表示匹配 0-9,有時0被認為是非魔鬼數字,所以明文指定[1-9]
# if Search(r' = \d', cleansed_line):
# '(0x)'只匹配'0x',而非'0'、'x',([1-9]|(0x))目的是與前面匹配連接一起
if Search(r' (=|==)\s*([1-9]|(0x))', cleansed_line):
# Match方法是基於行首開始所以匹配符必須先用'^',多個字符匹配需要使用括號和'|','*'需要轉義
# r'^\s+(/\*|//)':行首開始匹配,多空格,/*和//做匹配符
# r'^.*(/\*|//)':行首開始匹配,.*任意字符,/*和//做匹配符
if not Match(r'^\s+(/\*|//)', prev_line) and not Match(r'^.*(/\*|//)', line):
error(filename, linenum, 'build/devil', 3, 'This is probably using devil figure')
- 不許包含源文件
[DONE]
CheckIncludeLine():
elif (include.endswith(('.cc', '.cpp')) and
- <rule.17>宏定義是大寫字母和下划線組合
[DONE]
- 機制:提取第1和2空格間的內容進行檢查
>def CheckMarcoUppercase(filename, linenum, cleansed_line, error):
# r'^#>define' --> r'\s*#>define' 以解決非頂格書寫的宏定義 [todo]
if Match(r'^#>define', cleansed_line):
# 條件r'\('應該刪除,任何情況宏名都應符合規則 [tofo]
if not Search(r'\(', cleansed_line) and Search(r'[^A-Z_]', cleansed_line.split()[1]):
error(filename, linenum, 'build/macro', 4,
'Macro >definition names must use uppercase letters and underscores')
- 刪除規則包含
header
帶父目錄[DONE]
CheckIncludeLine()
''' @skull.
- 函數定義后的'{'需放置下行首,而非本行尾
[DONE]
CheckBraces()
# '{'獨占在一行時,檢查上一行是否是函數定義所在行
# 該匹配條件來自CheckForFunctionLengths()-->regexp=,可以匹配出函數定義
not Match(r'(\w(\w|::|\*|\&|\s)*)\(', prevline) and
# 在'{'出現的行,檢查是否是函數定義所在行,是則提示'{'需要新起一行
# 找出'{'所在行且非獨占行
if Search(r'{', line) and not Match(r'\s*{\s*$', line):
if Match(r'(\w(\w|::|\*|\&|\s)*)\(', line):
error(filename, linenum, 'whitespace/braces', 4,
'{ should not appear after a function, there should be a new line')
- 加入版本查詢選項,用法 --about
[DONE]
ParseArguments()
(opts, filenames) = getopt.getopt(args, '', ['help', 'output=', 'verbose=',
'counting=',
'filter=',
'root=',
'linelength=',
'extensions=',
'headers=',
'quiet',
'about'])
elif opt == '--about':
print("Version:0.1")
print("Release:2022-02-21")
print("Contact:skull.gu@gmail.com")
sys.exit(0)
- 頭文件防重包含的變量名字檢查,應忽略谷歌規則
[TODO]
- 可通過配置選項--root搞點事情
- 亦可通過--filter過濾
- C強制類型轉換,應忽略谷歌規則使用'_cast()'
[DONE]
CheckCStyleCast()
if CstyleCast() == 1:
error(filename, linenum, 'readability/casting', 4,
'Using C-style cast. Use %s<%s>(...) instead' % (cast_type, match.group(1)))
- 檢查注釋符
'/**/'
是否配對時,應該使用原始代碼(帶注釋)[DONE]
CheckForMultilineCommentsAndStrings()
line = clean_lines.elided[linenum] => line = clean_lines.raw_lines[linenum]
'#endif'
后需跟注釋[DONE]
- 機制:非注釋行中檢索
'#endif'
&& ('//'
||'/**/'
)
CheckComment()
if CommentEndif() == 1:
if Search(r'\s*#endif', line) and not (Search(r'//', line) or Search(r'/\*', line)):
error(filename, linenum, 'whitespace/comments', 4, 'Should be a comment after #endif')
- 鎖定.exe的文件名為lint
[DONE]
main()
# 獲取.exe的文件名,windows路徑轉為unix路徑
print os.path.abspath(sys.argv[0]).replace('\\', '/').split("/")[-1].split(".")[0]
if __file__.split("/")[-1].split(".")[0] != 'lint' or os.path.abspath(sys.argv[0]).replace('\\', '/').split("/")[-1].split(".")[0] != 'lint':
print("Please confirm the file name is 'lint'")
sys.exit(-1)
# 注:__file__:獲取.py文件名,sys.argv[0]:獲取最終的文件形態的所在路徑
- 注釋對齊
[TODO]
- 當前規則是:代碼行后至少空2格'//'空1個進行注釋
- 實現:若檢查代碼與'//'空格數 >2,則進行與下一行注釋的'//'位置進行匹配,一致則符合注釋對齊規則,不一致則error
- 但前提是當前帶注釋的行與下一帶注釋的行同屬一模塊,如均是宏定義、同在函數體內、同一結構體內等
- 預處理指令中井號后不允許有空格
[DONE]
CheckPreprocessWhitespace():
if Match(r'^\s*#\s+', cleansed_line):
error(filename, linenum, 'whitespace/preprocess', 4,
'Disable Spaces after # in preprocessor instructions')
- 宏定義表達式中的
'&'
被認為是取地址符,從而進行轉換檢查[DONE]
[eg]:
#define BLE_ISO_MODE_0_PROTOCOL (BLE_ISO_MODE_0 & BLE_HOST_PRESENT)
Message: Is this a non-const reference? If so, make const or use a pointer: BLE_ISO_MODE_0 & BLE_HOST_PRESENT [runtime/references] [2].
解決:
認定一個前提,取地址表達式中取地址符'&'會與變量無空格連接
基於該前提,進行'&'后有無空格檢查,有:位與,無:取地址
CheckForNonConstReference()
for parameter in re.findall(_RE_PATTERN_REF_PARAM, decls):
if (not Match(_RE_PATTERN_CONST_REF_PARAM, parameter) and
not Match(_RE_PATTERN_REF_STREAM_PARAM, parameter)):
# 檢查'&'后空格
if not Search(r'.&\s+', parameter):
error(filename, linenum, 'runtime/references', 2,
'Is this a non-const reference? '
'If so, make const or use a pointer: ' +
ReplaceAll(' *<', '<', parameter))
- Allman風格檢查
[DONE]
- 實現:
'{'
'}'
獨立占一行且與上一行縮進相同
CheckBraces()
elif CodeStyle() == 2:
- <rule.23>同一行多條代碼語句,應該拆分
[DONE]
- 實現:檢索到'{' 且 '{'后非空 且 非預處理行 且 非數組; 同一行存在>1個';'; 'if|else|for|while|do'后不是以')'或'{'結尾
CheckBraces()
if Search(r'{', line) and not Match(r'.*{\s*$', line) and not Match(r'\s*#', line) and not Match(r'.*=\s*{', line):
error(filename, linenum, 'readability/braces', 4,
'{...} involves code statements should be split into multiple lines ')
# '$'過濾掉'for(;;)'
if Search(r';.*;$', line):
error(filename, linenum, 'readability/braces', 4,
'\';..;\' involves code statements should be split into multiple lines ')
# '\s+'過濾掉'#if'
if Search(r'\s+(if|else|for|while|do)', line) and not Search(r'([\{\)]|(while.*;))$', line):
search = Search(r'\s+(if|else|for|while|do)', line)
error(filename, linenum, 'readability/braces', 4,
'\'%s\' involves code statements should be split into multiple lines ' % search.group(1))
- 當前運算符兩側有無空格不檢測,經分析原實現只針對'\w'([a-zA-Z0-9_])做運算符'='兩側空格檢查
[DONE]
- 實現:運算符左側出現']'或右側出現'{'認為是需要空格
CheckOperatorSpacing()
# search = Search(r'(\](\+|-|\*|/|%|&|\||>|<|>>|<<|=|>=|<=|==|!=|&=|\^=|\|=|\+=|-=|\*=|\/=|\%=|>>=|<<=)|={)', line)
# 因為使用的方法search,所以上述運算符在檢索式會有重復,如'='和'==','>'和'>=',首字符相同的,所以可簡化如下
search = Search(r'(\](\+|-|\*|/|%|&|\||>|<|=|!=|\^=)|={)', line)
if ((Search(r'[\w.]=', line) or
# and not Search(r'(>=|<=|==|!=|&=|\^=|\|=|\+=|\*=|\/=|\%=)', line)
elif search:
error(filename, linenum, 'whitespace/operators', 4, 'Missing spaces around %s' % search.group(1))
- 當前
'{}
','()'
,'[]'
內部有無空格不檢測,且空格是否配對也不檢測[DONE]
def ParsePairSymbol(symbol, line, filename, linenum, error):
- 運算符單空格檢查:
'+ - * / % > < >> << & && | || = == += -= *= /= %= >= <= ~ ! != &= |= ^= >>= <<= -> . ?:'
[DONE]
- 實現:'+ - * / % > < >> << & |'出現在'='右邊時做檢查
- '': 排除指針相關的
r'\S\s\w[^ (*]*[^ )*]': ''左右無空格:如"ab", ''左無空格:如if (a* b)
r'^\s\w+[ ]*[ ]': 指針定義時''左右均留空格,如"int * p;", "int * p = &addr;", 要排除:如"if (a * b)", "= a * b" - '&': 排除取地址相關的
r'\S\s\w[=][ (&]&[^ )&]':要排除:如"int * p = &addr;" - '<>': 排除#include,排除指針符'->'
ParsePairSymbol()
列出合法沒空格的情況:
a. i++ i++, i++; (i++) [i++] ++i ++(var) i += n
b. i-- i--, i--; (i--) [i--] --i --(var) i -= n struct->member return -n array[-i]
c. *addr, (type *)addr, ***addraddr, i *= n
*exp這種形式難檢測,無法區分出是定義還是表達式
d. i /= n
e. i %= n
f. i >= n i >> n i >>= n
g. i <= n i << n i <<= n
h. &var i && j i &= n
i. i || j i |= n
j. i == j
k. ~var
l. !var
m. i != j
n. i ^= j
o. .member = n # 無需實現,'.'就這一種情況
匹配表達式:
?: r'[ ]{1,}?[ ]{1,}.*[ ]{1,}:[ ]{1,})',
- 為兼顧各種code style,修改檢查'()'內空格的條件('{' -> '{*')
[DONE]
CheckParenthesisSpacing()
match = Search(r'\b(if|for|while|switch)\s*'
r'\(([ ]*)(.).*[^ ]+([ ]*)\)\s*{*\s*$',
line)
- <rule.22>代碼塊使用花括號
[DONE]
- 實現:關鍵字后無'{'或下一行首無'{',去除do-while
CheckBraces()
if linenum < len(clean_lines.elided) - 1:
next_line = clean_lines.elided[linenum + 1]
if (Search(r'\s+(if|else|for|while)', line) and not (Search(r'{', line) or Search(r'^\s*{', next_line)) and
not Search(r'while.*;$', line)):
error(filename, linenum, 'readability/braces', 4, 'The code block requires curly braces ')
- <rule.18>宏定義是大寫字母和下划線組合
[DONE]
- 機制:檢索出關鍵字'enum',確定枚舉定義范圍,遍歷范圍內枚舉變量進行檢查
enumeration_region = 0
def CheckEnumUppercase(filename, linenum, cleansed_line, error):
global enumeration_region
# 定義在一行
if Search(r'enum', cleansed_line):
if Search(r'}', cleansed_line):
# 提取'{}'中的內容,且以','分割
string_list = Search(r'\{(.*?)\}', cleansed_line).group(1).split(',')
for element in string_list:
# 以'='分割,r'[^A-Z_ ] 非大寫,下划線,空格(來源於之前分割后帶的空格) 則告警
if Search(r'[^A-Z_ ]', element.split('=')[0]):
error(filename, linenum, 'build/enum', 4,
'Enumeration variable "%s" names must use uppercase letters and underscores' %
element.split('=')[0].strip()) # .strip()刪除多余空格
else:
enumeration_region = 1
# 定義在多行
elif enumeration_region == 1:
# 檢索到最后則跳出
if Search(r'\}', cleansed_line):
enumeration_region = 0
return
# 過濾'{'獨占一行的情況
if not Search(r'\{', cleansed_line):
if Search(r'[^A-Z_ ]', cleansed_line.split(',')[0].split('=')[0]):
error(filename, linenum, 'build/enum', 4,
'Enumeration variable "%s" names must use uppercase letters and underscores' %
cleansed_line.split(',')[0].split('=')[0].strip())
- <rule.11>關鍵字需留且只留1個空格
[DONE]
CheckParenthesisSpacing()
# Extra space after the keyword
match = Search(r'(if|for|while|switch)[ ]{2,}', line)
if match:
error(filename, linenum, 'whitespace/parens', 5,
'Extra spaces appear before ( in %s' % match.group(1))
- <rule.16>函數名命名為小寫
[DONE]
CheckForFunctionLengths()
# Function and variable names are lowercase +_
if FuncNaming() == 1:
if Search(r'[^a-z_]', function_name):
error(filename, linenum, 'readability/fname', 5, 'The function naming is invalid.')
- <rule.20>注釋風格選擇
[DONE]
CheckForMultilineCommentsAndStrings
if CommentStyle() == 1:
if Search(r'[^/]/\*|\*/', line):
print "****%d--%s" % (linenum, line)
if CommentStyle() == 2:
if linenum + 1 < clean_lines.NumLines() and linenum > 1:
if Search(r'//', line):
print "****%d--%s" % (linenum, line)
檢查功能新增規則
- 接口ProcessFile()第3個參數extra_check_functions[],這是一個函數數組,入參為:filename, clean_lines, line, error
- 這樣做是為了不打破源代碼結構,做到對源碼最小破壞
- 當然這種方法是新增檢查功能,若是原有功能不符合當前代碼風格檢查,仍需要修改源碼
涉及的python知識點
-
行尾匹配多字符結束,使用括號
endswith(('.cc', '.cpp')) -
行首空格檢查,注意'+'意思是檢查1個空格以上
Match(r'^\s+#', line) -
分隔行內容split()
cleansed_line.split()[0]) :第1個元素
cleansed_line.split()[1]) :第2個元素
cleansed_line.split()[-1]) :倒數第1個元素 -
錯位的
if-else
[參考]
https://blog.csdn.net/yfanjy/article/details/103577126
// 遍歷line檢索'target',無則只輸出一次
for line in xrange()
if search('target', line)
print "find target"
break
else
print "no found"
- 檢查
'='
周圍無空格,排除'=='
if Search(r'\S=|=\S', line) and not Search(r'==', line):
error(filename, linenum, 'whitespace/operators', 4, 'Missing spaces around ='
- 一個正則表達式的理解
match = Search(r'\b(if|for|while|switch)\s*'
r'\(([ ]*)(.).*[^ ]+([ ]*)\)\s*{\s*$',
line)
r'\b(if|for|while|switch)\s*':匹配C關鍵字開頭后接>=0個空格
r'\(([ ]*)(.).*[^ ]+([ ]*)\)\s*{\s*$'
- 如下拆分便以理解:
r'(([ ])':匹配緊接'('后>=0個空格
r'(.)' :匹配單個任意字符除換行符
r'.' :匹配多個任意字符除換行符
r'[^ ]+([ ]))\s{\s*$':匹配單個非空字符(>=1)且 緊接')'前>=0個空格 且 >=0個空格緊接'{'緊接>=0個空格
py源碼打包為.exe可執行程序
py2exe或pyinstaller
[參考]
https://blog.csdn.net/zhaochongsi/article/details/103202410
[參考]
https://www.cnblogs.com/daibeisi/p/14539324.html
- pip install pyinstaller
- pyinstaller -F file.py
[注]
:python2.7不能直接安裝,需要特定版本
pip2 install pyinstaller==3.2.1
適配不同風格代碼規則的理想方式,每種風格對應一種配置文件CPPLINT.cfg
實現步驟:
- 梳理出不同代碼風格變動點,即共同規則項
- 將變動點加入配置選項類class _CppLintState(object)
- 在接口ProcessConfigOverrides()中實現變動點解析
- 將變動點用於各個檢查點
共同規則項:
- 文件名命名規則:1:純小寫,2:小寫+,3:小寫+數字+,4:大小寫,5:大小寫+數字+_ [3]
[DONE]
- 文件首是否要求書寫版權:-1:禁止,0:無所謂,1:有要求 [1]
[DONE]
- 文件尾是否要求新行:-1:禁止,0:無所謂,1:有要求 [1]
[DONE]
- 是否允許使用TAB:-1:禁止,0:無所謂,1:允許 [-1]
[DONE]
- 代碼行長度要求:0:無所謂,>0:長度 [120]
[DONE]
- 函數體行數要求:0:無所謂,>0:長度 [80]
[DONE]
- 代碼縮進空格數:0:無所謂,>0:長度 [4]
[DONE]
- 行尾多余空格是否允許:-1:禁止,0:無所謂,1:允許 [-1]
[DONE]
- 是否允許一行出現多條指令:-1:禁止,0:無所謂,1:允許 [-1]
[DONE]
- 是否要求代碼塊(if|else|for|while)使用花括號:-1:禁止,0:無所謂,1:有要求 [1]
[DONE]
- 是否要求關鍵字前后留1個空格:-1:禁止,0:無所謂,1:有要求 [1]
[DONE]
- 是否要求運算符前后留1個空格:-1:禁止,0:無所謂,1:有要求 [1]
[TODO]
- 是否要求預處理關鍵字'#include|#>define|if|#elif|#if>def|#ifn>def|#endif'頂格:0:無所謂,1:有要求 [1]
[DONE]
- 是否允許預處理關鍵字'#include|#>define|if|#elif|#if>def|#ifn>def|#endif'井號后有空格:-1:禁止,0:無所謂 [1]
[DONE]
- 代碼風格選擇:1:K&R風格,2:Allman風格,3:Whitesmiths風格,4:GNU風格 [1]
[TODO]
- 函數名命名規則為小寫+_:0:無所謂,1:有要求 [1]
[DONE]
- 宏命名規則:0:無所謂,1:大寫+,2:大寫+數字+ [1]
[DONE]
- 枚舉命名規則:0:無所謂,1:大寫+,2:大寫+數字+ [1]
[DONE]
- 是否允許出現魔鬼數字:-1:禁止,0:無所謂 [-1]
[DONE]
- 注釋風格選擇:0:無所謂,1://,2:/* */ [0]
[DONE]
- 是否禁止連續空行超過1行:0:無所謂,1:禁止 [-1]
[DONE]
- 類型轉換是否使用C-style cast(static_cast|const_cast|reinterpret_cast):0:無所謂,1:有要求 [1]
[DONE]
- 是否禁止多條代碼語句在同一行:0:無所謂, 1:禁止 [1]
[DONE]
- '#endif'后是否要求帶注釋:0:無所謂, 1:要求 [0]
[DONE]
衍生功能
- 清除代碼中的注釋
可參照 class CleansedLines(object) 生成方式
shell使用
- 只查看print輸出的信息
./lint.py test.c 2>dev/null