Skip to content

Commit 9dd47f5

Browse files
committed
Initial commit
* Initial commit of O2codercheck tool * Code based on clang-tidy but in a standalone form * We are relying on an existing installation of llvm + clang, to reuse as much as possible existing code but allowing to develop code outside the clang source tree
0 parents  commit 9dd47f5

File tree

12 files changed

+1551
-0
lines changed

12 files changed

+1551
-0
lines changed

CMakeLists.txt

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
cmake_minimum_required(VERSION 3.4.3)
2+
3+
4+
if( CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR )
5+
project(O2CodeChecker)
6+
7+
8+
# find clang + llvm
9+
find_package(Clang REQUIRED)
10+
11+
if( LLVM_FOUND )
12+
list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}")
13+
include(AddLLVM)
14+
15+
# set the compiler flags to match llvm
16+
include(HandleLLVMOptions)
17+
endif()
18+
19+
# Make sure that our source directory is on the current cmake module path so that
20+
# we can include cmake files from this directory.
21+
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
22+
23+
# include Clang macros (unfortunately they are not part of the cmake installation)
24+
include(AddClang)
25+
26+
# add include directories
27+
include_directories(${LLVM_INCLUDE_DIRS})
28+
29+
set(LLVM_LINK_COMPONENTS
30+
Support
31+
)
32+
33+
# the main executable
34+
add_subdirectory(tool)
35+
36+
# the specific aliceO2 checker code
37+
add_subdirectory(aliceO2)
38+
39+
40+
endif()

