xaizek / unused-funcs (License: GPLv2+) (since 2018-12-07)
Clang-based standalone tool that detects unused external functions in a set of source files.
Commit 1da596c1a79d66e9031413e0c8d4875a59699c6e

modernize code
clang-format code and compact code
apply clang-tidy modernize checks
replace auto_ptr by unique_ptr
simplify code
Author: Raphael Knaus
Author date (UTC): 2018-01-12 22:22
Committer name: Raphael Knaus
Committer date (UTC): 2018-01-14 15:34
Parent(s): b6612602883413560479653fe053a84f19a7edcc
Signing key:
Tree: 4acf6348498635eb0b5daaadc973cba50fe730b5
File Lines added Lines deleted
src/Finder.cpp 70 108
src/Finder.hpp 12 22
src/FuncInfo.cpp 26 48
src/FuncInfo.hpp 22 29
src/RefInfo.cpp 14 7
src/RefInfo.hpp 11 11
src/unused-funcs.cpp 19 26
File src/Finder.cpp changed (mode: 100644) (index aa790ec..47e420f)
25 25 #include <string> #include <string>
26 26
27 27 #include <clang/AST/Decl.h> #include <clang/AST/Decl.h>
28
29 28 #include <clang/ASTMatchers/ASTMatchFinder.h> #include <clang/ASTMatchers/ASTMatchFinder.h>
30 29 #include <clang/ASTMatchers/ASTMatchers.h> #include <clang/ASTMatchers/ASTMatchers.h>
31 30
32 31 #include "FuncInfo.hpp" #include "FuncInfo.hpp"
33 32
33 namespace {
34
34 35 using namespace clang; using namespace clang;
35 36 using namespace clang::ast_matchers; using namespace clang::ast_matchers;
36 37
37 typedef std::map<std::string, FuncInfo> Funcs;
38
39 static DeclarationMatcher funcDecl = functionDecl().bind("func");
40 static StatementMatcher funcRef =
41 declRefExpr( // referencing a variable/declaration
42 to( // something that is ...
43 functionDecl( // ... a function
44
45 )
46 )
47 ).bind("ref"); // bind matched function ref to "ref" name
48
49 namespace
50 {
38 using Funcs = std::map<std::string, FuncInfo>;
51 39
52 class MatchHelper : public MatchFinder::MatchCallback
53 {
54 typedef MatchFinder::MatchResult Result;
40 class MatchHelper : public MatchFinder::MatchCallback {
41 using Result = MatchFinder::MatchResult;
55 42
56 43 public: public:
57 MatchHelper(std::map<std::string, FuncInfo> &funcs);
44 MatchHelper(Funcs &funcs);
58 45
59 public:
60 virtual void run(const Result &result);
46 void run(const Result &result) override;
61 47
62 48 private: private:
63 Funcs::iterator registerFunc(const Result &result,
64 const FunctionDecl *func) const;
49 Funcs::iterator registerFunc(const Result &result,
50 const FunctionDecl *func) const;
65 51
66 void registerRef(const Result &result, const DeclRefExpr *ref) const;
52 void registerRef(const Result &result, const DeclRefExpr *ref) const;
67 53
68 private:
69 Funcs &funcs;
54 Funcs &funcs;
70 55 }; };
71 56
72 MatchHelper::MatchHelper(std::map<std::string, FuncInfo> &funcs)
73 :funcs(funcs)
74 {
75 }
57 MatchHelper::MatchHelper(Funcs &funcs)
58 : funcs(funcs) {}
76 59
77 void
78 MatchHelper::run(const Result &result)
79 {
80 typedef FunctionDecl Func;
81 typedef DeclRefExpr Ref;
60 void MatchHelper::run(const Result &result) {
61 using Func = FunctionDecl;
62 using Ref = DeclRefExpr;
82 63
83 if (const Func *func = result.Nodes.getNodeAs<Func>("func")) {
84 static_cast<void>(registerFunc(result, func));
85 } else if (const Ref *ref = result.Nodes.getNodeAs<Ref>("ref")) {
86 registerRef(result, ref);
87 }
64 if (const auto func = result.Nodes.getNodeAs<Func>("func")) {
65 static_cast<void>(registerFunc(result, func));
66 } else if (const auto ref = result.Nodes.getNodeAs<Ref>("ref")) {
67 registerRef(result, ref);
68 }
88 69 } }
89 70
90 Funcs::iterator
91 MatchHelper::registerFunc(const Result &result, const FunctionDecl *func) const
92 {
93 if (!func->isExternallyVisible() || func->isMain()) {
94 return Funcs::iterator();
95 }
96
97 const Funcs::iterator it = funcs.find(func->getNameAsString());
98 if (it == funcs.end()) {
99 const std::string &name = func->getNameAsString();
100 FuncInfo info(func, result.SourceManager);
101 return funcs.insert(std::make_pair(name, info)).first;
102 } else {
103 it->second.processDeclaration(func, result.SourceManager);
104 return it;
105 }
71 Funcs::iterator MatchHelper::registerFunc(const Result &result,
72 const FunctionDecl *func) const {
73 if (!func->isExternallyVisible() || func->isMain()) {
74 return {};
75 }
76
77 const auto it = funcs.find(func->getNameAsString());
78 if (it == funcs.end()) {
79 const auto name = func->getNameAsString();
80 FuncInfo info(func, result.SourceManager);
81 return funcs.insert(std::make_pair(name, info)).first;
82 }
83 it->second.processDeclaration(func, result.SourceManager);
84 return it;
106 85 } }
107 86
108 void
109 MatchHelper::registerRef(const Result &result, const DeclRefExpr *ref) const
110 {
111 if (const FunctionDecl *func = ref->getDecl()->getAsFunction()) {
112 const Funcs::iterator it = registerFunc(result, func);
113 if (it != Funcs::iterator()) {
114 it->second.registerRef(ref, result.SourceManager);
115 }
87 void MatchHelper::registerRef(const Result &result,
88 const DeclRefExpr *ref) const {
89 if (const auto func = ref->getDecl()->getAsFunction()) {
90 const auto it = registerFunc(result, func);
91 if (it != Funcs::iterator()) {
92 it->second.registerRef(ref, result.SourceManager);
116 93 } }
94 }
117 95 } }
118 96
119 }
97 } // namespace
120 98
121 class Finder::Impl
122 {
99 class Finder::Impl {
123 100 public: public:
124 Impl();
125 ~Impl();
101 Impl();
102 ~Impl();
126 103
127 104 public: public:
128 MatchFinder & getMatchFinder();
105 MatchFinder &getMatchFinder();
129 106
130 107 private: private:
131 Funcs funcs;
132 MatchHelper helper;
133 MatchFinder matchFinder;
108 Funcs funcs;
109 MatchHelper helper;
110 MatchFinder matchFinder;
134 111 }; };
135 112
136 Finder::Impl::Impl()
137 :helper(funcs)
138 {
139 matchFinder.addMatcher(funcDecl, &helper);
140 matchFinder.addMatcher(funcRef, &helper);
113 Finder::Impl::Impl() : helper(funcs) {
114 const auto funcDecl = functionDecl().bind("func");
115 matchFinder.addMatcher(funcDecl, &helper);
116 const auto funcRef = declRefExpr(to(functionDecl())).bind("ref");
117 matchFinder.addMatcher(funcRef, &helper);
141 118 } }
142 119
143 Finder::Impl::~Impl()
144 {
145 for (Funcs::const_iterator cit = funcs.begin(); cit != funcs.end(); ++cit) {
146 const FuncInfo &funcInfo = cit->second;
147
148 if (funcInfo.isFullyDeclared()) {
149 if (funcInfo.isUnused()) {
150 std::cout << funcInfo << ":unused\n";
151 } else if (funcInfo.canBeMadeStatic()) {
152 std::cout << funcInfo << ":can be made static\n";
153 }
154 }
155 }
156 }
120 Finder::Impl::~Impl() {
121 for (auto & func : funcs) {
122 const auto &funcInfo = func.second;
157 123
158 Finder::MatchFinder &
159 Finder::Impl::getMatchFinder()
160 {
161 return matchFinder;
124 if (funcInfo.isFullyDeclared()) {
125 if (funcInfo.isUnused()) {
126 std::cout << funcInfo << ": unused\n";
127 } else if (funcInfo.canBeMadeStatic()) {
128 std::cout << funcInfo << ": can be made static\n";
129 }
130 }
131 }
162 132 } }
163 133
164 Finder::Finder()
165 :impl(new Impl())
166 {
167 }
134 MatchFinder &Finder::Impl::getMatchFinder() { return matchFinder; }
168 135
169 Finder::~Finder()
170 {
171 }
136 Finder::Finder() : impl(llvm::make_unique<Impl>()) {}
137 Finder::~Finder() = default;
172 138
173 Finder::MatchFinder &
174 Finder::getMatchFinder()
175 {
176 return impl->getMatchFinder();
177 }
139 MatchFinder &Finder::getMatchFinder() { return impl->getMatchFinder(); }
File src/Finder.hpp changed (mode: 100644) (index 818187d..f11568e)
23 23
24 24 #include <memory> #include <memory>
25 25
26 namespace clang
27 {
28 namespace ast_matchers
29 {
30 class MatchFinder;
31 }
32 }
33
34 class Finder
35 {
36 typedef clang::ast_matchers::MatchFinder MatchFinder;
37 26
38 public:
39 Finder();
40 ~Finder();
27 namespace clang {
28 namespace ast_matchers {
29 class MatchFinder;
30 } // namespace ast_matchers
31 } // namespace clang
32
41 33
34 class Finder {
42 35 public: public:
43 MatchFinder & getMatchFinder();
36 Finder();
37 ~Finder();
44 38
45 private:
46 // these operations are forbidden
47 Finder(const Finder &rhs);
48 Finder & operator=(const Finder &rhs);
39 clang::ast_matchers::MatchFinder &getMatchFinder();
49 40
50 41 private: private:
51 class Impl;
52
53 const std::auto_ptr<Impl> impl;
42 class Impl;
43 const std::unique_ptr<Impl> impl;
54 44 }; };
55 45
56 46 #endif // UNUSED_FUNCS__FINDER_HPP__ #endif // UNUSED_FUNCS__FINDER_HPP__
File src/FuncInfo.cpp changed (mode: 100644) (index c3057f0..0175e72)
20 20
21 21 #include "FuncInfo.hpp" #include "FuncInfo.hpp"
22 22
23 #include <clang/AST/Decl.h>
24 #include <clang/AST/Expr.h>
23 25 #include <clang/Basic/SourceLocation.h> #include <clang/Basic/SourceLocation.h>
24 26 #include <clang/Basic/SourceManager.h> #include <clang/Basic/SourceManager.h>
25 27
26 #include <clang/AST/Decl.h>
27 #include <clang/AST/Expr.h>
28 #include <iostream>
28 29
29 30 FuncInfo::FuncInfo(const clang::FunctionDecl *func, FuncInfo::FuncInfo(const clang::FunctionDecl *func,
30 31 const clang::SourceManager *sm) const clang::SourceManager *sm)
31 :name(func->getNameAsString())
32 ,lineNum(0U)
33 {
34 processDeclaration(func, sm);
32 : name(func->getNameAsString()), lineNum(0U) {
33 processDeclaration(func, sm);
35 34 } }
36 35
37 void
38 FuncInfo::processDeclaration(const clang::FunctionDecl *func,
39 const clang::SourceManager *sm)
40 {
41 if (isFullyDeclared() || !func->isThisDeclarationADefinition()) {
42 return;
43 }
36 void FuncInfo::processDeclaration(const clang::FunctionDecl *func,
37 const clang::SourceManager *sm) {
38 if (isFullyDeclared() || !func->isThisDeclarationADefinition()) {
39 return;
40 }
44 41
45 clang::FullSourceLoc fullLoc(func->getNameInfo().getBeginLoc(), *sm);
46 fileName = sm->getFilename(fullLoc);
47 lineNum = fullLoc.getSpellingLineNumber();
42 clang::FullSourceLoc fullLoc(func->getNameInfo().getBeginLoc(), *sm);
43 fileName = sm->getFilename(fullLoc);
44 lineNum = fullLoc.getSpellingLineNumber();
48 45 } }
49 46
50 bool
51 FuncInfo::isFullyDeclared() const
52 {
53 return lineNum != 0U;
54 }
47 bool FuncInfo::isFullyDeclared() const { return lineNum != 0U; }
55 48
56 void
57 FuncInfo::registerRef(const clang::DeclRefExpr *ref,
58 const clang::SourceManager *sm)
59 {
60 calls.push_back(RefInfo(ref, sm));
49 void FuncInfo::registerRef(const clang::DeclRefExpr *ref,
50 const clang::SourceManager *sm) {
51 calls.emplace_back(ref, sm);
61 52 } }
62 53
63 bool
64 FuncInfo::isUnused() const
65 {
66 return calls.empty();
67 }
54 bool FuncInfo::isUnused() const { return calls.empty(); }
68 55
69 bool
70 FuncInfo::canBeMadeStatic() const
71 {
72 typedef Refs::const_iterator Cit;
73 for (Cit cit = calls.begin(); cit != calls.end(); ++cit) {
74 if (!cit->isInThisUnit(fileName)) {
75 return false;
76 }
56 bool FuncInfo::canBeMadeStatic() const {
57 for (const auto & call : calls) {
58 if (!call.isInThisUnit(fileName)) {
59 return false;
77 60 } }
78 return true;
61 }
62 return true;
79 63 } }
80 64
81 std::ostream &
82 operator<<(std::ostream &os, const FuncInfo &fi)
83 {
84 return os << fi.fileName
85 << ':'
86 << fi.lineNum
87 << ':'
88 << fi.name;
65 std::ostream &operator<<(std::ostream &os, const FuncInfo &fi) {
66 return os << fi.fileName << ':' << fi.lineNum << ':' << fi.name;
89 67 } }
File src/FuncInfo.hpp changed (mode: 100644) (index ec6aeed..08d2e24)
27 27
28 28 #include "RefInfo.hpp" #include "RefInfo.hpp"
29 29
30 namespace clang
31 {
32 class FunctionDecl;
33 class DeclRefExpr;
34 class SourceManager;
35 }
36 30
37 class FuncInfo
38 {
39 friend std::ostream & operator<<(std::ostream &os, const FuncInfo &fi);
31 namespace clang {
32 class FunctionDecl;
33 class DeclRefExpr;
34 class SourceManager;
35 } // namespace clang
40 36
41 public:
42 FuncInfo(const clang::FunctionDecl *func, const clang::SourceManager *sm);
43
44 public:
45 void processDeclaration(const clang::FunctionDecl *func,
46 const clang::SourceManager *sm);
47
48 bool isFullyDeclared() const;
49 37
50 void registerRef(const clang::DeclRefExpr *ref,
51 const clang::SourceManager *sm);
38 class FuncInfo {
39 friend std::ostream &operator<<(std::ostream &os, const FuncInfo &fi);
52 40
53 bool isUnused() const;
41 public:
42 FuncInfo(const clang::FunctionDecl *func, const clang::SourceManager *sm);
54 43
55 bool canBeMadeStatic() const;
44 void processDeclaration(const clang::FunctionDecl *func,
45 const clang::SourceManager *sm);
46 bool isFullyDeclared() const;
47 void registerRef(const clang::DeclRefExpr *ref,
48 const clang::SourceManager *sm);
49 bool isUnused() const;
50 bool canBeMadeStatic() const;
56 51
57 52 private: private:
58 const std::string name;
59
60 std::string fileName;
61 unsigned int lineNum;
62
63 typedef std::vector<RefInfo> Refs;
64 Refs calls;
53 const std::string name;
54 std::string fileName;
55 unsigned int lineNum;
56 typedef std::vector<RefInfo> Refs;
57 Refs calls;
65 58 }; };
66 59
67 std::ostream & operator<<(std::ostream &os, const FuncInfo &fi);
60 std::ostream &operator<<(std::ostream &os, const FuncInfo &fi);
68 61
69 62 #endif // UNUSED_FUNCS__FUNCINFO_HPP__ #endif // UNUSED_FUNCS__FUNCINFO_HPP__
File src/RefInfo.cpp changed (mode: 100644) (index 94b50d8..ad880ba)
20 20
21 21 #include "RefInfo.hpp" #include "RefInfo.hpp"
22 22
23 #include <clang/AST/Expr.h>
23 24 #include <clang/Basic/SourceLocation.h> #include <clang/Basic/SourceLocation.h>
24 25 #include <clang/Basic/SourceManager.h> #include <clang/Basic/SourceManager.h>
25 26
26 #include <clang/AST/Expr.h>
27 27
28 RefInfo::RefInfo(const clang::DeclRefExpr *ref, const clang::SourceManager *sm)
29 {
28 namespace {
29 std::string getFilename(const clang::DeclRefExpr *ref,
30 const clang::SourceManager *sm) {
30 31 clang::FullSourceLoc fullLoc(ref->getExprLoc(), *sm); clang::FullSourceLoc fullLoc(ref->getExprLoc(), *sm);
31 fileName = sm->getFilename(fullLoc);
32 return sm->getFilename(fullLoc);
33 }
34 } // namespace
35
36
37 RefInfo::RefInfo(const clang::DeclRefExpr *ref,
38 const clang::SourceManager *sm)
39 : fileName(getFilename(ref, sm)) {
32 40 } }
33 41
34 bool RefInfo::isInThisUnit(const std::string& other) const
35 {
36 return other == fileName;
42 bool RefInfo::isInThisUnit(const std::string &other) const {
43 return other == fileName;
37 44 } }
File src/RefInfo.hpp changed (mode: 100644) (index 57a600c..979d26c)
23 23
24 24 #include <string> #include <string>
25 25
26 namespace clang
27 {
28 class DeclRefExpr;
29 class SourceManager;
30 }
31
32 class RefInfo
33 {
26
27 namespace clang {
28 class DeclRefExpr;
29 class SourceManager;
30 } // namespace clang
31
32
33 class RefInfo {
34 34 public: public:
35 RefInfo(const clang::DeclRefExpr *ref, const clang::SourceManager *sm);
35 RefInfo(const clang::DeclRefExpr *ref, const clang::SourceManager *sm);
36 36
37 37 public: public:
38 bool isInThisUnit(const std::string& other) const;
38 bool isInThisUnit(const std::string &other) const;
39 39
40 40 private: private:
41 std::string fileName;
41 const std::string fileName;
42 42 }; };
43 43
44 44 #endif // UNUSED_FUNCS__REFINFO_HPP__ #endif // UNUSED_FUNCS__REFINFO_HPP__
File src/unused-funcs.cpp changed (mode: 100644) (index c38d670..51addf9)
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 #include <llvm/Support/CommandLine.h>
22
23 #include <clang/Basic/Diagnostic.h>
24
25 21 #include <clang/ASTMatchers/ASTMatchFinder.h> #include <clang/ASTMatchers/ASTMatchFinder.h>
26
22 #include <clang/Basic/Diagnostic.h>
27 23 #include <clang/Tooling/CommonOptionsParser.h> #include <clang/Tooling/CommonOptionsParser.h>
28 24 #include <clang/Tooling/Tooling.h> #include <clang/Tooling/Tooling.h>
25 #include <llvm/Support/CommandLine.h>
29 26
30 27 #include "Finder.hpp" #include "Finder.hpp"
31 28
32 using namespace clang::tooling;
29 namespace ct = clang::tooling;
33 30
34 static llvm::cl::OptionCategory toolCategory("unused-funcs options");
31 namespace {
32 class CustomDiagnosticConsumer: public clang::DiagnosticConsumer {
33 public:
34 bool IncludeInDiagnosticCounts() const override { return false; }
35 };
36 } // namespace
35 37
36 static llvm::cl::extrahelp commonHelp(CommonOptionsParser::HelpMessage);
38 static llvm::cl::extrahelp commonHelp(ct::CommonOptionsParser::HelpMessage);
37 39
38 int
39 main(int argc, const char *argv[])
40 {
41 CommonOptionsParser optionsParser(argc, argv, toolCategory);
42 ClangTool tool(optionsParser.getCompilations(),
43 optionsParser.getSourcePathList());
40 int main(int argc, const char *argv[]) {
41 llvm::cl::OptionCategory toolCategory("unused-funcs options");
42 ct::CommonOptionsParser optionsParser(argc, argv, toolCategory);
43 ct::ClangTool tool(optionsParser.getCompilations(),
44 optionsParser.getSourcePathList());
44 45
45 class : public clang::DiagnosticConsumer
46 {
47 public:
48 virtual bool
49 IncludeInDiagnosticCounts() const
50 {
51 return false;
52 }
53 } diagConsumer;
54 tool.setDiagnosticConsumer(&diagConsumer);
46 CustomDiagnosticConsumer diagConsumer;
47 tool.setDiagnosticConsumer(&diagConsumer);
55 48
56 Finder finder;
57 return tool.run(newFrontendActionFactory(&finder.getMatchFinder()).get());
49 Finder finder;
50 return tool.run(ct::newFrontendActionFactory(&finder.getMatchFinder()).get());
58 51 } }
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/unused-funcs

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

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