Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(34)

Unified Diff: tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp

Issue 197863003: Check consistency of manual trace and finalization dispatching. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: review comments Created 6 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | tools/clang/blink_gc_plugin/Config.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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_;
« no previous file with comments | « no previous file | tools/clang/blink_gc_plugin/Config.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698