blob: 1160bbf9b77a19c07ea3f5eb17eb412fde752045 [file] [log] [blame] [edit]
#!/usr/bin/env python3
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# This parses the output of 'include-what-you-use', focusing on just removing
# not needed includes and providing a relatively conservative output by
# filtering out a number of LibreOffice-specific false positives.
#
# It assumes you have a 'compile_commands.json' around (similar to clang-tidy),
# you can generate one with 'make vim-ide-integration'.
#
# Design goals:
# - excludelist mechanism, so a warning is either fixed or excluded
# - works in a plugins-enabled clang build
# - no custom configure options required
# - no need to generate a dummy library to build a header
import json
import multiprocessing
import os
import queue
import re
import subprocess
import sys
import threading
import yaml
import argparse
import pathlib
def ignoreRemoval(include, toAdd, absFileName, moduleRules, noexclude):
# global rules
# Avoid replacing .hpp with .hdl in the com::sun::star and ooo::vba namespaces.
if ( include.startswith("com/sun/star") or include.startswith("ooo/vba") ) and include.endswith(".hpp"):
hdl = include.replace(".hpp", ".hdl")
if hdl in toAdd:
return True
# Avoid proposing o3tl fw declaration
o3tl = {
"o3tl/typed_flags_set.hxx" : "namespace o3tl { template <typename T> struct typed_flags; }",
"o3tl/deleter.hxx" : "namespace o3tl { template <typename T> struct default_delete; }",
}
for k, v, in o3tl.items():
if include == k and v in toAdd:
return True
# Follow boost documentation.
if include == "boost/optional.hpp" and "boost/optional/optional.hpp" in toAdd:
return True
if include == "boost/intrusive_ptr.hpp" and "boost/smart_ptr/intrusive_ptr.hpp" in toAdd:
return True
if include == "boost/shared_ptr.hpp" and "boost/smart_ptr/shared_ptr.hpp" in toAdd:
return True
if include == "boost/variant.hpp" and "boost/variant/variant.hpp" in toAdd:
return True
if include == "boost/unordered_map.hpp" and "boost/unordered/unordered_map.hpp" in toAdd:
return True
if include == "boost/functional/hash.hpp" and "boost/container_hash/extensions.hpp" in toAdd:
return True
# Avoid .hxx to .h proposals in basic css/uno/* and basic URE API
unoapi = {
"com/sun/star/uno/Any.hxx": "com/sun/star/uno/Any.h",
"com/sun/star/uno/Reference.hxx": "com/sun/star/uno/Reference.h",
"com/sun/star/uno/Sequence.hxx": "com/sun/star/uno/Sequence.h",
"com/sun/star/uno/Type.hxx": "com/sun/star/uno/Type.h",
"rtl/bootstrap.hxx": "rtl/bootstrap.h",
"rtl/byteseq.hxx": "rtl/byteseq.h",
"typelib/typedescription.hxx": "typelib/typedescription.h",
"cppuhelper/interfacecontainer.hxx": "cppuhelper/interfacecontainer.h",
"osl/file.hxx": "osl/file.h",
"osl/module.hxx": "osl/module.h",
"osl/pipe.hxx": "osl/pipe.h",
"osl/pipe.hxx": "osl/pipe_decl.hxx",
"osl/socket.hxx": "osl/socket.h",
"osl/socket.hxx": "osl/socket_decl.hxx",
"osl/thread.hxx": "osl/thread.h"
}
for k, v in unoapi.items():
if include == k and v in toAdd:
return True
# Most Qt headers are used as <QtLib/QClass> and IWYU suggests
# them to be replaced with QtLib/qclass.h, these could be skipped
# BUT: Several Qt 6 minor versions are supported,
# making testing of false positives over-expensive
# so just skip all those headers for now
if include.startswith("Qt") and '/' in include:
return True
# 3rd-party, non-self-contained headers.
if include == "libepubgen/libepubgen.h" and "libepubgen/libepubgen-decls.h" in toAdd:
return True
if include == "librevenge/librevenge.h" and "librevenge/RVNGPropertyList.h" in toAdd:
return True
if include == "libetonyek/libetonyek.h" and "libetonyek/EtonyekDocument.h" in toAdd:
return True
if include == "box2d/box2d.h" and "box2d/b2_world.h" in toAdd:
return True
# Skip forward declaration of versioned namespaces
# see https://gerrit.libreoffice.org/c/core/+/72972
# for an example of breakage caused by this
if include == "unicode/regex.h":
for item in toAdd:
match = re.search(r'namespace icu.* class RegexMatcher', item)
if match:
return True
noRemove = (
# <https://www.openoffice.org/tools/CodingGuidelines.sxw> insists on not
# removing this.
"sal/config.h",
# Works around a build breakage specific to the broken Android
# toolchain.
"android/compatibility.hxx",
# Removing this would change the meaning of '#if defined OSL_BIGENDIAN'.
"osl/endian.h",
)
if include in noRemove:
return True
# Ignore when <foo> is to be replaced with "foo".
if include in toAdd:
return True
fileName = os.path.relpath(absFileName, os.getcwd())
# Skip headers used only for compile test
if fileName == "cppu/qa/cppumaker/test_cppumaker.cxx":
if include.endswith(".hpp"):
return True
# yaml rules, except when --noexclude is given
if "excludelist" in moduleRules.keys() and not noexclude:
excludelistRules = moduleRules["excludelist"]
if fileName in excludelistRules.keys():
if include in excludelistRules[fileName]:
return True
return False
def unwrapInclude(include):
# Drop <> or "" around the include.
return include[1:-1]
def processIWYUOutput(iwyuOutput, moduleRules, fileName, noexclude, checknamespaces, finderrors, removefwdd):
inAdd = False
toAdd = []
inRemove = False
toRemove = []
inFull = False
currentFileName = None
for line in iwyuOutput:
line = line.strip()
# Bail out if IWYU gave an error due to non self-containedness
if re.match ("(.*): error: (.*)", line):
return -1
# Bail out if we are in finderrors mode
if finderrors:
return -2
if len(line) == 0:
if inRemove:
inRemove = False
continue
if inAdd:
inAdd = False
continue
if inFull:
inFull = False
continue
shouldAdd = fileName + " should add these lines:"
match = re.match(shouldAdd, line)
if match:
currentFileName = match.group(0).split(' ')[0]
inAdd = True
continue
shouldRemove = fileName + " should remove these lines:"
match = re.match(shouldRemove, line)
if match:
currentFileName = match.group(0).split(' ')[0]
inRemove = True
continue
# Process the full include list to check for
# - obsolete namespace declarations in --ns mode
# - obsolete exclusions in the yaml file
match = re.match("The full include-list for " + fileName, line)
if match:
inFull = True
continue
# Check if IWYU considers the header as having correct
# list of includes - this would make all exclusions obsolete
match = re.match(".*has correct #includes/fwd-decls.*", line)
if match:
if "excludelist" in moduleRules.keys():
excludelistRules = moduleRules["excludelist"]
realFileName = os.path.relpath(fileName, os.getcwd())
if realFileName in excludelistRules.keys():
print("WARNING:", "File has correct #includes/fwd-decls; all excluded headers can be removed from exclusion list of:", realFileName)
continue
if inAdd:
match = re.match('#include ([^ ]+)', line)
if match:
include = unwrapInclude(match.group(1))
toAdd.append(include)
else:
# Forward declaration.
toAdd.append(line)
if inRemove and not checknamespaces:
if not removefwdd:
match = re.match("- #include (.*) // lines (.*)-.*", line)
if match:
include = unwrapInclude(match.group(1))
lineno = match.group(2)
if not ignoreRemoval(include, toAdd, currentFileName, moduleRules, noexclude):
toRemove.append("%s:%s: %s" % (currentFileName, lineno, include))
continue
else:
# Search for obsolete forward declarations, but not header -> fwdecl replacements
match = re.match("- (.*;(?: })*)* // lines (.*)-.*", line)
if match:
fwdDecl = match.group(1)
# Bug: IWYU 0.23 gives "- // lines 76-76" on basctl/source/inc/layout.hxx in --fwdecl mode
if fwdDecl is None:
continue
if fwdDecl.endswith(";"):
# Remove trailing semicolon.
fwdDecl = fwdDecl[:-1]
lineno = match.group(2)
if not ignoreRemoval(fwdDecl, toAdd, currentFileName, moduleRules, noexclude):
toRemove.append("%s:%s: %s" % (currentFileName, lineno, fwdDecl))
continue
if inFull:
# Check if a formerly excluded header is now considered
# as necessary for the full include list,
# thereby making the IWYU exclusion unnecessary
# This can happen if a newer IWYU no longer gives a false warning
match = re.match("#include\\s+[\"<]([^\">]+)[\">]", line)
if match and "excludelist" in moduleRules.keys():
include = match.group(1)
excludelistRules = moduleRules["excludelist"]
realFileName = os.path.relpath(fileName, os.getcwd())
if realFileName in excludelistRules.keys():
if include in excludelistRules[realFileName]:
print("WARNING:", include, "excluded header can be removed from exclusion list of file:", realFileName)
if checknamespaces:
# match for all possible URE/UNO namespaces, created with:
# find udkapi/com/sun/star/ -type d | sort| xargs basename -a | tr '\012' '|'
# find offapi/com/sun/star/ -type d | sort | xargs basename -a | tr '\012' '|'
# and ooo::vba namespaces
# plus a few popular ones about other modules
ns = re.compile(
'.*for\\ ('
# URE namespaces
'beans|'
'bridge|oleautomation|'
'connection|'
'container|'
'io|'
'java|'
'lang|'
'loader|'
'reflection|'
'registry|'
'script|'
'security|'
'task|'
'uno|'
'uri|'
'util|'
# UNO namespaces
'accessibility|'
'animations|'
'auth|'
'awt|tab|tree|grid|'
'chart|'
'chart2|data|'
'configuration|bootstrap|backend|xml|'
'cui|'
'datatransfer|clipboard|dnd|'
'deployment|test|ui|'
'document|'
'drawing|framework|'
'embed|'
'form|binding|runtime|control|inspection|submission|component|validation|'
'formula|'
'frame|status|'
'gallery|'
'geometry|'
'graphic|'
'i18n|'
'image|'
'inspection|'
'ldap|'
'linguistic2|'
'logging|'
'mail|'
'media|'
'mozilla|'
'office|'
'packages|zip|manifest|'
'presentation|textfield|'
'qa|'
'rdf|'
'rendering|'
'report|inspection|meta|'
'resource|'
'scanner|'
'script|vba|browse|provider|'
'sdb|application|tools|'
'sdbc|'
'sdbcx|'
'security|'
'setup|'
'sheet|opencl|'
'smarttags|'
'style|'
'svg|'
'system|windows|'
'table|'
'task|'
'text|textfield|docinfo|fieldmaster|'
'tiledrendering|'
'ucb|'
'ui|dialogs|test|'
'util|'
'view|'
'xforms|'
'xml|xslt|wrapper|csax|sax|input|xpath|dom|views|events|crypto|sax|'
'xsd|'
# ooo::vba and its namespaces
'ooo|vba|excel|powerpoint|adodb|access|office|word|stdole|msforms|dao|'
# use of module namespaces, as spotted in the code
'analysis|pricing' # sca internals
'apphelper|CloneHelper|DataSeriesProperties|SceneProperties|wrapper|' # for chart internals
'basegfx|utils|'
'boost|posix_time|gregorian'
'cairo|'
'canvas|'
'chelp|'
'comphelper|'
'connectivity|'
'cpp|java|' # for codemaker::
'cppu|'
'dbaccess|dbahsql|dbaui|dbtools|'
'desktop|dp_misc|'
'drawinglayer|attribute|geometry|primitive2d|processor2d|'
'editeng|'
'emscripten|'
'formula|'
'framework|'
'frm|'
'http_dav_ucp|tdoc_ucp|package_ucp|hierarchy_ucp|gio|fileaccess|ucb_impl|hcp_impl|ucb_cmdenv|' # for ucb internal
'i18npool|'
'internal|ColorComponentTag|' # for slideshow internals
'jfw_plugin|'
'jni_uno|'
'librevenge|'
'linguistic|'
'lok|'
'mtv|' # for mdds::mtv
'nsSwDocInfoSubType|SWUnoHelper|nsHdFtFlags|' # sw internal
'o3tl|'
'odfflatxml|' # filter internal
'oox|core|drawingml|ole|vml|'
'OpenStormBento|'
'osl|'
'pdfi|pdfparse|'
'ppt|'
'pyuno|'
'reportdesign|'
'rptui|'
'rtl|math|textenc|'
'salhelper|'
'sax_fastparser|'
'sax|' # for xml::sax
'sc|'
'SchXMLTools|' # for xmloff
'sd|slidesorter|cache|controller|model|view|'
'sf_misc|'
'sfx2|DocTempl|'
'sidebar|' # for sfx2::sidebar
'star|' # for com::sun::star
'std|chrono_literals|literals|'
'stoc_sec|'
'store|'
'svl|impl|'
'svt|'
'svtools|'
'svx|sdr|contact|table|'
'sw|access|annotation|mark|types|util|'
'toolkit|'
'treeview|'
'ucbhelper|'
'unopkg|'
'util|db|qe|' # for xmlsearch::
'utl|'
'vcl|psp|x11|'
'writerfilter|'
'xforms|'
'xmloff|token|EnhancedCustomShapeToken' # for xmloff::
'ZipUtils'
')$', re.VERBOSE
)
reason = re.match(ns, line)
if reason:
# Warn about namespaces: if a header is suggested only '// for $namespace', then the namespace is not used
# otherwise the used classes name would show up after the '// for'
# Cleaning out the respective header (if there is any
# - which is not always the case) is for the next run!
nameSpace = reason.group(1).split(' ')[0]
print("WARNING:", fileName, "This 'using namespace' is likely unnecessary:", nameSpace)
# Get the row number, normal IWYU output does not contain this info
subprocess.run(["git", "grep", "-n", "namespace.*[^a-zA-Z]"+nameSpace+" *;", fileName])
if removefwdd:
for remove in sorted(toRemove, key=lambda x: int(x.split(":")[1])):
print("ERROR: %s: remove not needed forward declaration" % remove)
else:
for remove in sorted(toRemove, key=lambda x: int(x.split(":")[1])):
print("ERROR: %s: remove not needed include" % remove)
return len(toRemove)
def run_tool(task_queue, failed_files, noexclude, checknamespaces, finderrors, removefwdd):
while True:
invocation, moduleRules = task_queue.get()
if not len(failed_files):
print("[IWYU] " + invocation.split(' ')[-1])
p = subprocess.Popen(invocation, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
retcode = processIWYUOutput(p.communicate()[0].decode('utf-8').splitlines(), moduleRules, invocation.split(' ')[-1], noexclude, checknamespaces, finderrors, removefwdd)
if finderrors:
if p.returncode == 1:
print("Running the IWYU process returned error code:\n" + invocation)
if retcode == -1 and not checknamespaces and not removefwdd:
print("WARNING: A file is probably not self contained, check this commands output:\n" + invocation)
elif retcode > 0:
if not removefwdd:
print("ERROR: The following command found unused includes:\n" + invocation)
else:
print("ERROR: The following command found unused forward declarations:\n" + invocation)
task_queue.task_done()
if checknamespaces:
# Workaround: sometimes running git grep makes the letters typed into the terminal disappear after the script is finished
os.system('stty sane')
def isInUnoIncludeFile(path):
return path.startswith("include/com/") \
or path.startswith("include/cppu/") \
or path.startswith("include/cppuhelper/") \
or path.startswith("include/osl/") \
or path.startswith("include/rtl/") \
or path.startswith("include/sal/") \
or path.startswith("include/salhelper/") \
or path.startswith("include/systools/") \
or path.startswith("include/typelib/") \
or path.startswith("include/uno/")
# Don't bother with non-linux specific paths,
# useful for automatically checking headers
def isNonLinuxPath(path):
return path.startswith("avmedia/source/win/") \
or path.startswith("avmedia/source/macavf/") \
or path.startswith("include/apple_remote/")
def isNonLinuxFile(path):
NonLinuxFiles = (
"include/comphelper/windowserrorstring.hxx",
"include/comphelper/windowsdebugoutput.hxx",
"include/comphelper/windowsStart.hxx",
"include/drawinglayer/processor2d/d2dpixelprocessor2d.hxx",
"include/static/unoembindhelpers/PrimaryBindings.hxx",
"include/version.hrc",
"include/vcl/winscheduler.hxx",
)
if path in NonLinuxFiles:
return True
def tidy(compileCommands, paths, noexclude, checknamespaces, finderrors, removefwdd, debug, headersfwd):
return_code = 0
try:
max_task = multiprocessing.cpu_count()
task_queue = queue.Queue(max_task)
failed_files = []
for _ in range(max_task):
t = threading.Thread(target=run_tool, args=(task_queue, failed_files, noexclude, checknamespaces, finderrors, removefwdd))
t.daemon = True
t.start()
for path in sorted(paths):
if isInUnoIncludeFile(path) or isNonLinuxFile(path) or isNonLinuxPath(path):
continue
# IWYU fails on these with #error: don't use this in new code
if path.startswith("include/vcl/toolkit"):
continue
moduleName = path.split("/")[0]
rulePath = os.path.join(moduleName, "IwyuFilter_" + moduleName + ".yaml")
moduleRules = {}
if os.path.exists(rulePath):
moduleRules = yaml.full_load(open(rulePath))
assume = None
pathAbs = os.path.abspath(path)
compileFile = pathAbs
matches = [i for i in compileCommands if i["file"] == compileFile]
if not len(matches):
# Only use assume-filename for headers, so we don't try to analyze e.g. Windows-only
# code on Linux.
if "assumeFilename" in moduleRules.keys() and not path.endswith("cxx"):
assume = moduleRules["assumeFilename"]
# Check for lower level assume file name
if "customAssumeFilenames" in moduleRules:
customAssumeRules = moduleRules["customAssumeFilenames"]
for dirName in customAssumeRules.keys():
if path.startswith(dirName):
assume = customAssumeRules[dirName]
if assume:
assumeAbs = os.path.abspath(assume)
compileFile = assumeAbs
matches = [i for i in compileCommands if i["file"] == compileFile]
if not len(matches):
print("WARNING: no compile commands for '" + path + "' (assumed filename: '" + assume + "'")
continue
else:
print("WARNING: no compile commands for '" + path + "'")
continue
_, _, args = matches[0]["command"].partition(" ")
if assume:
args = args.replace(assumeAbs, "-x c++ " + pathAbs)
if not removefwdd and not headersfwd:
# Do not suggest forward declarations in headers
invocation = "include-what-you-use -Xiwyu --no_fwd_decls -Xiwyu --max_line_length=200 " + args
# Suggest headers' replacement with forward declarations
elif headersfwd:
invocation = "include-what-you-use -Xiwyu --cxx17ns -Xiwyu --max_line_length=200 " + args
# In --fwdecl mode we ask for fw declaration removal suggestions.
# In this mode obsolete fw declarations are suggested for removal.
# Later we ignore the header removal suggestions, which may be
# there because of possibility of replacement with fw declarations
# but those and our CI are not reliable enough yet for use
else:
invocation = "include-what-you-use -Xiwyu --cxx17ns -Xiwyu --max_line_length=200 " + args
if debug:
print(invocation)
sys.exit(0)
task_queue.put((invocation, moduleRules))
task_queue.join()
if len(failed_files):
return_code = 1
except KeyboardInterrupt:
print('\nCtrl-C detected, goodbye.')
os.kill(0, 9)
sys.exit(return_code)
def main(argv):
parser = argparse.ArgumentParser(description='Check source files for unneeded includes.')
parser.add_argument('Files' , nargs='*',
help='The files to be checked')
parser.add_argument('--recursive', metavar='DIR', nargs=1, type=str,
help='Recursively search a directory for source files to check')
hgroup = parser.add_mutually_exclusive_group()
hgroup.add_argument('--headers', action='store_true',
help='Check header files. If omitted, check source files. Use with --recursive.')
hgroup.add_argument('--headersfwd', action='store_true',
help='Check header files, suggest fw declaration replacements. If omitted, check source files. Use with --recursive.')
parser.add_argument('--noexclude', action='store_true',
help='Ignore excludelist. Useful to check whether its exclusions are still all valid.')
parser.add_argument('--ns', action='store_true',
help='Warn about unused "using namespace" statements. '
'Removing these may uncover more removable headers '
'in a subsequent normal run')
parser.add_argument('--finderrors', action='store_true',
help='Report IWYU failures when it returns with -1 error code. '
'Use only for debugging this script!')
parser.add_argument('-d', action='store_true',
help='Print the full IWYU invocation of a given file. '
'Use only for debugging this script!')
parser.add_argument('--fwdecl', action='store_true',
help='Suggest removal of obsolete forward declarations')
parser.add_argument('--sanitycheck', action='store_true',
help='Sanity check of IwyuFilter_foo.yaml of a module')
args = parser.parse_args()
if not len(argv):
parser.print_help()
return
list_of_files = []
if args.recursive:
for root, dirs, files in os.walk(args.recursive[0]):
for file in files:
if args.headers:
if not args.fwdecl:
if (file.endswith(".hxx") or file.endswith(".hrc") or file.endswith(".h")):
list_of_files.append(os.path.join(root,file))
else:
# In fwdecl mode don't check hrc files as they contain a lot of fw declarations
# used in defines and iwyu (0.21 at least) can not yet understand those properly
if (file.endswith(".hxx") or file.endswith(".h")):
list_of_files.append(os.path.join(root,file))
elif args.headersfwd:
if (file.endswith(".hxx") or file.endswith(".hrc") or file.endswith(".h")):
# Peek inside the file to check for code behind #ifdef or similar
# This is the fragile part, skip forward declaration suggestions for such
p1 = subprocess.Popen(['git', 'grep', '-v', '-e', 'INCLUDED', os.path.join(root,file) ], stdout=subprocess.PIPE)
p2 = subprocess.Popen(['grep', '-e', '#if'], stdin=p1.stdout, stdout=subprocess.PIPE)
p1.stdout.close()
output, _ = p2.communicate()
if not output:
list_of_files.append(os.path.join(root,file))
else:
continue
else:
if (file.endswith(".cxx") or file.endswith(".c")):
list_of_files.append(os.path.join(root,file))
else:
list_of_files = args.Files
try:
with open("compile_commands.json", 'r') as compileCommandsSock:
compileCommands = json.load(compileCommandsSock)
except FileNotFoundError:
print ("File 'compile_commands.json' does not exist, please run:\nmake vim-ide-integration")
sys.exit(-1)
# quickly sanity check whether files with exceptions in yaml still exists
# only check for the module of the very first filename passed
# Verify there are files selected for checking, with --recursive it
# may happen that there are in fact no C/C++ files in a module directory
if not list_of_files:
print("No files found to check!")
sys.exit(-2)
if args.sanitycheck:
moduleName = sorted(list_of_files)[0].split("/")[0]
rulePath = os.path.join(moduleName, "IwyuFilter_" + moduleName + ".yaml")
moduleRules = {}
if os.path.exists(rulePath):
moduleRules = yaml.full_load(open(rulePath))
# Check for duplicate file name entries in the yaml file
p1 = subprocess.Popen(['git', 'grep', '-h', ':$', rulePath ], stdout=subprocess.PIPE)
p2 = subprocess.Popen(['sort'], stdin=p1.stdout, stdout=subprocess.PIPE)
p3 = subprocess.Popen(['uniq', '-d'], stdin=p2.stdout, stdout=subprocess.PIPE)
p1.stdout.close()
p2.stdout.close()
output, _ = p3.communicate()
if output:
print("WARNING: The following files have duplicate entries - Please merge them - in:", rulePath, "\n", re.sub(r'[: ]+', '', output.decode()).strip())
if "excludelist" in moduleRules.keys():
excludelistRules = moduleRules["excludelist"]
for pathname in excludelistRules.keys():
file = pathlib.Path(pathname)
if not file.exists():
print("WARNING: File listed in " + rulePath + " no longer exists: " + pathname)
# If the file mentioned in the IwyuFilter file exists,
# check that the excluded files / forward declarations are still present
# to avoid stale exclusion rules lingering forever
else:
try:
for exclusion in excludelistRules[pathname]:
# class names behind namespace prefixes may be in separate lines
c=re.match(r"(?:namespace.*{ )(class.*|struct .*); }.*", exclusion)
if c:
classname=c.group(1)
p = subprocess.run(["git", "grep", "-q", "-e", "#include <"+exclusion, "-e", exclusion, "-e", classname, file])
if p.returncode == 1 :
print("WARNING:", file, ":", exclusion, "line is not present anymore, remove it from:", rulePath)
else:
p = subprocess.run(["git", "grep", "-q", "-e", "#include <"+exclusion, "-e", exclusion, file])
if p.returncode == 1 :
print("WARNING:", file, ":", exclusion, "line is not present anymore, remove it from:", rulePath)
except TypeError:
print("WARNING: no exclusions are defined for:", file)
sys.exit(0)
tidy(compileCommands, paths=list_of_files, noexclude=args.noexclude, checknamespaces=args.ns, finderrors=args.finderrors, removefwdd=args.fwdecl, debug=args.d, headersfwd=args.headersfwd)
if __name__ == '__main__':
main(sys.argv[1:])
# vim:set shiftwidth=4 softtabstop=4 expandtab: