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):

检查是否输出错误信息

  • 如果置信度 >= 冗长度,类别通过过滤器不被抑制
  • 三种方式可决定不输出错误信息
    1. 'NOLINT'源码注释
    1. 冗长度不够高
    1. 过滤器将其过滤掉

def Error(filename, linenum, category, confidence, message):

错误信息输出

  • 记录了错误发生地,及错误置信度
  • 误报可以使用"cpplint(category)"注释误报行,这样就会解析为错误抑制
  • 置信度数越高意味着该错误越确定
  • 输出错误信息样式(3种):
    1. 'vs7': filename(linenum): error cpplint: [category] message [confidence]
    1. 'eclipse': filename:linenum: warning: message [category] [confidence]
    1. 'other': filename:linenum: message [category] [confidence]

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份变种,并进行不同的预处理
    1. elided member:删除字符串和注释的行
    1. lines member:删除注释的行
    1. raw_lines member:未进行处理的所有行
    1. 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出现的顺序规则是:
      1. for foo.cc, foo.h (preferred location)
      1. c system files
      1. cpp system files
      1. for foo.cc, foo.h (deprecated location)
      1. 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'
  • 遍历文件集合处理文件 ->>
  • 输出错误数

检查项梳理

  1. [新增]文件名检查
  2. [原有]文件开头版权检查
  3. [原有]header防重包含检查
  4. [原有]NOLINT检查
  5. [原有]namespace缩进检查
  6. [原有]函数体行数检查(非空非注释行)
  7. [原有]注释符检查'/**/' '//'
  8. [原有]字符串标记符'""'检查
  9. [原有]风格规则
  10. [原有]语言规则
  11. [原有]非常量引用
  12. [原有]非标准结构
  13. [原有]VLOG()接口使用检查
  14. [原有]线程安全函数检查
  15. [原有]指针无效自增检查
  16. [原有]make_pair模板参数省略检查
  17. [原有]virtual关键字冗余检查
  18. [原有]override和final关键字冗余检查
  19. [原有]STL相关头文件未包含检查
  20. [原有]源文件自包含头文件检查
  21. [原有]行内容坏字符(编码)检查
  22. [原有]行尾符 LF/CRLF 检查
  23. [原有]文件结尾新行检查

检查项补充或修改

基于华为C/C++编码规范

  1. 代码行缩进均为配置选项的整数倍空格 [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())
  1. 预处理指令顶格缩进检查'#' [DONE]
  CheckPreprocessWhitespace():
if Match(r'^\s+#', line):
error(filename, linenum, 'whitespace/preprocess', 4,
'Preprocessor directives are not allowed to start with Spaces')
  1. 预处理单空格检查,如'#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')
  1. 函数体行数检查 [DONE]
  class _FunctionState
_NORMAL_TRIGGER = 50
  1. 文件名由小写字母、数字和下划线组成 [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. 魔鬼数字,'=|=='后的'[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')
  1. 不许包含源文件 [DONE]
  CheckIncludeLine():
