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

    检查是否输出错误信息

    • 如果置信度 >= 冗长度,类别通过过滤器不被抑制
    • 三种方式可决定不输出错误信息
      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]

    衍生功能

    1. 清除代码中的注释
      可参照 class CleansedLines(object) 生成方式

    shell使用

    1. 只查看print输出的信息
      ./lint.py test.c 2>dev/null
    
  • 相关阅读:
    perl 监控mysql数据库
    17.3Replication Solutions
    java.sql.SQLException: Can not issue data manipulation statements with executeQuery().
    java.sql.SQLException: Can not issue empty query.
    [2015-06-10 20:53:50
    mysqldump --flush-logs
    Caused by: com.mysql.jdbc.MysqlDataTruncation: Data truncation: Truncated incorrect DOUBLE value: 'L
    Error Code: 1414. OUT or INOUT argument 2 for routine company.new_procedure is not a variable or NEW
    Deadlock found when trying to get lock; try restarting transaction
    java.text.ParseException: Unparseable date: "2015-06-09 hh:56:19"
  • 原文地址:https://www.cnblogs.com/skullboyer/p/16071822.html
Copyright © 2020-2023  润新知