| 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 3791a0f820ae9adb6d710bf5b82f2cbfc0265fe3..d60dd9572d84738d22f540395377b29c817d7870 100644
|
| --- a/tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp
|
| +++ b/tools/clang/blink_gc_plugin/BlinkGCPlugin.cpp
|
| @@ -50,6 +50,9 @@ const char kClassContainsGCRoot[] =
|
| const char kClassRequiresFinalization[] =
|
| "[blink-gc] Class %0 requires finalization.";
|
|
|
| +const char kClassDoesNotRequireFinalization[] =
|
| + "[blink-gc] Class %0 may not require finalization.";
|
| +
|
| const char kFinalizerAccessesFinalizedField[] =
|
| "[blink-gc] Finalizer %0 accesses potentially finalized field %1.";
|
|
|
| @@ -138,9 +141,15 @@ const char kBaseClassMustDeclareVirtualTrace[] =
|
| " must define a virtual trace method.";
|
|
|
| struct BlinkGCPluginOptions {
|
| - BlinkGCPluginOptions() : enable_oilpan(false), dump_graph(false) {}
|
| + BlinkGCPluginOptions()
|
| + : enable_oilpan(false)
|
| + , dump_graph(false)
|
| + , warn_raw_ptr(false)
|
| + , warn_unneeded_finalizer(false) {}
|
| bool enable_oilpan;
|
| bool dump_graph;
|
| + bool warn_raw_ptr;
|
| + bool warn_unneeded_finalizer;
|
| std::set<std::string> ignored_classes;
|
| std::set<std::string> checked_namespaces;
|
| std::vector<std::string> ignored_directories;
|
| @@ -619,6 +628,7 @@ class CheckFieldsVisitor : public RecursiveEdgeVisitor {
|
|
|
| enum Error {
|
| kRawPtrToGCManaged,
|
| + kRawPtrToGCManagedWarning,
|
| kRefPtrToGCManaged,
|
| kOwnPtrToGCManaged,
|
| kMemberInUnmanaged,
|
| @@ -692,7 +702,10 @@ class CheckFieldsVisitor : public RecursiveEdgeVisitor {
|
| current_, InvalidSmartPtr(Parent())));
|
| return;
|
| }
|
| -
|
| + if (options_.warn_raw_ptr && Parent()->IsRawPtr()) {
|
| + invalid_fields_.push_back(std::make_pair(
|
| + current_, kRawPtrToGCManagedWarning));
|
| + }
|
| return;
|
| }
|
|
|
| @@ -726,6 +739,28 @@ class CheckFieldsVisitor : public RecursiveEdgeVisitor {
|
| Errors invalid_fields_;
|
| };
|
|
|
| +class EmptyStmtVisitor
|
| + : public RecursiveASTVisitor<EmptyStmtVisitor> {
|
| +public:
|
| + static bool isEmpty(Stmt* stmt) {
|
| + EmptyStmtVisitor visitor;
|
| + visitor.TraverseStmt(stmt);
|
| + return visitor.empty_;
|
| + }
|
| +
|
| + bool WalkUpFromCompoundStmt(CompoundStmt* stmt) {
|
| + empty_ = stmt->body_empty();
|
| + return false;
|
| + }
|
| + bool VisitStmt(Stmt*) {
|
| + empty_ = false;
|
| + return false;
|
| + }
|
| +private:
|
| + EmptyStmtVisitor() : empty_(true) {}
|
| + bool empty_;
|
| +};
|
| +
|
| // Main class containing checks for various invariants of the Blink
|
| // garbage collection infrastructure.
|
| class BlinkGCPluginConsumer : public ASTConsumer {
|
| @@ -755,10 +790,14 @@ class BlinkGCPluginConsumer : public ASTConsumer {
|
| diagnostic_.getCustomDiagID(getErrorLevel(), kFieldsRequireTracing);
|
| diag_class_contains_invalid_fields_ = diagnostic_.getCustomDiagID(
|
| getErrorLevel(), kClassContainsInvalidFields);
|
| + diag_class_contains_invalid_fields_warning_ = diagnostic_.getCustomDiagID(
|
| + DiagnosticsEngine::Warning, kClassContainsInvalidFields);
|
| diag_class_contains_gc_root_ =
|
| diagnostic_.getCustomDiagID(getErrorLevel(), kClassContainsGCRoot);
|
| diag_class_requires_finalization_ = diagnostic_.getCustomDiagID(
|
| getErrorLevel(), kClassRequiresFinalization);
|
| + diag_class_does_not_require_finalization_ = diagnostic_.getCustomDiagID(
|
| + DiagnosticsEngine::Warning, kClassDoesNotRequireFinalization);
|
| diag_finalizer_accesses_finalized_field_ = diagnostic_.getCustomDiagID(
|
| getErrorLevel(), kFinalizerAccessesFinalizedField);
|
| diag_overridden_non_virtual_trace_ = diagnostic_.getCustomDiagID(
|
| @@ -941,6 +980,9 @@ class BlinkGCPluginConsumer : public ASTConsumer {
|
|
|
| if (info->NeedsFinalization())
|
| CheckFinalization(info);
|
| +
|
| + if (options_.warn_unneeded_finalizer && info->IsGCFinalized())
|
| + CheckUnneededFinalization(info);
|
| }
|
|
|
| DumpClass(info);
|
| @@ -1165,6 +1207,32 @@ class BlinkGCPluginConsumer : public ASTConsumer {
|
| }
|
| }
|
|
|
| + void CheckUnneededFinalization(RecordInfo* info) {
|
| + if (!HasNonEmptyFinalizer(info))
|
| + ReportClassDoesNotRequireFinalization(info);
|
| + }
|
| +
|
| + bool HasNonEmptyFinalizer(RecordInfo* info) {
|
| + CXXDestructorDecl* dtor = info->record()->getDestructor();
|
| + if (dtor && dtor->isUserProvided()) {
|
| + if (!dtor->hasBody() || !EmptyStmtVisitor::isEmpty(dtor->getBody()))
|
| + return true;
|
| + }
|
| + for (RecordInfo::Bases::iterator it = info->GetBases().begin();
|
| + it != info->GetBases().end();
|
| + ++it) {
|
| + if (HasNonEmptyFinalizer(it->second.info()))
|
| + return true;
|
| + }
|
| + for (RecordInfo::Fields::iterator it = info->GetFields().begin();
|
| + it != info->GetFields().end();
|
| + ++it) {
|
| + if (it->second.edge()->NeedsFinalization())
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| +
|
| // This is the main entry for tracing method definitions.
|
| void CheckTracingMethod(CXXMethodDecl* method) {
|
| RecordInfo* parent = cache_.Lookup(method->getParent());
|
| @@ -1462,13 +1530,23 @@ class BlinkGCPluginConsumer : public ASTConsumer {
|
| SourceLocation loc = info->record()->getLocStart();
|
| SourceManager& manager = instance_.getSourceManager();
|
| FullSourceLoc full_loc(loc, manager);
|
| - diagnostic_.Report(full_loc, diag_class_contains_invalid_fields_)
|
| + bool only_warnings = options_.warn_raw_ptr;
|
| + for (CheckFieldsVisitor::Errors::iterator it = errors->begin();
|
| + only_warnings && it != errors->end();
|
| + ++it) {
|
| + if (it->second != CheckFieldsVisitor::kRawPtrToGCManagedWarning)
|
| + only_warnings = false;
|
| + }
|
| + diagnostic_.Report(full_loc, only_warnings ?
|
| + diag_class_contains_invalid_fields_warning_ :
|
| + diag_class_contains_invalid_fields_)
|
| << info->record();
|
| for (CheckFieldsVisitor::Errors::iterator it = errors->begin();
|
| it != errors->end();
|
| ++it) {
|
| unsigned error;
|
| - if (it->second == CheckFieldsVisitor::kRawPtrToGCManaged) {
|
| + if (it->second == CheckFieldsVisitor::kRawPtrToGCManaged ||
|
| + it->second == CheckFieldsVisitor::kRawPtrToGCManagedWarning) {
|
| error = diag_raw_ptr_to_gc_managed_class_note_;
|
| } else if (it->second == CheckFieldsVisitor::kRefPtrToGCManaged) {
|
| error = diag_ref_ptr_to_gc_managed_class_note_;
|
| @@ -1530,6 +1608,14 @@ class BlinkGCPluginConsumer : public ASTConsumer {
|
| << info->record();
|
| }
|
|
|
| + void ReportClassDoesNotRequireFinalization(RecordInfo* info) {
|
| + SourceLocation loc = info->record()->getInnerLocStart();
|
| + SourceManager& manager = instance_.getSourceManager();
|
| + FullSourceLoc full_loc(loc, manager);
|
| + diagnostic_.Report(full_loc, diag_class_does_not_require_finalization_)
|
| + << info->record();
|
| + }
|
| +
|
| void ReportOverriddenNonVirtualTrace(RecordInfo* info,
|
| CXXMethodDecl* trace,
|
| CXXMethodDecl* overridden) {
|
| @@ -1705,8 +1791,10 @@ class BlinkGCPluginConsumer : public ASTConsumer {
|
| unsigned diag_base_requires_tracing_;
|
| unsigned diag_fields_require_tracing_;
|
| unsigned diag_class_contains_invalid_fields_;
|
| + unsigned diag_class_contains_invalid_fields_warning_;
|
| unsigned diag_class_contains_gc_root_;
|
| unsigned diag_class_requires_finalization_;
|
| + unsigned diag_class_does_not_require_finalization_;
|
| unsigned diag_finalizer_accesses_finalized_field_;
|
| unsigned diag_overridden_non_virtual_trace_;
|
| unsigned diag_missing_trace_dispatch_method_;
|
| @@ -1765,6 +1853,10 @@ class BlinkGCPluginAction : public PluginASTAction {
|
| options_.enable_oilpan = true;
|
| } else if (args[i] == "dump-graph") {
|
| options_.dump_graph = true;
|
| + } else if (args[i] == "warn-raw-ptr") {
|
| + options_.warn_raw_ptr = true;
|
| + } else if (args[i] == "warn-unneeded-finalizer") {
|
| + options_.warn_unneeded_finalizer = true;
|
| } else {
|
| parsed = false;
|
| llvm::errs() << "Unknown blink-gc-plugin argument: " << args[i] << "\n";
|
|
|