1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201
|
#!/usr/bin/env python3
__author__ = 'Manuel Holtgrewe <manuel.holtgrewe@fu-berlin.de>'
import os.path
import re
import clang.cindex as ci
from . import app
from . import violations
RULE_NAMING_CONSTANT = 'naming.constant'
RULE_NAMING_STRUCT = 'naming.struct'
RULE_NAMING_UNION = 'naming.union'
RULE_NAMING_CLASS = 'naming.class'
RULE_NAMING_ENUM = 'naming.enum'
RULE_NAMING_FIELD = 'naming.field'
RULE_NAMING_ENUM_CONSTANT = 'naming.enum_constant'
RULE_NAMING_VARIABLE = 'naming.variable'
RULE_NAMING_FUNCTION = 'naming.function'
RULE_NAMING_PARAMETER = 'naming.parameter'
RULE_NAMING_VARIABLE = 'naming.variable'
RULE_NAMING_CXX_METHOD = 'naming.method'
RULE_NAMING_TYPEDEF = 'naming.typedef'
RULE_NAMING_TPL_NON_TYPE_PARAMETER = 'naming.tpl_nontype_param'
RULE_NAMING_TPL_TYPE_PARAMETER = 'naming.tpl_type_param'
RULE_NAMING_TPL_TPL_PARAMETER = 'naming.tpl_tpl_param'
RULE_NAMING_FUNCTION_TPL = 'naming.function_tpl'
RULE_NAMING_CLASS_TPL = 'naming.class_tpl'
RULE_NAMING_CLASS_TPL_SPEC = 'naming.class_tpl_spec'
RE_CONSTANT = r'^[A-Z]([_A-Z0-9])*_*$'
RE_VARIABLE = r'^_?[a-z]([_a-zA-Z0-9])*_*$'
RE_FUNCTION = r'operator.*|^_?[a-z]([_a-zA-Z0-9])*\(.*\)_*$'
RE_TYPE = r'^[A-Z]([a-zA-Z0-9])*_*$'
RE_TYPE_TEMPLATE = r'^[A-Z]([a-zA-Z0-9])*_*<.*>$'
RE_STRUCT = r'^[A-Z]([a-zA-Z0-9])*_*(<.*>)?$'
RULE_TEXTS = {
RULE_NAMING_CONSTANT: 'Constant names must be all upper-case, separated by underscores.',
RULE_NAMING_STRUCT: 'Struct names must be all camel case, starting with upper case.',
RULE_NAMING_UNION: 'Union names must be all camel case, starting with upper case.',
RULE_NAMING_CLASS: 'Class names must be all camel case, starting with upper case.',
RULE_NAMING_ENUM: 'Enum names must be all camel case, starting with upper case.',
RULE_NAMING_FIELD: 'Field names must be camel case case, starting with lower case.',
RULE_NAMING_ENUM_CONSTANT: 'Enum constant names must be all upper-case, separated by underscores.',
RULE_NAMING_VARIABLE: 'Variable names must be camel case case, starting with lower case.',
RULE_NAMING_FUNCTION: 'Function names must be camel case case, starting with lower case.',
RULE_NAMING_PARAMETER: 'Parameter names must be camel case case, starting with lower case.',
RULE_NAMING_CXX_METHOD: 'Method names must be camel case case, starting with lower case.',
RULE_NAMING_TYPEDEF: 'Typedef names must be all camel case, starting with upper case.',
RULE_NAMING_TPL_NON_TYPE_PARAMETER: 'Template non-type parameters must be all upper-case, separated by underscores.',
RULE_NAMING_TPL_TYPE_PARAMETER: 'Template type parameter names must be all camel case, starting with upper case.',
RULE_NAMING_TPL_TPL_PARAMETER: 'Template template parameter names must be all camel case, starting with upper case.',
RULE_NAMING_FUNCTION_TPL: 'Function template names must be camel case case, starting with lower case.',
RULE_NAMING_CLASS_TPL: 'Class template names must be all camel case, starting with upper case.',
RULE_NAMING_CLASS_TPL_SPEC: 'Partial specialization names must be all camel case, starting with upper case.',
}
class GenericSymbolNameRule(object):
def __init__(self, kind, regular_ex, rule_name):
self.kind = kind
self.regular_ex = regular_ex
self.rule_name = rule_name
self.visitor = None
def allowVisit(self, node):
if not app._hasFileLocation(node):
#print 'no location'
return False
if not node.displayname:
#print 'no displayname'
return False # Ignore empty symbols.
# print 'allow visit template type?', displayname, node.kind
if node.kind == self.kind:
#print 'different kind'
return True
return False
def check(self, node):
displayname = node.displayname
#print 'checking', displayname
#import pdb; pdb.set_trace()
if not re.match(self.regular_ex, displayname):
v = violations.RuleViolation(
self.rule_name, displayname, node.location.file.name,
node.location.line, node.location.column)
return [v]
return []
class VariableNameRule(object):
"""Checks variable names (in variable declarations).
The name must either be camel case (starting with lower case character) or
all upper case.
"""
def __init__(self):
self.visitor = None
def allowVisit(self, node):
if not app._hasFileLocation(node):
return False
displayname = node.displayname
if not displayname:
return False # Ignore empty symbols.
if node.kind == ci.CursorKind.VAR_DECL:
return True
return False
def check(self, node):
displayname = node.displayname
if not re.match(RE_VARIABLE, displayname) and not re.match(RE_CONSTANT, displayname):
# TODO(holtgrew): Only allow RE_CONSTANT if 'const' in declaration type.
v = violations.RuleViolation(
RULE_NAMING_VARIABLE, displayname, node.location.file.name,
node.location.line, node.location.column)
return [v]
return []
class FunctionTemplateRule(object):
"""Checks function templates.
Function template have to follow the function naming scheme. However,
libclang also exposes constructors with kind function template. The visitor
keeps a stack of current classes so we look whether the current class or
class template has the same name as the function template and allow this
besides the function naming scheme.
"""
def __init__(self):
self.visitor = None
def allowVisit(self, node):
if not app._hasFileLocation(node):
return False
displayname = node.displayname
if not displayname:
return False # Ignore empty symbols.
if node.kind == ci.CursorKind.FUNCTION_TEMPLATE:
return True
return False
def check(self, node):
displayname = node.displayname
if not re.match(RE_FUNCTION, displayname):
up_to_bracket = displayname[:displayname.find('<')]
## print 'CHECK', self.visitor.getCurrentClassName(), '!=?', up_to_bracket
if self.visitor.getCurrentClassName() != up_to_bracket:
v = violations.RuleViolation(
RULE_NAMING_FUNCTION_TPL, displayname, node.location.file.name,
node.location.line, node.location.column)
return [v]
return []
class InIncludeDirsRule(object):
"""Rule to block visiting and recursion outside include dirs."""
def __init__(self, include_dirs, exclude_dirs, source_files):
self.include_dirs = [os.path.abspath(x) for x in include_dirs]
self.source_files = [os.path.abspath(x) for x in source_files]
self.exclude_dirs = [os.path.abspath(x) for x in exclude_dirs]
self.cache = {}
def allowVisit(self, node):
"""Return True if visiting is allowed."""
if node.kind == ci.CursorKind.TRANSLATION_UNIT:
return True
if not app._hasFileLocation(node):
return False
if node.location.file.name in self.cache:
return self.cache[node.location.file.name]
# Check whether node's location is below the include directories or one
# of the source files.
filename = os.path.abspath(node.location.file.name)
result = False
for x in self.include_dirs:
if filename.startswith(x):
result = True
break
if not result:
for x in self.source_files:
if filename == x:
result = True
break
if result:
for x in self.exclude_dirs:
if filename.startswith(x):
result = False
break
self.cache[node.location.file.name] = result
return result
def allowRecurse(self, node):
"""Return True if we want to recurse below node."""
return self.allowVisit(node)
|