xaizek / self-inc-first (License: GPLv2+) (since 2018-12-07)
Clang-based standalone tool that checks for first included file being self include.
Commit 7c3bbed5d3902a74711ee229886d9d9d3ffbd253

Check for self-include
Author: xaizek
Author date (UTC): 2014-04-08 21:11
Committer name: xaizek
Committer date (UTC): 2014-04-08 21:11
Parent(s): 05d0449507bfb4e9dcf749d0881c0f27a5da69c9
Signing key:
Tree: 7a74892ebcf7494f33e4d1a144e04973c05813a5
File Lines added Lines deleted
CMakeLists.txt 2 1
README.md 2 2
src/IncludeFinderAction.cpp 91 2
src/path_utils.cpp 54 0
src/path_utils.hpp 14 8
tests/ambiguous.cpp 2 0
tests/wrong.c 1 0
tests/wrong.cpp 1 0
File CMakeLists.txt changed (mode: 100644) (index b8f7618..323601a)
... ... set(LLVM_USED_LIBS clangTooling clangBasic clangAST)
3 3
4 4 add_clang_executable(self-inc-first add_clang_executable(self-inc-first
5 5 src/self-inc-first.cpp src/self-inc-first.cpp
6 src/IncludeFinderAction.cpp)
6 src/IncludeFinderAction.cpp
7 src/path_utils.cpp)
7 8 target_link_libraries(self-inc-first clangTooling clangBasic clangASTMatchers) target_link_libraries(self-inc-first clangTooling clangBasic clangASTMatchers)
File README.md changed (mode: 100644) (index e11536c..d0987d3)
1 1 Brief description Brief description
2 2 ----------------- -----------------
3 3
4 Clang-based standalone tool that checks that self include for each of passed
5 source is the first one in the file.
4 Clang-based standalone tool that checks for first included file being self
5 include.
6 6
7 7 Building Building
8 8 -------- --------
File src/IncludeFinderAction.cpp changed (mode: 100644) (index a67d4a3..e7554bd)
25 25 #include <clang/Lex/Preprocessor.h> #include <clang/Lex/Preprocessor.h>
26 26 #include <clang/Lex/PPCallbacks.h> #include <clang/Lex/PPCallbacks.h>
27 27
28 #include <algorithm>
28 29 #include <iostream> #include <iostream>
30 #include <string>
31 #include <vector>
32 #include <utility>
33
34 #include "path_utils.hpp"
29 35
30 36 namespace namespace
31 37 { {
 
... ... public:
86 92 public: public:
87 93 inline clang::PPCallbacks * createPreprocessorCallbacks(); inline clang::PPCallbacks * createPreprocessorCallbacks();
88 94
95 inline void diagnoseAndReport();
96
89 97 virtual inline void InclusionDirective(clang::SourceLocation hashLoc, virtual inline void InclusionDirective(clang::SourceLocation hashLoc,
90 98 const clang::Token &includeTok, const clang::Token &includeTok,
91 99 clang::StringRef fileName, clang::StringRef fileName,
 
... ... public:
98 106
99 107 private: private:
100 108 const clang::CompilerInstance &compiler; const clang::CompilerInstance &compiler;
109 std::string name;
110
111 typedef std::pair<int, std::string> IncludeInfo;
112 typedef std::vector<IncludeInfo> Includes;
113 Includes includes;
101 114 }; };
102 115
103 116 inline inline
104 117 IncludeFinder::IncludeFinder(const clang::CompilerInstance &compiler) IncludeFinder::IncludeFinder(const clang::CompilerInstance &compiler)
105 118 : compiler(compiler) : compiler(compiler)
106 119 { {
120 const clang::FileID mainFile = compiler.getSourceManager().getMainFileID();
121 name = compiler.getSourceManager().getFileEntryForID(mainFile)->getName();
107 122 } }
108 123
109 124 inline clang::PPCallbacks * inline clang::PPCallbacks *
 
... ... IncludeFinder::createPreprocessorCallbacks()
112 127 return new CallbacksProxy(*this); return new CallbacksProxy(*this);
113 128 } }
114 129
130 typedef std::vector<std::string> KnownHdrExts;
131 static KnownHdrExts
132 getKnownHdrExts()
133 {
134 KnownHdrExts knownHdrExts;
135 knownHdrExts.push_back("h");
136 knownHdrExts.push_back("H");
137 knownHdrExts.push_back("hpp");
138 knownHdrExts.push_back("HPP");
139 knownHdrExts.push_back("hxx");
140 knownHdrExts.push_back("HXX");
141 return knownHdrExts;
142 }
143 static const KnownHdrExts KNOWN_HDR_EXTS = getKnownHdrExts();
144
145 template <typename C, typename T>
146 inline bool
147 contains(const C &c, const T &val)
148 {
149 return std::find(c.begin(), c.end(), val) != c.end();
150 }
151
152 inline void
153 IncludeFinder::diagnoseAndReport()
154 {
155 IncludeInfo selfInclude;
156
157 const std::string &tail = path_utils::extractTail(name);
158 const std::string &root = path_utils::extractRoot(tail);
159
160 typedef Includes::iterator It;
161 for (It it = includes.begin(); it != includes.end(); ++it) {
162 const std::string &hdrName = it->second;
163
164 const std::pair<std::string, std::string> nameParts =
165 path_utils::crackName(hdrName);
166
167 // if root parts are the same and extension is one of known
168 // TODO: maybe compare roots case insensitive
169 if (nameParts.first == root) {
170 if (contains(KNOWN_HDR_EXTS, nameParts.second)) {
171 if (selfInclude.second.empty()) {
172 selfInclude = *it;
173 } else {
174 // TODO: issue a warning about failed self include detection
175 std::cout << name
176 << ':'
177 << "ambiguous header name detection: "
178 << hdrName
179 << std::endl;
180 }
181 }
182 }
183 }
184
185
186 if (!selfInclude.second.empty() && !includes.empty()) {
187 if (includes[0] != selfInclude) {
188 std::cout << name
189 << ':'
190 << selfInclude.first
191 << ':'
192 << "should be the first include in the file"
193 << std::endl;
194 }
195 }
196
197 }
198
115 199 inline void inline void
116 200 IncludeFinder::InclusionDirective(clang::SourceLocation hashLoc, IncludeFinder::InclusionDirective(clang::SourceLocation hashLoc,
117 201 const clang::Token &includeTok, const clang::Token &includeTok,
 
... ... IncludeFinder::InclusionDirective(clang::SourceLocation hashLoc,
123 207 clang::StringRef relativePath, clang::StringRef relativePath,
124 208 const clang::Module *imported) const clang::Module *imported)
125 209 { {
126 if (compiler.getSourceManager().isInMainFile(hashLoc)) {
127 std::cout << fileName.str() << std::endl;
210 clang::SourceManager &sm = compiler.getSourceManager();
211
212 if (sm.isInMainFile(hashLoc)) {
213 const unsigned int lineNum = sm.getSpellingLineNumber(hashLoc);
214 includes.push_back(std::make_pair(lineNum, fileName));
128 215 } }
129 216 } }
130 217
 
... ... IncludeFinderAction::ExecuteAction()
139 226 ); );
140 227
141 228 clang::PreprocessOnlyAction::ExecuteAction(); clang::PreprocessOnlyAction::ExecuteAction();
229
230 includeFinder.diagnoseAndReport();
142 231 } }
File src/path_utils.cpp added (mode: 100644) (index 0000000..e944ee3)
1 /*
2 * self-inc-first
3 *
4 * Copyright (C) 2014 xaizek.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21 #include "path_utils.hpp"
22
23 #include <algorithm>
24 #include <string>
25
26 std::string
27 path_utils::extractTail(std::string path)
28 {
29 // normalize slashes
30 std::replace(path.begin(), path.end(), '\\', '/');
31
32 // leave only trailing path component
33 std::string::size_type tailPos = path.rfind('/');
34 tailPos = (tailPos == std::string::npos) ? 0UL : (tailPos + 1);
35 return std::string(path, tailPos);
36 }
37
38 std::pair<std::string, std::string>
39 path_utils::crackName(const std::string &name)
40 {
41 const std::string::size_type dotPos = name.rfind('.');
42 const std::string::size_type extPos = (dotPos == std::string::npos)
43 ? 0UL
44 : (dotPos + 1);
45
46 return std::make_pair(std::string(name, 0UL, dotPos),
47 std::string(name, extPos));
48 }
49
50 std::string
51 path_utils::extractRoot(const std::string &name)
52 {
53 return std::string(name, 0UL, name.rfind('.'));
54 }
File src/path_utils.hpp copied from file src/IncludeFinderAction.hpp (similarity 68%) (mode: 100644) (index 2a066f4..254f01d)
18 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
19 19 */ */
20 20
21 #ifndef SELF_INC_FIRST__INCLUDEFINDERACTION_HPP__
22 #define SELF_INC_FIRST__INCLUDEFINDERACTION_HPP__
21 #ifndef SELF_INC_FIRST__PATH_UTILS_HPP__
22 #define SELF_INC_FIRST__PATH_UTILS_HPP__
23 23
24 #include <clang/Frontend/FrontendActions.h>
24 #include <string>
25 #include <utility>
25 26
26 class IncludeFinderAction : public clang::PreprocessOnlyAction
27 namespace path_utils
27 28 { {
28 protected:
29 virtual void ExecuteAction();
30 };
31 29
32 #endif // SELF_INC_FIRST__INCLUDEFINDERACTION_HPP__
30 std::string extractTail(std::string path);
31
32 std::pair<std::string, std::string> crackName(const std::string &name);
33
34 std::string extractRoot(const std::string &name);
35
36 }
37
38 #endif // SELF_INC_FIRST__PATH_UTILS_HPP__
File tests/ambiguous.cpp added (mode: 100644) (index 0000000..1e7db3a)
1 #include "ambiguous.h"
2 #include "ambiguous.hpp"
File tests/wrong.c added (mode: 100644) (index 0000000..8d2ded0)
1 #include "does_not_exist.h"
File tests/wrong.cpp added (mode: 100644) (index 0000000..69bff57)
1 #include "does_not_exist.hpp"
Hints

Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://code.reversed.top/user/xaizek/self-inc-first

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@code.reversed.top/user/xaizek/self-inc-first

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a pull request:
... clone the repository ...
... make some changes and some commits ...
git push origin master