elif (include.endswith(('.cc', '.cpp')) and
  1. <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')
  1. 删除规则包含header带父目录 [DONE]
  CheckIncludeLine()
''' @skull.
  1. 函数定义后的'{'需放置下行首,而非本行尾 [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')
  1. 加入版本查询选项,用法 --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)
  1. 头文件防重包含的变量名字检查,应忽略谷歌规则 [TODO]
  • 可通过配置选项--root搞点事情
  • 亦可通过--filter过滤
  1. 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)))
  1. 检查注释符'/**/'是否配对时,应该使用原始代码(带注释) [DONE]
  CheckForMultilineCommentsAndStrings()
line = clean_lines.elided[linenum] => line = clean_lines.raw_lines[linenum]
  1. '#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')
  1. 锁定.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]:获取最终的文件形态的所在路径
  1. 注释对齐 [TODO]
  • 当前规则是:代码行后至少空2格'//'空1个进行注释
  • 实现:若检查代码与'//'空格数 >2,则进行与下一行注释的'//'位置进行匹配,一致则符合注释对齐规则,不一致则error
  • 但前提是当前带注释的行与下一带注释的行同属一模块,如均是宏定义、同在函数体内、同一结构体内等
  1. 预处理指令中井号后不允许有空格 [DONE]
  CheckPreprocessWhitespace():
if Match(r'^\s*#\s+', cleansed_line):
error(filename, linenum, 'whitespace/preprocess', 4,
'Disable Spaces after # in preprocessor instructions')
  1. 宏定义表达式中的'&'被认为是取地址符,从而进行转换检查 [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))
  1. Allman风格检查 [DONE]
  • 实现:'{' '}'独立占一行且与上一行缩进相同
  CheckBraces()
elif CodeStyle() == 2:
  1. <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))
  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))
  1. 当前'{}','()','[]'内部有无空格不检测,且空格是否配对也不检测 [DONE]
  def ParsePairSymbol(symbol, line, filename, linenum, error):
  1. 运算符单空格检查:'+ - * / % > < >> << & && | || = == += -= *= /= %= >= <= ~ ! != &= |= ^= >>= <<= -> . ?:' [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,})',

  1. 为兼顾各种code style,修改检查'()'内空格的条件('{' -> '{*') [DONE]
  CheckParenthesisSpacing()
match = Search(r'\b(if|for|while|switch)\s*'
r'\(([ ]*)(.).*[^ ]+([ ]*)\)\s*{*\s*$',
line)
  1. <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 ')
  1. <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())
  1. <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))
  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.')
  1. <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知识点

  1. 行尾匹配多字符结束,使用括号

    endswith(('.cc', '.cpp'))

  2. 行首空格检查,注意'+'意思是检查1个空格以上

    Match(r'^\s+#', line)

  3. 分隔行内容split()

    cleansed_line.split()[0]) :第1个元素

    cleansed_line.split()[1]) :第2个元素

    cleansed_line.split()[-1]) :倒数第1个元素

  4. 错位的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"
  1. 检查'='周围无空格,排除'=='
  if Search(r'\S=|=\S', line) and not Search(r'==', line):
error(filename, linenum, 'whitespace/operators', 4, 'Missing spaces around ='
  1. 一个正则表达式的理解
  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

  1. pip install pyinstaller
  2. pyinstaller -F file.py

[注]:python2.7不能直接安装,需要特定版本

pip2 install pyinstaller==3.2.1


适配不同风格代码规则的理想方式,每种风格对应一种配置文件CPPLINT.cfg

实现步骤:

  1. 梳理出不同代码风格变动点,即共同规则项
  2. 将变动点加入配置选项类class _CppLintState(object)
  3. 在接口ProcessConfigOverrides()中实现变动点解析
  4. 将变动点用于各个检查点

共同规则项:

  1. 文件名命名规则:1:纯小写,2:小写+,3:小写+数字+,4:大小写,5:大小写+数字+_ [3] [DONE]
  2. 文件首是否要求书写版权:-1:禁止,0:无所谓,1:有要求 [1] [DONE]
  3. 文件尾是否要求新行:-1:禁止,0:无所谓,1:有要求 [1] [DONE]
  4. 是否允许使用TAB:-1:禁止,0:无所谓,1:允许 [-1] [DONE]
  5. 代码行长度要求:0:无所谓,>0:长度 [120] [DONE]
  6. 函数体行数要求:0:无所谓,>0:长度 [80] [DONE]
  7. 代码缩进空格数:0:无所谓,>0:长度 [4] [DONE]
  8. 行尾多余空格是否允许:-1:禁止,0:无所谓,1:允许 [-1] [DONE]
  9. 是否允许一行出现多条指令:-1:禁止,0:无所谓,1:允许 [-1] [DONE]
  10. 是否要求代码块(if|else|for|while)使用花括号:-1:禁止,0:无所谓,1:有要求 [1] [DONE]
  11. 是否要求关键字前后留1个空格:-1:禁止,0:无所谓,1:有要求 [1] [DONE]
  12. 是否要求运算符前后留1个空格:-1:禁止,0:无所谓,1:有要求 [1] [TODO]
  13. 是否要求预处理关键字'#include|#>define|if|#elif|#if>def|#ifn>def|#endif'顶格:0:无所谓,1:有要求 [1] [DONE]
  14. 是否允许预处理关键字'#include|#>define|if|#elif|#if>def|#ifn>def|#endif'井号后有空格:-1:禁止,0:无所谓 [1] [DONE]
  15. 代码风格选择:1:K&R风格,2:Allman风格,3:Whitesmiths风格,4:GNU风格 [1] [TODO]
  16. 函数名命名规则为小写+_:0:无所谓,1:有要求 [1] [DONE]
  17. 宏命名规则:0:无所谓,1:大写+,2:大写+数字+ [1] [DONE]
  18. 枚举命名规则:0:无所谓,1:大写+,2:大写+数字+ [1] [DONE]
  19. 是否允许出现魔鬼数字:-1:禁止,0:无所谓 [-1] [DONE]
  20. 注释风格选择:0:无所谓,1://,2:/* */ [0] [DONE]
  21. 是否禁止连续空行超过1行:0:无所谓,1:禁止 [-1] [DONE]
  22. 类型转换是否使用C-style cast(static_cast|const_cast|reinterpret_cast):0:无所谓,1:有要求 [1] [DONE]
  23. 是否禁止多条代码语句在同一行:0:无所谓, 1:禁止 [1] [DONE]
  24. '#endif'后是否要求带注释:0:无所谓, 1:要求 [0] [DONE]

实现:cpplint修改版:自定义编码风格检查工具lint


衍生功能

  1. 清除代码中的注释

    可参照 class CleansedLines(object) 生成方式

shell使用

  1. 只查看print输出的信息
  ./lint.py test.c 2>dev/null

【LINT】cpplint 分析笔记的更多相关文章

  1. 3.View绘制分析笔记之onLayout

    上一篇文章我们了解了View的onMeasure,那么今天我们继续来学习Android View绘制三部曲的第二步,onLayout,布局. ViewRootImpl#performLayout pr ...

  2. 4.View绘制分析笔记之onDraw

    上一篇文章我们了解了View的onLayout,那么今天我们来学习Android View绘制三部曲的最后一步,onDraw,绘制. ViewRootImpl#performDraw private ...

  3. 2.View绘制分析笔记之onMeasure

    今天主要学习记录一下Android View绘制三部曲的第一步,onMeasure,测量. 起源 在Activity中,所有的View都是DecorView的子View,然后DecorView又是被V ...

  4. 1.Android 视图及View绘制分析笔记之setContentView

    自从1983年第一台图形用户界面的个人电脑问世以来,几乎所有的PC操作系统都支持可视化操作,Android也不例外.对于所有Android Developer来说,我们接触最多的控件就是View.通常 ...

  5. zeromq源码分析笔记之线程间收发命令(2)

    在zeromq源码分析笔记之架构说到了zmq的整体架构,可以看到线程间通信包括两类,一类是用于收发命令,告知对象该调用什么方法去做什么事情,命令的结构由command_t结构体确定:另一类是socke ...

  6. glusterfs 4.0.1 api 分析笔记1

    一般来说,我们写个客户端程序大概的样子是这样的: /* glfs_example.c */ // gcc -o glfs_example glfs_example.c -L /usr/lib64/ - ...

  7. SEH分析笔记(X64篇)

    SEH分析笔记(X64篇) v1.0.0 boxcounter 历史: v1.0.0, 2011-11-4:最初版本. [不介意转载,但请注明出处 www.boxcounter.com  附件里有本文 ...

  8. 【转载】Instagram架构分析笔记

    原文地址:http://chengxu.org/p/401.html Instagram 架构分析笔记 全部 技术博客 Instagram团队上个月才迎来第 7 名员工,是的,7个人的团队.作为 iP ...

  9. CentOS下使用Iptraf进行网络流量的分析笔记

    CentOS下使用Iptraf进行网络流量的分析笔记 一.概述 Iptraf是一款linux环境下,监控网络流量的一款绝佳的免费小软件. 本博客其他随笔参考: Centos安装流量监控工具iftop笔 ...

  10. 《linux 内核全然剖析》 fork.c 代码分析笔记

    fork.c 代码分析笔记 verifiy_area long last_pid=0; //全局变量,用来记录眼下最大的pid数值 void verify_area(void * addr,int s ...

随机推荐

  1. 初识BigDecimal

    BigDecimal所创建的是对象,我们不能使用传统的+.-.*./等算术运算符直接对其对象进行数学运算,而必须调用其相对应的方法. 方法中的参数也必须是BigDecimal的对象. BigDecim ...

  2. ElasticSearch之Node query cache settings

    对于filter查询,ElasticSearch提供了缓存查询结果的特性,当缓存中存在满足查询条件要求的数据时,直接从缓存中提取查询结果. 对于ElasticSearch节点,该节点上的所有shard ...

  3. Ubuntu系统 安装 Zabbix Server 6.0

    Zabbix6.0简介: zabbix6.0新特性 1.开箱即用的Zabbix server高可用群集 Zabbix server高可用防止硬件故障或计划维护期的停机: 原生选择加入HA群集配置 定义 ...

  4. JavaFx之横向布局左右两侧对齐(十九)

    JavaFx之横向布局左右两侧对齐(十九) 横向布局HBox在子节点A.B中添加<HBox HBox.hgrow="ALWAYS"></HBox> 即可做到 ...

  5. 从VMWare安装到Nginx配置

    1.安装VMWare Workstation 16 player,Mac也可使用Parallels Desktop,自己有虚拟主机的跳过: 2.在虚拟机中,安装Centos7操作系统,使用Minima ...

  6. bazel test 编译失败:googletest、gtest 报错

    问题描述 bazel test 遇到很多奇怪的编译错误,报错位置位于"googletest"目录,而且没有修改过 googletest 源码: ERROR: /bazel_cach ...

  7. CodeForces 1105D 嵌套BFS

    CodeForces 1105D 嵌套BFS 题意 - 给我们一个n*m的阵列,一个格子如果是#则为障碍,若为.则为空,若为数字,则代表这个格子属于该数字代表的玩家. - 给我们每个玩家(不到十个)的 ...

  8. 微服务下,使用 ELK 进行日志采集以及统一处理

    摘要:微服务各个组件的相关实践会涉及到工具,本文将会介绍微服务日常开发的一些利器,这些工具帮助我们构建更加健壮的微服务系统,并帮助排查解决微服务系统中的问题与性能瓶颈等. 微服务各个组件的相关实践会涉 ...

  9. 认识一下MRS里的“中间人”Alluxio

    摘要:Alluxio在mrs的数据处理生态中处于计算和存储之间,为上层spark.presto.mapredue.hive计算框架提供了数据抽象层,计算框架可以通过统一的客户端api和全局命名空间访问 ...

  10. 50亿海量数据如何高效存储和分析? GaussDB (for Cassandra) 3个秘诀搞定

    摘要:信息社会正在从互联网时代走向物联网时代,企业不可避免的要面对数据量剧增带来的一系列问题:如何高效存储和扩容,如何在对原有业务改动最小的情况下做到智能化和实时分析. 本文分享自华为云社区<5 ...