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 a5e78e0b3d92ea80094cdaf7d6e33398b450a49e..6603d6e6b804d31dc32ff4864621a53058fe0c1b 100644 |
--- a/tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp |
+++ b/tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp |
@@ -62,6 +62,29 @@ const char kPartObjectContainsGCRoot[] = |
const char kFieldContainsGCRoot[] = |
"[blink-gc] Field %0 defining a GC root declared here:"; |
+const char kOverriddenNonVirtualTrace[] = |
+ "[blink-gc] Class %0 overrides non-virtual trace of base class %1."; |
+ |
+const char kOverriddenNonVirtualTraceNote[] = |
+ "[blink-gc] Non-virtual trace method declared here:"; |
+ |
+const char kVirtualTraceAndManualDispatch[] = |
+ "[blink-gc] Class %0 declares a virtual trace" |
+ " but implements manual dispatching."; |
+ |
+const char kVirtualAndManualDispatch[] = |
+ "[blink-gc] Class %0 contains or inherits virtual methods" |
+ " but implements manual dispatching."; |
+ |
+const char kMissingTraceDispatch[] = |
+ "[blink-gc] Missing dispatch to class %0 in manual trace dispatch."; |
+ |
+const char kMissingFinalize[] = |
+ "[blink-gc] Class %0 is missing manual finalize dispatch."; |
+ |
+const char kMissingFinalizeDispatch[] = |
+ "[blink-gc] Missing dispatch to class %0 in manual finalize dispatch."; |
+ |
const char kFinalizedFieldNote[] = |
"[blink-gc] Potentially finalized field %0 declared here:"; |
@@ -176,8 +199,9 @@ class CheckFinalizerVisitor |
case OO_Arrow: |
case OO_Subscript: |
this->WalkUpFromCallExpr(expr); |
+ default: |
+ return true; |
} |
- return true; |
} |
// We consider all non-operator calls to be blacklisted contexts. |
@@ -220,6 +244,29 @@ class CheckFinalizerVisitor |
RecordCache* cache_; |
}; |
+// This visitor checks that a method contains within its body, a call to a |
+// method on the provided receiver class. This is used to check manual |
+// dispatching for trace and finalize methods. |
+class CheckDispatchVisitor : public RecursiveASTVisitor<CheckDispatchVisitor> { |
+ public: |
+ CheckDispatchVisitor(RecordInfo* receiver) |
+ : receiver_(receiver), dispatched_to_receiver_(false) { } |
+ |
+ bool dispatched_to_receiver() { return dispatched_to_receiver_; } |
+ |
+ bool VisitMemberExpr(MemberExpr* member) { |
+ if (CXXMethodDecl* fn = dyn_cast<CXXMethodDecl>(member->getMemberDecl())) { |
+ if (fn->getParent() == receiver_->record()) |
+ dispatched_to_receiver_ = true; |
+ } |
+ return true; |
+ } |
+ |
+ private: |
+ RecordInfo* receiver_; |
+ bool dispatched_to_receiver_; |
+}; |
+ |
// This visitor checks a tracing method by traversing its body. |
// - A member field is considered traced if it is referenced in the body. |
// - A base is traced if a base-qualified call to a trace method is found. |
@@ -446,6 +493,16 @@ class BlinkGCPluginConsumer : public ASTConsumer { |
getErrorLevel(), kClassRequiresFinalization); |
diag_finalizer_accesses_finalized_field_ = diagnostic_.getCustomDiagID( |
getErrorLevel(), kFinalizerAccessesFinalizedField); |
+ diag_overridden_non_virtual_trace_ = diagnostic_.getCustomDiagID( |
+ getErrorLevel(), kOverriddenNonVirtualTrace); |
+ diag_virtual_and_manual_dispatch_ = diagnostic_.getCustomDiagID( |
+ getErrorLevel(), kVirtualAndManualDispatch); |
+ diag_missing_trace_dispatch_ = diagnostic_.getCustomDiagID( |
+ getErrorLevel(), kMissingTraceDispatch); |
+ diag_missing_finalize_ = diagnostic_.getCustomDiagID( |
+ getErrorLevel(), kMissingFinalize); |
+ diag_missing_finalize_dispatch_ = diagnostic_.getCustomDiagID( |
+ getErrorLevel(), kMissingFinalizeDispatch); |
// Register note messages. |
diag_field_requires_tracing_note_ = diagnostic_.getCustomDiagID( |
@@ -468,6 +525,8 @@ class BlinkGCPluginConsumer : public ASTConsumer { |
DiagnosticsEngine::Note, kBaseRequiresFinalizationNote); |
diag_field_requires_finalization_note_ = diagnostic_.getCustomDiagID( |
DiagnosticsEngine::Note, kFieldRequiresFinalizationNote); |
+ diag_overridden_non_virtual_trace_note_ = diagnostic_.getCustomDiagID( |
+ DiagnosticsEngine::Note, kOverriddenNonVirtualTraceNote); |
} |
virtual void HandleTranslationUnit(ASTContext& context) { |
@@ -522,6 +581,9 @@ class BlinkGCPluginConsumer : public ASTConsumer { |
if (info->RequiresTraceMethod() && !info->GetTraceMethod()) |
ReportClassRequiresTraceMethod(info); |
+ if (CXXMethodDecl* dispatch = info->GetTraceDispatchMethod()) |
+ CheckDispatch(info, dispatch); |
+ |
{ |
CheckFieldsVisitor visitor(options_); |
if (visitor.ContainsInvalidFields(info)) |
@@ -538,6 +600,52 @@ class BlinkGCPluginConsumer : public ASTConsumer { |
} |
} |
+ void CheckDispatch(RecordInfo* info, CXXMethodDecl* dispatch) { |
+ if (info->record()->isPolymorphic()) |
+ ReportVirtualAndManualDispatch(info); |
+ |
+ CXXRecordDecl* base = dispatch->getParent(); |
+ // If this class is finalized get its finalize dispatch method. |
+ bool is_finalized = info->IsGCFinalized(); |
+ CXXMethodDecl* finalize = 0; |
+ if (is_finalized) { |
+ for (CXXRecordDecl::method_iterator it = base->method_begin(); |
+ it != base->method_end(); |
+ ++it) { |
+ if (it->getNameAsString() == kFinalizeName) { |
+ finalize = *it; |
+ break; |
+ } |
+ } |
+ } |
+ |
+ // Check that the dispatching class implements finalize if needed. |
+ if (is_finalized && !finalize && base == info->record()) |
+ ReportMissingFinalize(info); |
+ |
+ // If this is a non-abstract class check that it is dispatched to. |
+ // TODO: Create a global variant of this local check. We can only check if |
+ // the dispatch body is known in this compilation unit. |
+ if (info->IsConsideredAbstract()) |
+ return; |
+ |
+ const FunctionDecl* defn; |
+ |
+ if (dispatch->isDefined(defn)) { |
+ CheckDispatchVisitor visitor(info); |
+ visitor.TraverseStmt(defn->getBody()); |
+ if (!visitor.dispatched_to_receiver()) |
+ ReportMissingTraceDispatch(defn, info); |
+ } |
+ |
+ if (is_finalized && finalize && finalize->isDefined(defn)) { |
+ CheckDispatchVisitor visitor(info); |
+ visitor.TraverseStmt(defn->getBody()); |
+ if (!visitor.dispatched_to_receiver()) |
+ ReportMissingFinalizeDispatch(defn, info); |
+ } |
+ } |
+ |
void CheckFinalization(RecordInfo* info) { |
// TODO: Should we collect destructors similar to trace methods? |
// TODO: Check overridden finalize(). |
@@ -557,7 +665,7 @@ class BlinkGCPluginConsumer : public ASTConsumer { |
} |
// Don't require finalization of a mixin that has not yet been "mixed in". |
- if (info->IsUnmixedGCMixin()) |
+ if (info->IsGCMixin()) |
return; |
// Report the finalization error, and proceed to print possible causes for |
@@ -609,20 +717,32 @@ class BlinkGCPluginConsumer : public ASTConsumer { |
void CheckTraceOrDispatchMethod(RecordInfo* parent, CXXMethodDecl* method) { |
bool isTraceAfterDispatch; |
if (Config::IsTraceMethod(method, &isTraceAfterDispatch)) { |
- if (!isTraceAfterDispatch && parent->GetTraceDispatchMethod()) |
- CheckTraceDispatchMethod(parent, method); |
- else |
- CheckTraceMethod(parent, method); |
+ if (isTraceAfterDispatch || !parent->GetTraceDispatchMethod()) { |
+ CheckTraceMethod(parent, method, isTraceAfterDispatch); |
+ } |
+ // Dispatch methods are checked when we identify subclasses. |
} |
} |
- // Check a tracing dispatch (ie, it dispatches to traceAfterDispatch) |
- void CheckTraceDispatchMethod(RecordInfo* parent, CXXMethodDecl* trace) { |
- // TODO: check correct dispatch. |
- } |
- |
// Check an actual trace method. |
- void CheckTraceMethod(RecordInfo* parent, CXXMethodDecl* trace) { |
+ void CheckTraceMethod(RecordInfo* parent, |
+ CXXMethodDecl* trace, |
+ bool isTraceAfterDispatch) { |
+ // A non-virtual trace method must not override another trace. |
+ if (!isTraceAfterDispatch && !trace->isVirtual()) { |
+ for (RecordInfo::Bases::iterator it = parent->GetBases().begin(); |
+ it != parent->GetBases().end(); |
+ ++it) { |
+ RecordInfo* base = it->second.info(); |
+ // We allow mixin bases to contain a non-virtual trace since it will |
+ // never be used for dispatching. |
+ if (base->IsGCMixin()) |
+ continue; |
+ if (CXXMethodDecl* other = base->InheritsNonVirtualTrace()) |
+ ReportOverriddenNonVirtualTrace(parent, trace, other); |
+ } |
+ } |
+ |
CheckTraceVisitor visitor(trace, parent); |
visitor.TraverseCXXMethodDecl(trace); |
@@ -807,6 +927,52 @@ class BlinkGCPluginConsumer : public ASTConsumer { |
<< info->record(); |
} |
+ void ReportOverriddenNonVirtualTrace(RecordInfo* info, |
+ CXXMethodDecl* trace, |
+ CXXMethodDecl* overridden) { |
+ SourceLocation loc = trace->getLocStart(); |
+ SourceManager& manager = instance_.getSourceManager(); |
+ FullSourceLoc full_loc(loc, manager); |
+ diagnostic_.Report(full_loc, diag_overridden_non_virtual_trace_) |
+ << info->record() |
+ << overridden->getParent(); |
+ NoteOverriddenNonVirtualTrace(overridden); |
+ } |
+ |
+ void ReportVirtualAndManualDispatch(RecordInfo* info) { |
+ SourceLocation loc = info->record()->getInnerLocStart(); |
+ SourceManager& manager = instance_.getSourceManager(); |
+ FullSourceLoc full_loc(loc, manager); |
+ diagnostic_.Report(full_loc, diag_virtual_and_manual_dispatch_) |
+ << info->record(); |
+ } |
+ |
+ void ReportMissingTraceDispatch(const FunctionDecl* dispatch, |
+ RecordInfo* receiver) { |
+ ReportMissingDispatch(dispatch, receiver, diag_missing_trace_dispatch_); |
+ } |
+ |
+ void ReportMissingFinalize(RecordInfo* info) { |
+ SourceLocation loc = info->record()->getInnerLocStart(); |
+ SourceManager& manager = instance_.getSourceManager(); |
+ FullSourceLoc full_loc(loc, manager); |
+ diagnostic_.Report(full_loc, diag_missing_finalize_) << info->record(); |
+ } |
+ |
+ void ReportMissingFinalizeDispatch(const FunctionDecl* dispatch, |
+ RecordInfo* receiver) { |
+ ReportMissingDispatch(dispatch, receiver, diag_missing_finalize_dispatch_); |
+ } |
+ |
+ void ReportMissingDispatch(const FunctionDecl* dispatch, |
+ RecordInfo* receiver, |
+ unsigned error) { |
+ SourceLocation loc = dispatch->getLocStart(); |
+ SourceManager& manager = instance_.getSourceManager(); |
+ FullSourceLoc full_loc(loc, manager); |
+ diagnostic_.Report(full_loc, error) << receiver->record(); |
+ } |
+ |
void NoteFieldRequiresTracing(RecordInfo* holder, FieldDecl* field) { |
NoteField(field, diag_field_requires_tracing_note_); |
} |
@@ -850,6 +1016,14 @@ class BlinkGCPluginConsumer : public ASTConsumer { |
diagnostic_.Report(full_loc, note) << field; |
} |
+ void NoteOverriddenNonVirtualTrace(CXXMethodDecl* overridden) { |
+ SourceLocation loc = overridden->getLocStart(); |
+ SourceManager& manager = instance_.getSourceManager(); |
+ FullSourceLoc full_loc(loc, manager); |
+ diagnostic_.Report(full_loc, diag_overridden_non_virtual_trace_note_) |
+ << overridden; |
+ } |
+ |
unsigned diag_class_requires_trace_method_; |
unsigned diag_base_requires_tracing_; |
unsigned diag_fields_require_tracing_; |
@@ -857,6 +1031,11 @@ class BlinkGCPluginConsumer : public ASTConsumer { |
unsigned diag_class_contains_gc_root_; |
unsigned diag_class_requires_finalization_; |
unsigned diag_finalizer_accesses_finalized_field_; |
+ unsigned diag_overridden_non_virtual_trace_; |
+ unsigned diag_virtual_and_manual_dispatch_; |
+ unsigned diag_missing_trace_dispatch_; |
+ unsigned diag_missing_finalize_; |
+ unsigned diag_missing_finalize_dispatch_; |
unsigned diag_field_requires_tracing_note_; |
unsigned diag_raw_ptr_to_gc_managed_class_note_; |
@@ -868,6 +1047,7 @@ class BlinkGCPluginConsumer : public ASTConsumer { |
unsigned diag_user_declared_destructor_note_; |
unsigned diag_base_requires_finalization_note_; |
unsigned diag_field_requires_finalization_note_; |
+ unsigned diag_overridden_non_virtual_trace_note_; |
CompilerInstance& instance_; |
DiagnosticsEngine& diagnostic_; |