Skip to content

Commit 2b9a00e

Browse files
ZdravkoDsawenzel
authored andcommitted
Add namespace naming check. Add unit test base files.
* Add namespace naming check. * Add unit test base files and unit test for namespace check.
1 parent 50cbf26 commit 2b9a00e

13 files changed

+541
-7
lines changed

add_new_check.py

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ def adapt_cmake(module_path, check_name_camel):
2626
# Figure out whether this check already exists.
2727
for line in lines:
2828
if line.strip() == cpp_file:
29+
print "cpp file already exists!", cpp_file
2930
return False
3031

3132
print('Updating %s...' % filename)
@@ -191,9 +192,14 @@ def adapt_module(module_path, module, check_name, check_name_camel):
191192

192193
# Adds a test for the check.
193194
def write_test(module_path, module, check_name):
195+
194196
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+
filepath = os.path.join(module_path, '../test/')
198+
if not os.path.exists(filepath):
199+
os.makedirs(filepath)
200+
201+
filename = os.path.normpath(os.path.join(filepath, check_name_dashes + '.cpp'))
202+
197203
print('Creating %s...' % filename)
198204
with open(filename, 'wb') as f:
199205
f.write("""// RUN: %%check_clang_tidy %%s %(check_name_dashes)s %%t
@@ -215,8 +221,21 @@ def write_test(module_path, module, check_name):
215221

216222
# Recreates the list of checks in the docs/clang-tidy/checks directory.
217223
def update_checks_list(clang_tidy_path):
218-
docs_dir = os.path.join(clang_tidy_path, '../docs/clang-tidy/checks')
224+
docs_dir = os.path.join(clang_tidy_path, './docs/clang-tidy/checks')
219225
filename = os.path.normpath(os.path.join(docs_dir, 'list.rst'))
226+
227+
if not os.path.exists(filename):
228+
os.mknod(filename)
229+
print 'Creating file ', filename
230+
with open(filename, 'w') as f:
231+
f.write('.. title:: clang-tidy - Clang-Tidy Checks\n')
232+
f.write('\n')
233+
f.write('Clang-Tidy Checks\n')
234+
f.write('=========================\n')
235+
f.write('\n')
236+
f.write('.. toctree::\n')
237+
f.write('\n')
238+
220239
with open(filename, 'r') as f:
221240
lines = f.readlines()
222241
doc_files = filter(lambda s: s.endswith('.rst') and s != 'list.rst',
@@ -249,8 +268,12 @@ def format_link(doc_file):
249268
# Adds a documentation for the check.
250269
def write_docs(module_path, module, check_name):
251270
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'))
271+
filepath = os.path.join(module_path, '../docs/clang-tidy/checks/')
272+
filename = os.path.normpath(os.path.join(filepath, check_name_dashes + '.rst'))
273+
274+
if not os.path.exists(filepath):
275+
os.makedirs(filepath)
276+
254277
print('Creating %s...' % filename)
255278
with open(filename, 'wb') as f:
256279
f.write(""".. title:: clang-tidy - %(check_name_dashes)s
@@ -290,8 +313,12 @@ def main():
290313
clang_tidy_path = os.path.dirname(sys.argv[0])
291314
module_path = os.path.join(clang_tidy_path, module)
292315

316+
if not os.path.exists(module_path):
317+
os.makedirs(module_path)
318+
293319
if not adapt_cmake(module_path, check_name_camel):
294320
return
321+
295322
write_header(module_path, module, check_name, check_name_camel)
296323
write_implementation(module_path, module, check_name_camel)
297324
adapt_module(module_path, module, check_name, check_name_camel)

aliceO2/AliceO2TidyModule.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "../ClangTidyModule.h"
1212
#include "../ClangTidyModuleRegistry.h"
1313
#include "MemberNamesCheck.h"
14+
#include "NamespaceNamingCheck.h"
1415
#include "SizeofCheck.h"
1516

1617
namespace clang {
@@ -20,7 +21,10 @@ namespace aliceO2 {
2021
class AliceO2Module : public ClangTidyModule {
2122
public:
2223
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
23-
CheckFactories.registerCheck<MemberNamesCheck>("aliceO2-member-name");
24+
CheckFactories.registerCheck<MemberNamesCheck>(
25+
"aliceO2-member-name");
26+
CheckFactories.registerCheck<NamespaceNamingCheck>(
27+
"aliceO2-namespace-naming");
2428
CheckFactories.registerCheck<SizeofCheck>(
2529
"aliceO2-SizeOf");
2630
}

aliceO2/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ set(LLVM_LINK_COMPONENTS support)
33
add_clang_library(clangTidyAliceO2Module
44
AliceO2TidyModule.cpp
55
MemberNamesCheck.cpp
6+
NamespaceNamingCheck.cpp
67
SizeofCheck.cpp
78

89
LINK_LIBS

aliceO2/NamespaceNamingCheck.cpp

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
//===--- NamespaceNamingCheck.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 "NamespaceNamingCheck.h"
11+
#include "clang/AST/ASTContext.h"
12+
#include "clang/ASTMatchers/ASTMatchFinder.h"
13+
#include <regex>
14+
#include <string>
15+
#include <ctype.h>
16+
17+
using namespace clang::ast_matchers;
18+
19+
namespace clang {
20+
namespace tidy {
21+
namespace aliceO2 {
22+
23+
const std::string VALID_NAME_REGEX = "([a-z]+_)*[a-z]+";
24+
25+
void NamespaceNamingCheck::registerMatchers(MatchFinder *Finder) {
26+
const auto validNameMatch = matchesName( std::string("::") + VALID_NAME_REGEX + "$" );
27+
28+
// matches namespace declarations that have invalid name
29+
Finder->addMatcher(namespaceDecl( unless( validNameMatch ) ).bind("namespace-decl"), this);
30+
// matches usage of namespace
31+
Finder->addMatcher(nestedNameSpecifierLoc(loc(nestedNameSpecifier(specifiesNamespace(
32+
unless( validNameMatch ) )))).bind("namespace-usage"), this );
33+
// matches "using namespace" declarations
34+
Finder->addMatcher(usingDirectiveDecl().bind("using-namespace"), this);
35+
}
36+
37+
void NamespaceNamingCheck::check(const MatchFinder::MatchResult &Result) {
38+
const auto *MatchedNamespaceDecl = Result.Nodes.getNodeAs<NamespaceDecl>("namespace-decl");
39+
if( MatchedNamespaceDecl )
40+
{
41+
std::string newName(MatchedNamespaceDecl->getDeclName().getAsString());
42+
43+
fixNamespaceName(newName);
44+
45+
diag(MatchedNamespaceDecl->getLocation(), "namespace %0 does not follow the underscore convention")
46+
<< MatchedNamespaceDecl
47+
<< FixItHint::CreateReplacement(MatchedNamespaceDecl->getLocation(), newName);
48+
}
49+
50+
const auto *MatchedNamespaceLoc = Result.Nodes.getNodeAs<NestedNameSpecifierLoc>("namespace-usage");
51+
if( MatchedNamespaceLoc )
52+
{
53+
const auto *AsNamespace = MatchedNamespaceLoc->getNestedNameSpecifier()->getAsNamespace();
54+
std::string newName(AsNamespace->getDeclName().getAsString());
55+
56+
fixNamespaceName(newName);
57+
58+
diag(MatchedNamespaceLoc->getLocalBeginLoc(), "namespace %0 does not follow the underscore convention")
59+
<< AsNamespace
60+
<< FixItHint::CreateReplacement(MatchedNamespaceLoc->getLocalBeginLoc(), newName);
61+
}
62+
63+
const auto *MatchedUsingNamespace = Result.Nodes.getNodeAs<UsingDirectiveDecl>("using-namespace");
64+
if( MatchedUsingNamespace )
65+
{
66+
std::string newName(MatchedUsingNamespace->getNominatedNamespace()->getDeclName().getAsString());
67+
68+
if( std::regex_match(newName, std::regex(VALID_NAME_REGEX)) )
69+
{
70+
return;
71+
}
72+
73+
fixNamespaceName(newName);
74+
75+
diag(MatchedUsingNamespace->getLocation(), "namespace %0 does not follow the underscore convention")
76+
<< MatchedUsingNamespace->getNominatedNamespace()
77+
<< FixItHint::CreateReplacement(MatchedUsingNamespace->getLocation(), newName);
78+
}
79+
}
80+
81+
void NamespaceNamingCheck::fixNamespaceName(std::string &name)
82+
{
83+
for(int i=0; i<name.size(); i++) {
84+
if(isupper(name[i])) {
85+
if(i != 0 && name[i-1] != '_') {
86+
name.insert(i, "_");
87+
i++;
88+
}
89+
name[i] = tolower(name[i]);
90+
}
91+
}
92+
}
93+
94+
} // namespace aliceO2
95+
} // namespace tidy
96+
} // namespace clang

aliceO2/NamespaceNamingCheck.h

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//===--- NamespaceNamingCheck.h - clang-tidy---------------------*- C++ -*-===//
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+
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ALICEO2_NAMESPACE_NAMING_H
11+
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ALICEO2_NAMESPACE_NAMING_H
12+
13+
#include "../ClangTidy.h"
14+
15+
namespace clang {
16+
namespace tidy {
17+
namespace aliceO2 {
18+
19+
/// FIXME: Write a short description.
20+
///
21+
/// For the user-facing documentation see:
22+
/// http://clang.llvm.org/extra/clang-tidy/checks/aliceO2-namespace-naming.html
23+
class NamespaceNamingCheck : public ClangTidyCheck {
24+
public:
25+
NamespaceNamingCheck(StringRef Name, ClangTidyContext *Context)
26+
: ClangTidyCheck(Name, Context) {}
27+
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
28+
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
29+
30+
void fixNamespaceName(std::string &name);
31+
};
32+
33+
} // namespace aliceO2
34+
} // namespace tidy
35+
} // namespace clang
36+
37+
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_ALICEO2_NAMESPACE_NAMING_H

check_clang_tidy.py

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
#!/usr/bin/env python
2+
#
3+
#===- check_clang_tidy.py - ClangTidy Test Helper ------------*- 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+
# This script is used for unit tests.
13+
# Usage:
14+
# ./check_clang_tidy.py <test_file_name>.cpp '<check_names>'
15+
# Examples:
16+
# ./check_clang_tidy.py test/aliceO2-aliceO2-namespace-naming.cpp 'aliceO2*'
17+
# or
18+
# ./check_clang_tidy.py test/aliceO2-aliceO2-namespace-naming.cpp 'aliceO2-namespace-naming'
19+
#
20+
21+
22+
r"""
23+
ClangTidy Test Helper
24+
=====================
25+
26+
This script runs clang-tidy in fix mode and verify fixes, messages or both.
27+
28+
Usage:
29+
check_clang_tidy.py [-resource-dir <resource-dir>] \
30+
<source-file> <check-name> \
31+
-- [optional clang-tidy arguments]
32+
33+
Example:
34+
// RUN: %check_clang_tidy %s llvm-include-order %t -- -- -isystem %S/Inputs
35+
"""
36+
37+
import argparse
38+
import re
39+
import subprocess
40+
import sys
41+
42+
43+
def write_file(file_name, text):
44+
with open(file_name, 'w') as f:
45+
f.write(text)
46+
f.truncate()
47+
48+
def main():
49+
parser = argparse.ArgumentParser()
50+
parser.add_argument('-resource-dir')
51+
parser.add_argument('input_file_name')
52+
parser.add_argument('check_name')
53+
54+
args, extra_args = parser.parse_known_args()
55+
56+
resource_dir = args.resource_dir
57+
input_file_name = args.input_file_name
58+
check_name = args.check_name
59+
60+
extension = '.cpp'
61+
if (input_file_name.endswith('.c')):
62+
extension = '.c'
63+
if (input_file_name.endswith('.hpp')):
64+
extension = '.hpp'
65+
66+
temp_file_name = input_file_name.replace(extension, '_result'+extension)
67+
68+
clang_tidy_extra_args = extra_args
69+
if len(clang_tidy_extra_args) == 0:
70+
clang_tidy_extra_args = ['--', '--std=c++11'] if extension == '.cpp' \
71+
else ['--']
72+
if resource_dir is not None:
73+
clang_tidy_extra_args.append('-resource-dir=%s' % resource_dir)
74+
75+
with open(input_file_name, 'r') as input_file:
76+
input_text = input_file.read()
77+
78+
has_check_fixes = input_text.find('CHECK-FIXES') >= 0
79+
has_check_messages = input_text.find('CHECK-MESSAGES') >= 0
80+
81+
if not has_check_fixes and not has_check_messages:
82+
sys.exit('Neither CHECK-FIXES nor CHECK-MESSAGES found in the input')
83+
84+
# Remove the contents of the CHECK lines to avoid CHECKs matching on
85+
# themselves. We need to keep the comments to preserve line numbers while
86+
# avoiding empty lines which could potentially trigger formatting-related
87+
# checks.
88+
cleaned_test = re.sub('// *CHECK-[A-Z-]*:[^\r\n]*', '//', input_text)
89+
90+
write_file(temp_file_name, cleaned_test)
91+
92+
original_file_name = temp_file_name + ".orig"
93+
write_file(original_file_name, cleaned_test)
94+
95+
args = ['O2codecheck', temp_file_name, '-fix', '--checks=-*,' + check_name] + \
96+
clang_tidy_extra_args
97+
print('Running ' + repr(args) + '...')
98+
99+
try:
100+
clang_tidy_output = \
101+
subprocess.check_output(args, stderr=subprocess.STDOUT).decode()
102+
except subprocess.CalledProcessError as e:
103+
clang_tidy_output = e.output.decode()
104+
105+
try:
106+
diff_output = subprocess.check_output(
107+
['diff', '-u', original_file_name, temp_file_name],
108+
stderr=subprocess.STDOUT)
109+
except subprocess.CalledProcessError as e:
110+
diff_output = e.output
111+
112+
print('------------------------------ Fixes -----------------------------\n' +
113+
diff_output.decode() +
114+
'\n------------------------------------------------------------------')
115+
116+
if has_check_fixes:
117+
try:
118+
subprocess.check_output(
119+
['FileCheck', '-input-file=' + temp_file_name, input_file_name,
120+
'-check-prefix=CHECK-FIXES', '-strict-whitespace'],
121+
stderr=subprocess.STDOUT)
122+
except subprocess.CalledProcessError as e:
123+
print('FileCheck failed:\n' + e.output.decode())
124+
raise
125+
126+
if has_check_messages:
127+
messages_file = temp_file_name + '.msg'
128+
write_file(messages_file, clang_tidy_output)
129+
try:
130+
subprocess.check_output(
131+
['FileCheck', '-input-file=' + messages_file, input_file_name,
132+
'-check-prefix=CHECK-MESSAGES',
133+
'-implicit-check-not={{warning|error}}:'],
134+
stderr=subprocess.STDOUT)
135+
except subprocess.CalledProcessError as e:
136+
print('FileCheck failed:\n' + e.output.decode())
137+
raise
138+
139+
if __name__ == '__main__':
140+
main()
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
.. title:: clang-tidy - aliceO2-members-name
2+
3+
aliceO2-members-name
4+
============
5+
6+
Namespace names follow underscore convention and start with a lower case letter: my_namespace.
7+
8+
Additional Info:
9+
10+
namespace my_namespace
11+
{
12+
13+
void MyClass::doSomething()
14+
{
15+
...
16+
}
17+
}

0 commit comments

Comments
 (0)