add_new_check.py

Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
#!/usr/bin/env python
2+
#
3+
#===- add_new_check.py - clang-tidy check generator ----------*- python -*--===#
4+
#
5+
# The LLVM Compiler Infrastructure
6+
#
7+
# This file is distributed under the University of Illinois Open Source
8+
# License. See LICENSE.TXT for details.
9+
#
10+
#===------------------------------------------------------------------------===#
11+
12+
import os
13+
import re
14+
import sys
15+
16+
17+
# Adapts the module's CMakelist file. Returns 'True' if it could add a new entry
18+
# and 'False' if the entry already existed.
19+
def adapt_cmake(module_path, check_name_camel):
20+
filename = os.path.join(module_path, 'CMakeLists.txt')
21+
with open(filename, 'r') as f:
22+
lines = f.readlines()
23+
24+
cpp_file = check_name_camel + '.cpp'
25+
26+
# Figure out whether this check already exists.
27+
for line in lines:
28+
if line.strip() == cpp_file:
29+
return False
30+
31+
print('Updating %s...' % filename)
32+
with open(filename, 'wb') as f:
33+
cpp_found = False
34+
file_added = False
35+
for line in lines:
36+
cpp_line = line.strip().endswith('.cpp')
37+
if (not file_added) and (cpp_line or cpp_found):
38+
cpp_found = True
39+
if (line.strip() > cpp_file) or (not cpp_line):
40+
f.write(' ' + cpp_file + '\n')
41+
file_added = True
42+
f.write(line)
43+
44+
return True
45+
46+
47+
# Adds a header for the new check.
48+
def write_header(module_path, module, check_name, check_name_camel):
49+
check_name_dashes = module + '-' + check_name
50+
filename = os.path.join(module_path, check_name_camel) + '.h'
51+
print('Creating %s...' % filename)
52+
with open(filename, 'wb') as f:
53+
header_guard = ('LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_' + module.upper() + '_'
54+
+ check_name.upper().replace('-', '_') + '_H')
55+
f.write('//===--- ')
56+
f.write(os.path.basename(filename))
57+
f.write(' - clang-tidy')
58+
f.write('-' * max(0, 43 - len(os.path.basename(filename))))
59+
f.write('*- C++ -*-===//')
60+
f.write("""
61+
//
62+
// The LLVM Compiler Infrastructure
63+
//
64+
// This file is distributed under the University of Illinois Open Source
65+
// License. See LICENSE.TXT for details.
66+
//
67+
//===----------------------------------------------------------------------===//
68+
69+
#ifndef %(header_guard)s
70+
#define %(header_guard)s
71+
72+
#include "../ClangTidy.h"
73+
74+
namespace clang {
75+
namespace tidy {
76+
namespace %(module)s {
77+
78+
/// FIXME: Write a short description.
79+
///
80+
/// For the user-facing documentation see:
81+
/// http://clang.llvm.org/extra/clang-tidy/checks/%(check_name_dashes)s.html
82+
class %(check_name)s : public ClangTidyCheck {
83+
public:
84+
%(check_name)s(StringRef Name, ClangTidyContext *Context)
85+
: ClangTidyCheck(Name, Context) {}
86+
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
87+
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
88+
};
89+
90+
} // namespace %(module)s
91+
} // namespace tidy
92+
} // namespace clang
93+
94+
#endif // %(header_guard)s
95+
""" % {'header_guard': header_guard,
96+
'check_name': check_name_camel,
97+
'check_name_dashes': check_name_dashes,
98+
'module': module})
99+
100+
101+
# Adds the implementation of the new check.
102+
def write_implementation(module_path, module, check_name_camel):
103+
filename = os.path.join(module_path, check_name_camel) + '.cpp'
104+
print('Creating %s...' % filename)
105+
with open(filename, 'wb') as f:
106+
f.write('//===--- ')
107+
f.write(os.path.basename(filename))
108+
f.write(' - clang-tidy')
109+
f.write('-' * max(0, 52 - len(os.path.basename(filename))))
110+
f.write('-===//')
111+
f.write("""
112+
//
113+
// The LLVM Compiler Infrastructure
114+
//
115+
// This file is distributed under the University of Illinois Open Source
116+
// License. See LICENSE.TXT for details.
117+
//
118+
//===----------------------------------------------------------------------===//
119+
120+
#include "%(check_name)s.h"
121+
#include "clang/AST/ASTContext.h"
122+
#include "clang/ASTMatchers/ASTMatchFinder.h"
123+
124+
using namespace clang::ast_matchers;
125+
126+
namespace clang {
127+
namespace tidy {
128+
namespace %(module)s {
129+
130+
void %(check_name)s::registerMatchers(MatchFinder *Finder) {
131+
// FIXME: Add matchers.
132+
Finder->addMatcher(functionDecl().bind("x"), this);
133+
}
134+
135+
void %(check_name)s::check(const MatchFinder::MatchResult &Result) {
136+
// FIXME: Add callback implementation.
137+
const auto *MatchedDecl = Result.Nodes.getNodeAs<FunctionDecl>("x");
138+
if (MatchedDecl->getName().startswith("awesome_"))
139+
return;
140+
diag(MatchedDecl->getLocation(), "function %%0 is insufficiently awesome")
141+
<< MatchedDecl
142+
<< FixItHint::CreateInsertion(MatchedDecl->getLocation(), "awesome_");
143+
}
144+
145+
} // namespace %(module)s
146+
} // namespace tidy
147+
} // namespace clang
148+
""" % {'check_name': check_name_camel,
149+
'module': module})
150+
151+
152+
# Modifies the module to include the new check.
153+
def adapt_module(module_path, module, check_name, check_name_camel):
154+
modulecpp = filter(lambda p: p.lower() == module.lower() + 'tidymodule.cpp',
155+
os.listdir(module_path))[0]
156+
filename = os.path.join(module_path, modulecpp)
157+
with open(filename, 'r') as f:
158+
lines = f.readlines()
159+
160+
print('Updating %s...' % filename)
161+
with open(filename, 'wb') as f:
162+
header_added = False
163+
header_found = False
164+
check_added = False
165+
check_decl = (' CheckFactories.registerCheck<' + check_name_camel +
166+
'>(\n "' + module + '-' + check_name + '");\n')
167+
168+
for line in lines:
169+
if not header_added:
170+
match = re.search('#include "(.*)"', line)
171+
if match:
172+
header_found = True
173+
if match.group(1) > check_name_camel:
174+
header_added = True
175+
f.write('#include "' + check_name_camel + '.h"\n')
176+
elif header_found:
177+
header_added = True
178+
f.write('#include "' + check_name_camel + '.h"\n')
179+
180+
if not check_added:
181+
if line.strip() == '}':
182+
check_added = True
183+
f.write(check_decl)
184+
else:
185+
match = re.search('registerCheck<(.*)>', line)
186+
if match and match.group(1) > check_name_camel:
187+
check_added = True
188+
f.write(check_decl)
189+
f.write(line)
190+
191+
192+
# Adds a test for the check.
193+
def write_test(module_path, module, check_name):
194+
check_name_dashes = module + '-' + check_name
195+
filename = os.path.normpath(os.path.join(module_path, '../../test/clang-tidy',
196+
check_name_dashes + '.cpp'))
197+
print('Creating %s...' % filename)
198+
with open(filename, 'wb') as f:
199+
f.write("""// RUN: %%check_clang_tidy %%s %(check_name_dashes)s %%t
200+
201+
// FIXME: Add something that triggers the check here.
202+
void f();
203+
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'f' is insufficiently awesome [%(check_name_dashes)s]
204+
205+
// FIXME: Verify the applied fix.
206+
// * Make the CHECK patterns specific enough and try to make verified lines
207+
// unique to avoid incorrect matches.
208+
// * Use {{}} for regular expressions.
209+
// CHECK-FIXES: {{^}}void awesome_f();{{$}}
210+
211+
// FIXME: Add something that doesn't trigger the check here.
212+
void awesome_f2();
213+
""" % {'check_name_dashes': check_name_dashes})
214+
215+
216+
# Recreates the list of checks in the docs/clang-tidy/checks directory.
217+
def update_checks_list(clang_tidy_path):
218+
docs_dir = os.path.join(clang_tidy_path, '../docs/clang-tidy/checks')
219+
filename = os.path.normpath(os.path.join(docs_dir, 'list.rst'))
220+
with open(filename, 'r') as f:
221+
lines = f.readlines()
222+
doc_files = filter(lambda s: s.endswith('.rst') and s != 'list.rst',
223+
os.listdir(docs_dir))
224+
doc_files.sort()
225+
226+
def format_link(doc_file):
227+
check_name = doc_file.replace('.rst', '')
228+
with open(os.path.join(docs_dir, doc_file), 'r') as doc:
229+
match = re.search('.*:http-equiv=refresh: \d+;URL=(.*).html.*',
230+
doc.read())
231+
if match:
232+
return ' %(check)s (redirects to %(target)s) <%(check)s>\n' % {
233+
'check': check_name,
234+
'target': match.group(1)
235+
}
236+
return ' %s\n' % check_name
237+
238+
checks = map(format_link, doc_files)
239+
240+
print('Updating %s...' % filename)
241+
with open(filename, 'wb') as f:
242+
for line in lines:
243+
f.write(line)
244+
if line.startswith('.. toctree::'):
245+
f.writelines(checks)
246+
break
247+
248+
249+
# Adds a documentation for the check.
250+
def write_docs(module_path, module, check_name):
251+
check_name_dashes = module + '-' + check_name
252+
filename = os.path.normpath(os.path.join(
253+
module_path, '../../docs/clang-tidy/checks/', check_name_dashes + '.rst'))
254+
print('Creating %s...' % filename)
255+
with open(filename, 'wb') as f:
256+
f.write(""".. title:: clang-tidy - %(check_name_dashes)s
257+
258+
%(check_name_dashes)s
259+
%(underline)s
260+
261+
FIXME: Describe what patterns does the check detect and why. Give examples.
262+
""" % {'check_name_dashes': check_name_dashes,
263+
'underline': '=' * len(check_name_dashes)})
264+
265+
266+
def main():
267+
if len(sys.argv) == 2 and sys.argv[1] == '--update-docs':
268+
update_checks_list(os.path.dirname(sys.argv[0]))
269+
return
270+
271+
if len(sys.argv) != 3:
272+
print """\
273+
Usage: add_new_check.py <module> <check>, e.g.
274+
add_new_check.py misc awesome-functions
275+
276+
Alternatively, run 'add_new_check.py --update-docs' to just update the list of
277+
documentation files."""
278+
279+
return
280+
281+
module = sys.argv[1]
282+
check_name = sys.argv[2]
283+
284+
if check_name.startswith(module):
285+
print 'Check name "%s" must not start with the module "%s". Exiting.' % (
286+
check_name, module)
287+
return
288+
check_name_camel = ''.join(map(lambda elem: elem.capitalize(),
289+
check_name.split('-'))) + 'Check'
290+
clang_tidy_path = os.path.dirname(sys.argv[0])
291+
module_path = os.path.join(clang_tidy_path, module)
292+
293+
if not adapt_cmake(module_path, check_name_camel):
294+
return
295+
write_header(module_path, module, check_name, check_name_camel)
296+
write_implementation(module_path, module, check_name_camel)
297+
adapt_module(module_path, module, check_name, check_name_camel)
298+
write_test(module_path, module, check_name)
299+
write_docs(module_path, module, check_name)
300+
update_checks_list(clang_tidy_path)
301+
print('Done. Now it\'s your turn!')
302+
303+
304+
if __name__ == '__main__':
305+
main()

aliceO2/AliceO2TidyModule.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//===--- AliceO2TidyModule.cpp - clang-tidy ----------------------------------===//
2+
//
3+
// The LLVM Compiler Infrastructure
4+
//
5+
// This file is distributed under the University of Illinois Open Source
6+
// License. See LICENSE.TXT for details.
7+
//
8+
//===----------------------------------------------------------------------===//
9+
10+
#include "../ClangTidy.h"
11+
#include "../ClangTidyModule.h"
12+
#include "../ClangTidyModuleRegistry.h"
13+
#include "MemberNamesCheck.h"
14+
15+
namespace clang {
16+
namespace tidy {
17+
namespace aliceO2 {
18+
19+
class AliceO2Module : public ClangTidyModule {
20+
public:
21+
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
22+
CheckFactories.registerCheck<MemberNamesCheck>("aliceO2-member-name");
23+
}
24+
};
25+
26+
} // namespace aliceO2
27+
28+
// Register the AliceO2TidyModule using this statically initialized variable.
29+
static ClangTidyModuleRegistry::Add<aliceO2::AliceO2Module>
30+
X("aliceO2-module", "Adds AliceO2 specific checks");
31+
32+
// This anchor is used to force the linker to link in the generated object file
33+
// and thus register the AliceO2Module.
34+
volatile int AliceO2ModuleAnchorSource = 0;
35+
36+
} // namespace tidy
37+
} // namespace clang

0 commit comments

Comments
 (0)