Index: tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp |
diff --git a/tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp b/tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp |
index fb87ce8cd29f84387c481ac212b324adfa8358a6..41e694dcd8538c43f22c43bbfcc489248a1b6e82 100644 |
--- a/tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp |
+++ b/tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp |
@@ -325,8 +325,12 @@ class CheckDispatchVisitor : public RecursiveASTVisitor<CheckDispatchVisitor> { |
// - A base is traced if a base-qualified call to a trace method is found. |
class CheckTraceVisitor : public RecursiveASTVisitor<CheckTraceVisitor> { |
public: |
- CheckTraceVisitor(CXXMethodDecl* trace, RecordInfo* info) |
- : trace_(trace), info_(info), delegates_to_traceimpl_(false) {} |
+ CheckTraceVisitor(CXXMethodDecl* trace, RecordInfo* info, RecordCache* cache) |
+ : trace_(trace), |
+ info_(info), |
+ cache_(cache), |
+ delegates_to_traceimpl_(false) { |
+ } |
bool delegates_to_traceimpl() const { return delegates_to_traceimpl_; } |
@@ -541,12 +545,48 @@ class CheckTraceVisitor : public RecursiveASTVisitor<CheckTraceVisitor> { |
if (!IsTraceCallName(func_name)) |
return false; |
- RecordInfo::Bases::iterator iter = info_->GetBases().find(callee_record); |
- if (iter == info_->GetBases().end()) |
- return false; |
+ for (auto& base : info_->GetBases()) { |
+ // We want to deal with omitted trace() function in an intermediary |
+ // class in the class hierarchy, e.g.: |
+ // class A : public GarbageCollected<A> { trace() { ... } }; |
+ // class B : public A { /* No trace(); have nothing to trace. */ }; |
+ // class C : public B { trace() { B::trace(visitor); } } |
+ // where, B::trace() is actually A::trace(), and in some cases we get |
+ // A as |callee_record| instead of B. We somehow need to mark B as |
+ // traced if we find A::trace() 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.MarkTraced(); |
+ return true; |
+ } |
- iter->second.MarkTraced(); |
- return true; |
+ if (RecordInfo* base_info = cache_->Lookup(base_record)) { |
+ if (!base_info->RequiresTraceMethod()) { |
+ // 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; |
} |
bool CheckTraceFieldCall(CXXMemberCallExpr* call) { |
@@ -609,7 +649,8 @@ class CheckTraceVisitor : public RecursiveASTVisitor<CheckTraceVisitor> { |
}; |
// Nested checking for weak callbacks. |
- CheckTraceVisitor(RecordInfo* info) : trace_(0), info_(info) {} |
+ CheckTraceVisitor(RecordInfo* info) |
+ : trace_(nullptr), info_(info), cache_(nullptr) {} |
bool IsWeakCallback() { return !trace_; } |
@@ -643,6 +684,7 @@ class CheckTraceVisitor : public RecursiveASTVisitor<CheckTraceVisitor> { |
CXXMethodDecl* trace_; |
RecordInfo* info_; |
+ RecordCache* cache_; |
bool delegates_to_traceimpl_; |
}; |
@@ -1387,7 +1429,7 @@ class BlinkGCPluginConsumer : public ASTConsumer { |
} |
} |
- CheckTraceVisitor visitor(trace, parent); |
+ CheckTraceVisitor visitor(trace, parent, &cache_); |
visitor.TraverseCXXMethodDecl(trace); |
// Skip reporting if this trace method is a just delegate to |