Skip to content

Instantly share code, notes, and snippets.

@emilk
Last active March 19, 2022 10:22
Show Gist options
  • Save emilk/f79d43a578554a43b13b to your computer and use it in GitHub Desktop.
Save emilk/f79d43a578554a43b13b to your computer and use it in GitHub Desktop.
C++ linter in Python using libclang
#!/usr/bin/env python
# -*- coding: utf-8 -*-
""" C++ linter using libclang. Call with [filenames] """
from __future__ import print_function
from clang.cindex import Config, TypeKind, CursorKind, Index
from pprint import pprint
import platform
import sys
if platform.system() == "Linux":
Config.set_library_file("/usr/lib/llvm-3.4/lib/libclang.so")
else:
Config.set_library_file("/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/libclang.dylib")
def error(out_errors, node, msg):
error_msg = "{}:{}: {}".format(node.location.file, node.location.line, msg)
out_errors.append(error_msg)
print(error_msg, file=sys.stderr)
def is_mut_ref(arg_type):
if arg_type.kind in [TypeKind.POINTER, TypeKind.LVALUEREFERENCE,
TypeKind.INCOMPLETEARRAY, TypeKind.CONSTANTARRAY]:
if arg_type.kind in [TypeKind.POINTER, TypeKind.LVALUEREFERENCE]:
pointee_type = arg_type.get_pointee()
else:
pointee_type = arg_type.get_array_element_type()
# print("pointee_type.kind: {}".format(pointee_type.kind))
# print("pointee_type.is_const_qualified(): {}".format(pointee_type.is_const_qualified()))
if not pointee_type.is_const_qualified():
return True
return False
def check_argument(out_errors, function, node, function_parse_progress):
assert node.kind == CursorKind.PARM_DECL
if function.kind == CursorKind.FUNCTION_DECL and function.spelling == "main":
# Ignore main function
return
# print("")
# print("node.spelling: {}".format(node.spelling))
# print("node.type.kind: {}".format(node.type.kind))
# print("node.type.get_ref_qualifier(): {}".format(node.type.get_ref_qualifier()))
# print("node.type.is_const_qualified(): {}".format(node.type.is_const_qualified()))
# pprint(dir(node))
name = node.spelling
if not name:
# Ignore nameless arguments (e.g. Foo(Foo&))
return
if is_mut_ref(node.type):
if name.startswith("o_"):
function_parse_progress["state"] = "out"
elif name.startswith("io_"):
if function_parse_progress["state"] != "io":
error(out_errors, node, "io_ arguments should be first")
function_parse_progress["state"] = "io"
else:
error(out_errors, node,
"Non-const reference/pointer/array argument should be prefixed with " \
"either o_ (for out) or io_ (for in-out), e.g. 'o_{}'".format(name))
else:
if function_parse_progress["state"] == "out":
error(out_errors, node, "input arguments should come before output arguments")
function_parse_progress["state"] = "in"
def do_lint(out_errors, node, root_file):
if node.location.file and node.location.file.name != root_file.name:
# This is ugly, but works.
return
# print("{}:{} node.kind: {}".format(node.location.file, node.location.line, node.kind))
# print("node.translation_unit: {}".format(node.translation_unit))
# # pprint(dir(node.translation_unit))
# print("node.location.file: {}".format(node.location.file))
# pprint(dir(node.location))
# exit()
# CursorKind.CONSTRUCTOR excluded: references there are often stored, so not o_ or io_
if node.kind in [CursorKind.FUNCTION_DECL, CursorKind.CXX_METHOD]:
# print("Found a function!")
# print("node.spelling: {}".format(node.spelling))
# print("node.displayname: {}".format(node.displayname))
function_parse_progress = {
"state": "io", # "io", "in" or "out"
}
for arg in node.get_arguments():
check_argument(out_errors, node, arg, function_parse_progress)
# Recurse for children of this node
for c in node.get_children():
do_lint(out_errors, c, root_file)
def lint_file(filepath):
index = Index.create()
tu = index.parse(filepath)
root_file = tu.get_file(tu.spelling)
errors = []
do_lint(errors, tu.cursor, root_file)
return errors
def main():
if len(sys.argv) == 1:
print("Usage: {} [filenames]".format(sys.argv[0]))
return
for filepath in sys.argv[1:]:
lint_file(filepath)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment