xaizek / unused-funcs (License: GPLv2+) (since 2018-12-07)
Clang-based standalone tool that detects unused external functions in a set of source files.
<root> / src / Finder.cpp (aa790ec62db340b378e7ec1d2d3d2629617d8973) (4,291B) (mode 100644) [raw]
/*
 * unused-funcs
 *
 * Copyright (C) 2014 xaizek.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include "Finder.hpp"

#include <iostream>
#include <map>
#include <string>

#include <clang/AST/Decl.h>

#include <clang/ASTMatchers/ASTMatchFinder.h>
#include <clang/ASTMatchers/ASTMatchers.h>

#include "FuncInfo.hpp"

using namespace clang;
using namespace clang::ast_matchers;

typedef std::map<std::string, FuncInfo> Funcs;

static DeclarationMatcher funcDecl = functionDecl().bind("func");
static StatementMatcher funcRef =
    declRefExpr(              // referencing a variable/declaration
        to(                   // something that is ...
            functionDecl(     // ... a function

            )
        )
    ).bind("ref");            // bind matched function ref to "ref" name

namespace
{

class MatchHelper : public MatchFinder::MatchCallback
{
    typedef MatchFinder::MatchResult Result;

public:
    MatchHelper(std::map<std::string, FuncInfo> &funcs);

public:
    virtual void run(const Result &result);

private:
    Funcs::iterator registerFunc(const Result &result,
                                 const FunctionDecl *func) const;

    void registerRef(const Result &result, const DeclRefExpr *ref) const;

private:
    Funcs &funcs;
};

MatchHelper::MatchHelper(std::map<std::string, FuncInfo> &funcs)
    :funcs(funcs)
{
}

void
MatchHelper::run(const Result &result)
{
    typedef FunctionDecl Func;
    typedef DeclRefExpr Ref;

    if (const Func *func = result.Nodes.getNodeAs<Func>("func")) {
        static_cast<void>(registerFunc(result, func));
    } else if (const Ref *ref = result.Nodes.getNodeAs<Ref>("ref")) {
        registerRef(result, ref);
    }
}

Funcs::iterator
MatchHelper::registerFunc(const Result &result, const FunctionDecl *func) const
{
    if (!func->isExternallyVisible() || func->isMain()) {
        return Funcs::iterator();
    }

    const Funcs::iterator it = funcs.find(func->getNameAsString());
    if (it == funcs.end()) {
        const std::string &name = func->getNameAsString();
        FuncInfo info(func, result.SourceManager);
        return funcs.insert(std::make_pair(name, info)).first;
    } else {
        it->second.processDeclaration(func, result.SourceManager);
        return it;
    }
}

void
MatchHelper::registerRef(const Result &result, const DeclRefExpr *ref) const
{
    if (const FunctionDecl *func = ref->getDecl()->getAsFunction()) {
        const Funcs::iterator it = registerFunc(result, func);
        if (it != Funcs::iterator()) {
            it->second.registerRef(ref, result.SourceManager);
        }
    }
}

}

class Finder::Impl
{
public:
    Impl();
    ~Impl();

public:
    MatchFinder & getMatchFinder();

private:
    Funcs funcs;
    MatchHelper helper;
    MatchFinder matchFinder;
};

Finder::Impl::Impl()
    :helper(funcs)
{
    matchFinder.addMatcher(funcDecl, &helper);
    matchFinder.addMatcher(funcRef, &helper);
}

Finder::Impl::~Impl()
{
    for (Funcs::const_iterator cit = funcs.begin(); cit != funcs.end(); ++cit) {
        const FuncInfo &funcInfo = cit->second;

        if (funcInfo.isFullyDeclared()) {
            if (funcInfo.isUnused()) {
                std::cout << funcInfo << ":unused\n";
            } else if (funcInfo.canBeMadeStatic()) {
                std::cout << funcInfo << ":can be made static\n";
            }
        }
    }
}

Finder::MatchFinder &
Finder::Impl::getMatchFinder()
{
    return matchFinder;
}

Finder::Finder()
    :impl(new Impl())
{
}

Finder::~Finder()
{
}

Finder::MatchFinder &
Finder::getMatchFinder()
{
    return impl->getMatchFinder();
}
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