blob: 106f9833551cd6809f38950a5ca9b98b5b10370f [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "CheckTraceWrappersVisitor.h"
#include <vector>
#include "Config.h"
using namespace clang;
CheckTraceWrappersVisitor::CheckTraceWrappersVisitor(CXXMethodDecl* trace,
RecordInfo* info,
RecordCache* cache)
: trace_wrappers_(trace), info_(info), cache_(cache) {}
bool CheckTraceWrappersVisitor::VisitCallExpr(CallExpr* call) {
CheckTraceBaseCall(call);
return true;
}
bool CheckTraceWrappersVisitor::IsTraceWrappersCallName(
const std::string& name) {
// See CheckTraceVisitor::IsTraceCallName.
return name == trace_wrappers_->getName();
}
bool CheckTraceWrappersVisitor::CheckTraceBaseCall(CallExpr* call) {
// Checks for "Base::TraceWrappers(visitor)"-like calls.
// For example, if we've got "Base::TraceWrappers(visitor)" as |call|,
// callee_record will be "Base", and func_name will be "TraceWrappers".
CXXRecordDecl* callee_record = nullptr;
std::string func_name;
if (MemberExpr* callee = dyn_cast<MemberExpr>(call->getCallee())) {
if (!callee->hasQualifier())
return false;
FunctionDecl* trace_decl = dyn_cast<FunctionDecl>(callee->getMemberDecl());
if (!trace_decl || !Config::IsTraceWrappersMethod(trace_decl))
return false;
const Type* type = callee->getQualifier()->getAsType();
if (!type)
return false;
callee_record = type->getAsCXXRecordDecl();
func_name = trace_decl->getName();
}
if (!callee_record)
return false;
if (!IsTraceWrappersCallName(func_name))
return false;
for (auto& base : info_->GetBases()) {
// We want to deal with omitted TraceWrappers() function in an intermediary
// class in the class hierarchy, e.g.:
// class A : public TraceWrapperBase<A> { TraceWrappers() { ... } };
// class B : public A {
// /* No TraceWrappers(); have nothing to trace. */
// };
// class C : public B { TraceWrappers() { B::TraceWrappers(visitor); } }
// where, B::TraceWrappers() is actually A::TraceWrappers(), and in some
// cases we get A as |callee_record| instead of B. We somehow need to mark B
// as wrapper traced if we find A::TraceWrappers() call.
//
// To solve this, here we keep going up the class hierarchy as long as
// they are not required to have a trace method. The implementation is
// a simple DFS, where |base_records| represents the set of base classes
// we need to visit.
std::vector<CXXRecordDecl*> base_records;
base_records.push_back(base.first);
while (!base_records.empty()) {
CXXRecordDecl* base_record = base_records.back();
base_records.pop_back();
if (base_record == callee_record) {
// If we find a matching trace method, pretend the user has written
// a correct trace() method of the base; in the example above, we
// find A::trace() here and mark B as correctly traced.
base.second.MarkWrapperTraced();
return true;
}
if (RecordInfo* base_info = cache_->Lookup(base_record)) {
if (!base_info->RequiresTraceWrappersMethod()) {
// If this base class is not required to have a trace method, then
// the actual trace method may be defined in an ancestor.
for (auto& inner_base : base_info->GetBases())
base_records.push_back(inner_base.first);
}
}
}
}
return false;
}