【LINT】cpplint 分析笔记
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*[^ \(\*]\*[^ \)\*]*'
:'*'
左右无空格:如"a*b"
,'*'
左无空格:如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