我可以想象,編寫一個clang插件檢查哪個函數被調用,以及其他人在過載集合中。我認爲,追蹤查找規則並找出爲什麼來自超載集合的候選者被拋棄以及爲什麼選擇的函數是過載集合中的最佳候選者,這是非常不同的。
我還沒有玩確定過載集合等,但是,下面是一個簡單的出發點:一個鏗鏘插件打印函數調用如果一個具有特定名稱的函數(當前硬編碼爲"foo"
)被發現。它還打印找到的過載。
我編譯代碼,並命令(很明顯,這些都存儲在一個文件make
)上運行它:
/opt/llvm-debug/bin/clang -I/usr/include/c++/4.2.1 -I/opt/llvm-debug/include -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS -g -fno-exceptions -fno-rtti -c -o MacOS/overloads.o overloads.cpp
/opt/llvm-debug/bin/clang -L/opt/llvm-debug/lib -Wl,-undefined,dynamic_lookup -dynamiclib -o MacOS/overloads.dylib MacOS/overloads.o
/opt/llvm-debug/bin/clang -cc1 -load MacOS/overloads.dylib -plugin overloads -plugin-arg-overloads argument -fexceptions tst.cpp
鐺的使用是建立與調試信息的版本:否則它不」 t似乎找到了一個調試符號。我應該找出如何直接構建一個工具,而不是從叮噹中運行。
#include <clang/Frontend/FrontendPluginRegistry.h>
#include <clang/Frontend/CompilerInstance.h>
#include <clang/Lex/Preprocessor.h>
#include <clang/Lex/PPCallbacks.h>
#include <clang/AST/ASTConsumer.h>
#include <clang/AST/AST.h>
#include <clang/AST/RecursiveASTVisitor.h>
#include <clang/Sema/Sema.h>
#include <clang/Sema/Lookup.h>
#include <llvm/Support/raw_ostream.h>
#include <string>
using namespace clang;
using namespace llvm;
typedef clang::CompilerInstance CI;
typedef clang::DeclGroupRef DGR;
typedef clang::DiagnosticsEngine DE;
// ----------------------------------------------------------------------------
namespace
{
struct Consumer: clang::ASTConsumer
{
Consumer(CI& c, std::string const& name): c_(&c), name_(name) {}
bool HandleTopLevelDecl(clang::DeclGroupRef DG);
CI* c_;
std::string name_;
};
}
// ----------------------------------------------------------------------------
struct Visitor: RecursiveASTVisitor<Visitor>
{
CI* c_;
std::string name_;
Visitor(CI* c, std::string const& name): c_(c), name_(name) {}
bool VisitCallExpr(CallExpr* d);
};
bool Visitor::VisitCallExpr(CallExpr* c) {
FunctionDecl* fun = c->getDirectCallee();
if (fun && fun->getNameAsString() == this->name_) {
SourceLocation w(c->getExprLoc());
DE &de(this->c_->getDiagnostics());
int id = de.getCustomDiagID(DE::Warning, "function call: %0");
int info = de.getCustomDiagID(DE::Note, "function called");
DiagnosticBuilder(de.Report(w, id))
<< fun->getNameAsString()
;
DiagnosticBuilder(de.Report(fun->getLocStart(), info))
<< fun->getNameAsString()
;
Sema& sema = this->c_->getSema();
LookupResult result(sema, fun->getDeclName(), w, Sema::LookupOrdinaryName);
DeclContext* context = fun->getDeclContext();
if (sema.LookupName(result, sema.getScopeForContext(context))) {
int over = de.getCustomDiagID(DE::Note, "function overload");
LookupResult::Filter filter = result.makeFilter();
while (filter.hasNext()) {
DiagnosticBuilder(de.Report(filter.next()->getLocStart(), over))
;
}
filter.done();
}
}
//else {
// // I think the callee was a function object or a function pointer
//}
return true;
}
void doDecl(Consumer* c, Decl* d) {
Visitor(c->c_, c->name_).TraverseDecl(d);
}
// ----------------------------------------------------------------------------
bool Consumer::HandleTopLevelDecl(DeclGroupRef DG) {
std::for_each(DG.begin(), DG.end(),
std::bind1st(std::ptr_fun(&doDecl), this));
return true;
}
// ----------------------------------------------------------------------------
namespace
{
class Plug
: public clang::PluginASTAction
{
protected:
ASTConsumer*
CreateASTConsumer(CompilerInstance& c, llvm::StringRef);
bool ParseArgs(clang::CompilerInstance const&,
std::vector<std::string> const&) {
return true;
}
};
}
ASTConsumer*
Plug::CreateASTConsumer(CompilerInstance& c, llvm::StringRef) {
return new Consumer(c, "foo");
}
static clang::FrontendPluginRegistry::Add<Plug>
registerPlugin("overloads", "report overloads of a function at a call");
該代碼並不漂亮,並不是真的在做你正在尋找的東西。但是,將函數聲明格式化得更好一點,可能需要調查一下對象爲什麼它不匹配等等,我認爲可以使代碼與您正在尋找的工具相當接近。
稍微偏離主題,但是當你不能穩定地預測將從重載的堆中調用什麼特定的函數時,這是一個糟糕的設計。你的重載版本應該有明顯的區別。 – Mikhail
@Mikhail我在預測正確的過載。它沒有被拾取,因爲它在一個const對象中調用了一個非const成員函數(我忘記了添加一個const超載...我的不好)。我知道,不要混用通用引用+重載......除非它是正確的事情。編譯器可以讓它更容易處理。 – gnzlbg