├── .codeclimate.yml ├── .gitignore ├── .travis.yml ├── MANIFEST.in ├── README.md ├── config └── cohda.cstyle ├── cstyle.py ├── setup.py ├── test ├── 0001_pointer_prefix.c ├── 0001_pointer_prefix.conf ├── 0002_pointer_prefix_repeat.c ├── 0002_pointer_prefix_repeat.conf ├── 0003_no_goto.c ├── 0003_no_goto.conf ├── 0004_prefer_goto.c ├── 0004_prefer_goto.conf ├── 0005_arrays_are_pointers.c ├── 0005_arrays_are_pointers.conf ├── 0006_arrays_arent_pointers.c ├── 0006_arrays_arent_pointers.conf ├── 0007_ignore_leading_underscores.c ├── 0007_ignore_leading_underscores.conf ├── 0008_dont_ignore_leading_underscores.c ├── 0008_dont_ignore_leading_underscores.conf ├── 0009_ignore_goto.c └── 0009_ignore_goto.conf └── test_cstyle.py /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | engines: 3 | duplication: 4 | enabled: true 5 | config: 6 | languages: 7 | - python 8 | radon: 9 | enabled: true 10 | pep8: 11 | enabled: true 12 | fixme: 13 | enabled: true 14 | ratings: 15 | paths: 16 | - "**.py" 17 | exclude_paths: 18 | - test_*.py 19 | - tests/**/* 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | /.coverage 3 | /*.pyc 4 | /cstyle.egg-info 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: trusty 3 | language: python 4 | python: 5 | - "3.6.1" 6 | 7 | install: 8 | - sudo apt-add-repository "deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-5.0 main" --yes 9 | - sudo apt-add-repository "deb http://ppa.launchpad.net/ubuntu-toolchain-r/test/ubuntu trusty main" --yes 10 | - sudo apt-get update 11 | - sudo apt-get install -qq --allow-unauthenticated libclang-5.0-dev 12 | - pip3 install clang==5.0 13 | - pip3 install coveralls 14 | 15 | script: 16 | - coverage run --include=cstyle.py setup.py test 17 | 18 | after_success: 19 | - coveralls 20 | 21 | notifications: 22 | email: true 23 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CStyle 2 | 3 | [![License GPL 3](https://img.shields.io/badge/license-GPL_3-green.svg)](http://www.gnu.org/licenses/gpl-3.0.txt) 4 | [![Build Status](https://travis-ci.org/alexmurray/cstyle.svg?branch=master)](https://travis-ci.org/alexmurray/cstyle) 5 | [![Coverage Status](https://coveralls.io/repos/github/alexmurray/cstyle/badge.svg?branch=master&bust=2)](https://coveralls.io/github/alexmurray/cstyle?branch=master) 6 | [![Code Climate](https://codeclimate.com/github/alexmurray/cstyle/badges/gpa.svg)](https://codeclimate.com/github/alexmurray/cstyle) 7 | 8 | CStyle is a simple tool to check the conformance of C/C++ coding against a 9 | predefined style convention for variable / function naming etc. 10 | 11 | CStyle uses libclang to parse and identify elements (variable declarations, 12 | function names / parameters etc) and then simply checks each against a list of 13 | rules. 14 | 15 | ## Installation 16 | 17 | git clone https://github.com/alexmurray/cstyle.git 18 | cd cstyle 19 | pip3 install . 20 | cstyle --generate-config > ~/.cstyle 21 | # edit ~/.cstyle as required 22 | 23 | ## Usage 24 | 25 | `cstyle` supports a few command-line arguments: 26 | 27 | * `--generate-config` - Generate a sample configuration file 28 | * `--config` - Used to specify the path to the configuration file (See 29 | [Configuration](#configuration) below) 30 | 31 | * `--msg-template` - Used to specify the template string for outputting messages. This is specified as a Python new-style format string. This supports the following named arguments: 32 | 33 | * `file` - the path to the file 34 | * `line` - the line of the message 35 | * `column` - the column number of the identifier which the message concerns 36 | * `reason` - a human readable description of the message 37 | 38 | The default is `'{file}:{line}:{column}: {reason}'` 39 | 40 | See `cstyle --help` for more details. 41 | 42 | ## Configuration 43 | 44 | CStyle is configured using a configuration file (a sample configuration can be 45 | automatically generated with the `--generate-config` command-line 46 | argument). This defaults to `~/.cstyle` but a particular configuration can be 47 | specified using the `--config` command-line option. 48 | 49 | The configuration file supports some basic options in the `Options` section: 50 | 51 | * `ignore_leading_underscores` - If set to `true` ignore any leading 52 | underscores in variable and function names - these will be stripped before 53 | checking for any possible `pointer_prefix` or name format etc. 54 | 55 | * `ignore_goto` - If set to `true`, will ignore all gotos - disables 56 | `prefer_goto` checking. 57 | 58 | * `prefer_goto` - If set to `true`, will warn when multiple `return` statements 59 | exist in a single function. However, if set to `false` will warn about *any* 60 | use of `goto` at all. 61 | 62 | * `pointer_prefix` - If a variable is a pointer, this prefix is checked to 63 | exist at the start of the variable name. To disable simply remove this 64 | configuration option from the configuration file. 65 | 66 | * `pointer_prefix_repeat` - If set to `true` (and `pointer_prefix` is set), 67 | then the `pointer_prefix` is expected to be repeated by the depth of the 68 | pointer. i.e. for the argument `char **ppArgv`, `pointer_prefix` should be 69 | set to `p` and `pointer_prefix_repeat` should be `true`. 70 | 71 | * `arrays_are_pointers` - If a variable is an array, treat it as a pointer for 72 | `pointer_prefix` and related checks. 73 | 74 | 75 | Rules for naming variables are specified in the `Rules` section - these specify 76 | a libclang kind and the associated regular expression to validate the name. For 77 | instance, `var_decl` is used to specify the regular expression for all variable 78 | declarations. 79 | 80 | For more information on the various libclang kinds see the 81 | [libclang documentation](http://clang.llvm.org/doxygen/group__CINDEX.html#gaaccc432245b4cd9f2d470913f9ef0013) 82 | for the `CursorKind` type. NOTE: In the cstyle configuration file, each kind is 83 | specified as lowercase without the `CXCursor_` prefix, with the CamelCase suffix 84 | written in snake_case. 85 | 86 | ## Requirements 87 | 88 | * python3 89 | * python-clang 90 | 91 | ## Emacs Integration 92 | 93 | To integrate with Emacs there are two options - either standalone or via [flycheck](http://flycheck.org). 94 | 95 | For standalone usage see [cstyle.el](https://github.com/alexmurray/cstyle.el). 96 | 97 | To integrate with flycheck see [flycheck-cstyle](https://github.com/alexmurray/flycheck-cstyle). 98 | 99 | ## Vim Integration 100 | 101 | To integrate with vim via [syntastic](https://github.com/scrooloose/syntastic) see [syntastic-cstyle](https://github.com/alexmurray/syntastic-cstyle). 102 | -------------------------------------------------------------------------------- /config/cohda.cstyle: -------------------------------------------------------------------------------- 1 | [Options] 2 | ignore_leading_underscores: true 3 | pointer_prefix: p 4 | pointer_prefix_repeat: true 5 | arrays_are_pointers: false 6 | ignore_goto: true 7 | 8 | [Rules] 9 | struct_decl: _?[A-Z][A-Za-z0-9_]+ 10 | union_decl: [A-Z][A-Za-z0-9_]+ 11 | enum_decl: [A-Z][A-Za-z0-9_]+ 12 | function_decl: (main|[A-Z][A-Za-z0-9_]+) 13 | # allow lower-case i,j,k or CamelCase possibly preceded by (multiple?) _ 14 | var_decl: ([ijk]|_*([A-Z]|[A-Za-z0-9_]+)) 15 | parm_decl: ([ijk]|_*([A-Z]|[A-Za-z0-9_]+)) 16 | # enum typedef has e prefix, functions have f, otherwise should have t prefix 17 | typedef_decl: [eft][A-Z][A-Za-z0-9_]+ 18 | label_stmt: [A-Z][A-Za-z0-9_]+ 19 | -------------------------------------------------------------------------------- /cstyle.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """cstyle C/C++ style checker based on libclang""" 3 | 4 | import argparse 5 | import configparser 6 | import clang.cindex 7 | import ctypes.util 8 | import os 9 | import re 10 | import sys 11 | import platform 12 | 13 | # try find and set libclang manually once 14 | found = False 15 | if platform.system() == 'Darwin': 16 | libclangs = [ 17 | '/Applications/Xcode.app/Contents/Developer/Toolchains/' 18 | 'XcodeDefault.xctoolchain/usr/lib/libclang.dylib', 19 | '/Library/Developer/CommandLineTools/usr/lib/libclang.dylib' 20 | ] 21 | for libclang in libclangs: 22 | if os.path.exists(libclang): 23 | clang.cindex.Config.set_library_path(os.path.dirname(libclang)) 24 | found = True 25 | break 26 | 27 | if not found: 28 | for version in ([None] + ["-{version}".format(version=version) 29 | for version in ["5.0", "4.0", "3.9", "3.8", "3.7", 30 | "3.6", "3.5", "3.4", "3.3"]]): 31 | lib_name = 'clang' 32 | if version is not None: 33 | lib_name += version 34 | lib_file = ctypes.util.find_library(lib_name) 35 | if lib_file is not None: 36 | clang.cindex.Config.set_library_file(lib_file) 37 | break 38 | 39 | 40 | def config_section_to_dict(config, section, defaults=None): 41 | """Create a dict from a section of config""" 42 | _dict = {} if defaults is None else defaults 43 | try: 44 | for (name, value) in config.items(section): 45 | _dict[name] = value 46 | except configparser.NoSectionError: 47 | pass 48 | return _dict 49 | 50 | 51 | def node_is_variable_or_function(node): 52 | """Is node a variable / param declaration?""" 53 | return (node_is_variable(node) or 54 | node.kind == clang.cindex.CursorKind.FUNCTION_DECL) 55 | 56 | 57 | def node_is_variable(node): 58 | """Is node a variable / param declaration?""" 59 | return (node.kind == clang.cindex.CursorKind.VAR_DECL or 60 | node.kind == clang.cindex.CursorKind.PARM_DECL) 61 | 62 | 63 | def node_is_pointer(node, arrays_are_pointers): 64 | """Is node a pointer?""" 65 | return (node_is_variable(node) and 66 | node.type and (node.type.spelling.count('*') + 67 | (node.type.spelling.count('[') 68 | if arrays_are_pointers else 0)) > 0) 69 | 70 | 71 | class CStyle(object): 72 | """CStyle checker""" 73 | def __init__(self, config_file=None, files=None): 74 | self.options_map = { 75 | 'ignore_leading_underscores': { 76 | 'type': bool, 77 | 'default': False, 78 | 'doc': ('If set to `true` ignore any leading underscores\n' 79 | 'in variable and function names - these will be\n' 80 | 'stripped before checking for any possible pointer\n' 81 | 'prefix or name format etc.\n') 82 | }, 83 | 'pointer_prefix': { 84 | 'type': str, 85 | 'default': '', 86 | 'doc': ('If a variable is a pointer, this prefix is checked\n' 87 | 'to exist at the start of the variable name.') 88 | }, 89 | 'pointer_prefix_repeat': { 90 | 'type': bool, 91 | 'default': False, 92 | 'doc': ('If set to `true` (and `pointer_prefix` is set),\n' 93 | 'then the `pointer_prefix` is\n' 94 | 'expected to be repeated by the depth of the\n' 95 | 'pointer. i.e. for the argument `char **ppArgv`,\n' 96 | '`pointer_prefix` should be set to `p` and\n' 97 | '`pointer_prefix_repeat` should be `true`.)') 98 | }, 99 | 'arrays_are_pointers': { 100 | 'type': bool, 101 | 'default': False, 102 | 'doc': ('If a variable is an array, treat it as a pointer\n' 103 | 'for `pointer_prefix` and related checks.') 104 | }, 105 | 'ignore_goto': { 106 | 'type': bool, 107 | 'default': False, 108 | 'doc': ('If set to `true`, will ignore all gotos - disables\n' 109 | '`prefer_goto` checking.') 110 | }, 111 | 'prefer_goto': { 112 | 'type': bool, 113 | 'default': False, 114 | 'doc': ('If set to `true`, will warn when multiple\n' 115 | '`return` statements exist in a single function.\n' 116 | 'However, if set to `false` will warn about *any*\n' 117 | 'use of `goto` at all.') 118 | } 119 | } 120 | # checks to perform on each node in this order 121 | self.checks = [self.check_ignore_leading_underscores, 122 | self.check_pointer_prefix, 123 | self.check_prefer_goto, 124 | self.check_goto_harmful, 125 | self.check_rules] 126 | kinds = {kind.name.lower(): kind 127 | for kind in clang.cindex.CursorKind.get_all_kinds()} 128 | config = configparser.ConfigParser() 129 | if config_file is not None: 130 | config.read(config_file) 131 | rules = config_section_to_dict(config, 'Rules') 132 | self.rules_db = {kinds[kind]: re.compile(pattern) 133 | for (kind, pattern) in list(rules.items())} 134 | else: 135 | self.rules_db = {kinds[kind]: re.compile('^.*$') 136 | for kind in list(kinds.keys())} 137 | self.options = self.parse_options(config) 138 | self.files = files if files is not None else [] 139 | self._n_returns = 0 140 | 141 | def parse_options(self, config): 142 | """Parse Options section of config.""" 143 | options = {} 144 | for name, option in list(self.options_map.items()): 145 | get = 'get' 146 | if option['type'] is bool: 147 | get = 'getboolean' 148 | options[name] = (option['default'] 149 | if not config.has_option('Options', name) 150 | else getattr(config, get)('Options', name)) 151 | return options 152 | 153 | def local(self, node): 154 | """Check if node refers to a local file.""" 155 | return node.location.file and node.location.file.name in self.files 156 | 157 | def check_ignore_leading_underscores(self, node, name): 158 | """Do ignore_leading_underscores related checks on node.""" 159 | if ((self.options['ignore_leading_underscores'] and 160 | node_is_variable_or_function(node))): 161 | while name.startswith('_'): 162 | # strip all leading underscores 163 | name = name[1:] 164 | return False, '', name 165 | 166 | def check_pointer_prefix(self, node, name): 167 | """Do pointer_prefix related checks on node.""" 168 | invalid = False 169 | reason = '' 170 | if ((self.options['pointer_prefix'] != '' and 171 | node_is_pointer(node, self.options['arrays_are_pointers']))): 172 | prefix = self.options['pointer_prefix'] 173 | type_ = node.type.spelling 174 | count = len(prefix) 175 | if self.options['pointer_prefix_repeat']: 176 | count = type_.count('*') 177 | if self.options['arrays_are_pointers']: 178 | count += type_.count('[') 179 | prefix = prefix * count 180 | invalid = not name.startswith(prefix) 181 | if invalid: 182 | fmt = ('"{name}" is invalid - expected pointer prefix ' 183 | '"{prefix}"') 184 | reason = fmt.format(name=name, prefix=prefix) 185 | return invalid, reason, name 186 | # strip n prefix chars 187 | name = name[count:] 188 | return invalid, reason, name 189 | 190 | def check_goto_harmful(self, node, name): 191 | """Check on any use of goto for node.""" 192 | invalid = False 193 | reason = '' 194 | if not self.options['ignore_goto'] and not self.options['prefer_goto']: 195 | invalid = (node.kind == clang.cindex.CursorKind.GOTO_STMT) 196 | if invalid: 197 | reason = 'goto considered harmful' 198 | return invalid, reason, name 199 | 200 | def check_prefer_goto(self, node, name): 201 | """Perform prefer_goto check on node.""" 202 | invalid = False 203 | reason = '' 204 | if not self.options['ignore_goto'] and self.options['prefer_goto']: 205 | if node.kind == clang.cindex.CursorKind.FUNCTION_DECL: 206 | self._n_returns = 0 207 | elif node.kind == clang.cindex.CursorKind.RETURN_STMT: 208 | self._n_returns = self._n_returns + 1 209 | invalid = self._n_returns > 1 210 | if invalid: 211 | reason = 'Only 1 return statement per function (prefer_goto)' 212 | return invalid, reason, name 213 | 214 | def check_rules(self, node, name): 215 | """Check rules on node with name.""" 216 | # no point checking something which doesn't have a name (could be an 217 | # unnamed struct etc) 218 | invalid = False 219 | reason = '' 220 | if len(name) > 0: 221 | invalid = (node.kind in self.rules_db and 222 | not self.rules_db[node.kind].match(name)) 223 | if invalid: 224 | fmt = '"{name}" is invalid - failed pattern check "{pattern}"' 225 | reason = fmt.format(name=name, 226 | pattern=self.rules_db[node.kind].pattern) 227 | return invalid, reason, name 228 | 229 | def invalid(self, node): 230 | """Check if node is invalid.""" 231 | invalid = False 232 | reason = '' 233 | name = node.spelling 234 | 235 | for check in self.checks: 236 | invalid, reason, name = check(node, name) 237 | if invalid: 238 | return invalid, reason 239 | return invalid, reason 240 | 241 | def check_unit(self, unit): 242 | """Check the translation unit.""" 243 | errors = [] 244 | for node in [node for node in unit.cursor.walk_preorder() 245 | if self.local(node)]: 246 | invalid, reason = self.invalid(node) 247 | if invalid: 248 | errors.append({'file': node.location.file.name, 249 | 'line': node.location.line, 250 | 'column': node.location.column, 251 | 'reason': reason}) 252 | return errors 253 | 254 | def check(self): 255 | """Check files against rules_db and return errors""" 256 | errors = [] 257 | for files in self.files: 258 | errors += self.check_unit(clang.cindex.Index.create().parse(files)) 259 | return errors 260 | 261 | def generate_config(self): 262 | """Generate configuration and return as a string""" 263 | config = '' 264 | # Options 265 | config += '[Options]\n' 266 | for (name, option) in list(self.options_map.items()): 267 | default = option['default'] 268 | if option['type'] is bool: 269 | default = str(default).lower() 270 | doc = option['doc'].replace('\n', '\n# ') 271 | config += '# {doc}\n'.format(doc=doc) 272 | config += '{name}: {default}\n'.format(name=name, 273 | default=default) 274 | config += '\n' 275 | config += '[Rules]\n' 276 | for (kind, pattern) in list(self.rules_db.items()): 277 | config += '{kind}: {pattern}\n'.format(kind=kind.name.lower(), 278 | pattern=pattern.pattern) 279 | return config 280 | 281 | 282 | def main(): 283 | """Run cstyle""" 284 | parser = argparse.ArgumentParser(description='C Style Checker') 285 | parser.add_argument('--generate-config', action='store_true', 286 | help='generate a configuration file') 287 | parser.add_argument('--config', dest='config', 288 | default=os.path.expanduser('~/.cstyle'), 289 | help='configuration file') 290 | parser.add_argument('--msg-template', dest='template', 291 | default='{file}:{line}:{column}: {reason}', 292 | help='Set the template used to display messages.') 293 | parser.add_argument('FILES', metavar='FILE', nargs='*', 294 | help='files to check') 295 | args = parser.parse_args() 296 | if args.generate_config: 297 | sys.stdout.write(CStyle().generate_config()) 298 | return 0 299 | 300 | if len(args.FILES) == 0: 301 | parser.print_help() 302 | return 0 303 | 304 | errors = CStyle(args.config, args.FILES).check() 305 | for error in errors: 306 | sys.stderr.write(args.template.format(file=error['file'], 307 | line=error['line'], 308 | column=error['column'], 309 | reason=error['reason']) + '\n') 310 | return 1 if len(errors) > 0 else 0 311 | 312 | 313 | if __name__ == '__main__': 314 | sys.exit(main()) 315 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | This module defines the attributes of the PyPI package for cstyle 4 | """ 5 | 6 | # Always prefer setuptools over distutils 7 | from setuptools import setup 8 | # To use a consistent encoding 9 | from codecs import open 10 | from os import path 11 | 12 | here = path.abspath(path.dirname(__file__)) 13 | 14 | # Get the long description from the README file 15 | with open(path.join(here, 'README.md'), encoding='utf-8') as f: 16 | long_description = f.read() 17 | 18 | setup( 19 | name='cstyle', 20 | version='0.1.0', 21 | description='CStyle C/C++ Style Checker', 22 | long_description=long_description, 23 | url='https://github.com/alexmurray/cstyle', 24 | author='Alex Murray', 25 | author_email='murray.alex@gmail.com', 26 | license='GPLv3+', 27 | # See https://pypi.python.org/pypi?%3Aaction=list_classifiers 28 | classifiers=[ 29 | 'Development Status :: 3 - Alpha', 30 | 'Environment :: Console', 31 | 'Intended Audience :: Developers', 32 | 'Topic :: Software Development :: Build Tools', 33 | 'License :: OSI Approved :: ' 34 | 'GNU General Public License v3 or later (GPLv3+)', 35 | 'Programming Language :: C', 36 | 'Programming Language :: C++', 37 | 'Programming Language :: Python :: 2', 38 | 'Programming Language :: Python :: 2.6', 39 | 'Programming Language :: Python :: 2.7', 40 | 'Programming Language :: Python :: 3', 41 | 'Programming Language :: Python :: 3.2', 42 | 'Programming Language :: Python :: 3.3', 43 | 'Programming Language :: Python :: 3.4', 44 | 'Programming Language :: Python :: 3.5', 45 | 'Topic :: Software Development :: Quality Assurance', 46 | ], 47 | keywords='development', 48 | py_modules=['cstyle'], 49 | install_requires=['clang'], 50 | test_suite='test_cstyle.CStyleTestSuite', 51 | entry_points={ 52 | 'console_scripts': [ 53 | 'cstyle=cstyle:main', 54 | ], 55 | }, 56 | ) 57 | -------------------------------------------------------------------------------- /test/0001_pointer_prefix.c: -------------------------------------------------------------------------------- 1 | int main(int Argc, char **Argv) 2 | { 3 | return 0; 4 | } 5 | -------------------------------------------------------------------------------- /test/0001_pointer_prefix.conf: -------------------------------------------------------------------------------- 1 | [Options] 2 | pointer_prefix: p 3 | 4 | [Rules] 5 | parm_decl: [A-Z][A-Za-z_]+ 6 | -------------------------------------------------------------------------------- /test/0002_pointer_prefix_repeat.c: -------------------------------------------------------------------------------- 1 | int main(int Argc, char **pArgv) 2 | { 3 | return 0; 4 | } 5 | -------------------------------------------------------------------------------- /test/0002_pointer_prefix_repeat.conf: -------------------------------------------------------------------------------- 1 | [Options] 2 | pointer_prefix: p 3 | pointer_prefix_repeat: true 4 | 5 | [Rules] 6 | parm_decl: [A-Z][A-Za-z_]+ 7 | -------------------------------------------------------------------------------- /test/0003_no_goto.c: -------------------------------------------------------------------------------- 1 | int main(int Argc, char **pArgv) 2 | { 3 | goto foo; 4 | 5 | foo: 6 | goto bar; 7 | 8 | bar: 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /test/0003_no_goto.conf: -------------------------------------------------------------------------------- 1 | [Options] 2 | prefer_goto: false 3 | -------------------------------------------------------------------------------- /test/0004_prefer_goto.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int Argc, char **pArgv) 4 | { 5 | if (random()) 6 | return 0; 7 | else 8 | return 1; 9 | } 10 | -------------------------------------------------------------------------------- /test/0004_prefer_goto.conf: -------------------------------------------------------------------------------- 1 | [Options] 2 | prefer_goto: true 3 | -------------------------------------------------------------------------------- /test/0005_arrays_are_pointers.c: -------------------------------------------------------------------------------- 1 | int main(int Argc, char *Argv[]) 2 | { 3 | return 0; 4 | } 5 | -------------------------------------------------------------------------------- /test/0005_arrays_are_pointers.conf: -------------------------------------------------------------------------------- 1 | [Options] 2 | pointer_prefix: p 3 | pointer_prefix_repeat: true 4 | arrays_are_pointers: true 5 | 6 | [Rules] 7 | parm_decl: [A-Z][A-Za-z_]+ 8 | -------------------------------------------------------------------------------- /test/0006_arrays_arent_pointers.c: -------------------------------------------------------------------------------- 1 | int main(int Argc, char *Argv[]) 2 | { 3 | char Foo[] = {}; 4 | return 0; 5 | } 6 | -------------------------------------------------------------------------------- /test/0006_arrays_arent_pointers.conf: -------------------------------------------------------------------------------- 1 | [Options] 2 | pointer_prefix: p 3 | pointer_prefix_repeat: true 4 | arrays_are_pointers: false 5 | 6 | [Rules] 7 | parm_decl: [A-Z][A-Za-z_]+ 8 | -------------------------------------------------------------------------------- /test/0007_ignore_leading_underscores.c: -------------------------------------------------------------------------------- 1 | static void _Baz(void) 2 | { 3 | return 1; 4 | } 5 | 6 | int Main(int _Argc, char **_pArgv) 7 | { 8 | int __Bar; 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /test/0007_ignore_leading_underscores.conf: -------------------------------------------------------------------------------- 1 | [Options] 2 | ignore_leading_underscores: true 3 | pointer_prefix: p 4 | 5 | [Rules] 6 | parm_decl: [A-Z][A-Za-z_]+ 7 | function_decl: [A-Z][A-Za-z_]+ 8 | -------------------------------------------------------------------------------- /test/0008_dont_ignore_leading_underscores.c: -------------------------------------------------------------------------------- 1 | static void _Baz(void) 2 | { 3 | return 1; 4 | } 5 | 6 | int Main(int _Argc, char **_pArgv) 7 | { 8 | int __Bar; 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /test/0008_dont_ignore_leading_underscores.conf: -------------------------------------------------------------------------------- 1 | [Options] 2 | ignore_leading_underscores: false 3 | pointer_prefix: p 4 | 5 | [Rules] 6 | parm_decl: [A-Z][A-Za-z_]+ 7 | function_decl: [A-Z][A-Za-z_]+ 8 | -------------------------------------------------------------------------------- /test/0009_ignore_goto.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int Argc, char **pArgv) 4 | { 5 | if (random()) 6 | goto foo; 7 | else 8 | return 1; 9 | foo: 10 | return 2; 11 | } 12 | -------------------------------------------------------------------------------- /test/0009_ignore_goto.conf: -------------------------------------------------------------------------------- 1 | [Options] 2 | ignore_goto: true 3 | -------------------------------------------------------------------------------- /test_cstyle.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Unit tests for cstyle. 4 | """ 5 | import cstyle 6 | import os.path 7 | import unittest 8 | import sys 9 | 10 | class CStyleTestCase(unittest.TestCase): 11 | """Base test case for testing CStyle.""" 12 | def __init__(self, name, basename, expected_errors): 13 | self._basename = basename 14 | self._expected_errors = (expected_errors if expected_errors is not None 15 | else []) 16 | super(CStyleTestCase, self).__init__(name) 17 | 18 | def runTest(self): 19 | """Test case""" 20 | # output our name as all tests are called runTest so too hard to 21 | # distinguish 22 | sys.stderr.write(self._basename + ' ... ') 23 | base = os.path.join(os.path.abspath(os.path.dirname(__file__)), 24 | 'test', self._basename) 25 | errors = cstyle.CStyle(base + '.conf', 26 | [base + '.c']).check() 27 | self.assertEqual(errors, self._expected_errors) 28 | 29 | class CStyleGenerateConfigTestCase(unittest.TestCase): 30 | """Test generation of configuration file.""" 31 | def runTest(self): 32 | """Test configuration file generation.""" 33 | cstyle.CStyle(None, []).generate_config() 34 | 35 | class CStyleMainTestCase(unittest.TestCase): 36 | """Test main of cstyle.""" 37 | def runTest(self): 38 | """Test cstyle.main.""" 39 | sys.argv = [ "cstyle", "test/0001_pointer_prefix.c"] 40 | cstyle.main() 41 | 42 | class CStyleMainNoArgumentsTestCase(unittest.TestCase): 43 | """Test main of cstyle with no arguments.""" 44 | def runTest(self): 45 | """Test cstyle.main with no arguments.""" 46 | sys.argv = [ "cstyle" ] 47 | cstyle.main() 48 | 49 | class CStyleMainGenerateConfigTestCase(unittest.TestCase): 50 | """Test main of cstyle with --generate-config argument.""" 51 | def runTest(self): 52 | """Test cstyle.main with --generate-config.""" 53 | sys.argv = [ "cstyle", "--generate-config" ] 54 | cstyle.main() 55 | 56 | class CStyleTestSuite(unittest.TestSuite): 57 | """Test suite for cstyle.""" 58 | def __init__(self): 59 | super(CStyleTestSuite, self).__init__() 60 | base = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'test') 61 | tests = {'0001_pointer_prefix': 62 | [ 63 | {'column': 27, 64 | 'file': os.path.join(base, '0001_pointer_prefix.c'), 65 | 'line': 1, 66 | 'reason': '"Argv" is invalid - expected pointer prefix "p"'} 67 | ], 68 | '0002_pointer_prefix_repeat': 69 | [ 70 | {'column': 27, 71 | 'file': os.path.join(base, '0002_pointer_prefix_repeat.c'), 72 | 'line': 1, 73 | 'reason': '"pArgv" is invalid - expected pointer prefix "pp"'} 74 | ], 75 | '0003_no_goto': 76 | [ 77 | {'column': 3, 78 | 'file': os.path.join(base, '0003_no_goto.c'), 79 | 'line': 3, 80 | 'reason': 'goto considered harmful'}, 81 | {'column': 3, 82 | 'file': os.path.join(base, '0003_no_goto.c'), 83 | 'line': 6, 84 | 'reason': 'goto considered harmful'} 85 | ], 86 | '0004_prefer_goto': 87 | [ 88 | {'column': 5, 89 | 'file': os.path.join(base, '0004_prefer_goto.c'), 90 | 'line': 8, 91 | 'reason': 'Only 1 return statement per function (prefer_goto)'} 92 | ], 93 | '0005_arrays_are_pointers': 94 | [ 95 | {'column': 26, 96 | 'file': os.path.join(base, '0005_arrays_are_pointers.c'), 97 | 'line': 1, 98 | 'reason': '"Argv" is invalid - expected pointer prefix "pp"'} 99 | ], 100 | '0006_arrays_arent_pointers': 101 | [ 102 | {'column': 26, 103 | 'file': os.path.join(base, '0006_arrays_arent_pointers.c'), 104 | 'line': 1, 105 | 'reason': '"Argv" is invalid - expected pointer prefix "p"'} 106 | ], 107 | '0007_ignore_leading_underscores': 108 | [ 109 | 110 | ], 111 | '0008_dont_ignore_leading_underscores': 112 | [ 113 | {'column': 13, 114 | 'file': os.path.join(base, '0008_dont_ignore_leading_underscores.c'), 115 | 'line': 1, 116 | 'reason': '"_Baz" is invalid - failed pattern check "[A-Z][A-Za-z_]+"'}, 117 | {'column': 14, 118 | 'file': os.path.join(base, '0008_dont_ignore_leading_underscores.c'), 119 | 'line': 6, 120 | 'reason': '"_Argc" is invalid - failed pattern check "[A-Z][A-Za-z_]+"'}, 121 | {'column': 28, 122 | 'file': os.path.join(base, '0008_dont_ignore_leading_underscores.c'), 123 | 'line': 6, 124 | 'reason': '"_pArgv" is invalid - expected pointer prefix "p"'} 125 | ], 126 | '0009_ignore_goto': 127 | [ 128 | 129 | ], 130 | 131 | } 132 | for (basename, expected_errors) in list(tests.items()): 133 | test = CStyleTestCase('runTest', basename, expected_errors) 134 | self.addTest(test) 135 | self.addTest(CStyleGenerateConfigTestCase()) 136 | self.addTest(CStyleMainTestCase()) 137 | self.addTest(CStyleMainNoArgumentsTestCase()) 138 | self.addTest(CStyleMainGenerateConfigTestCase()) 139 | 140 | if __name__ == '__main__': 141 | unittest.TextTestRunner().run(CStyleTestSuite()) 142 | --------------------------------------------------------------------------------