| // Copyright 2015 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "CheckFinalizerVisitor.h" |
| |
| using namespace clang; |
| |
| namespace { |
| |
| // Simple visitor to determine if the content of a field might be collected |
| // during finalization. |
| class MightBeCollectedVisitor : public EdgeVisitor { |
| public: |
| bool might_be_collected() const; |
| |
| void VisitMember(Member* edge) override; |
| void VisitCollection(Collection* edge) override; |
| |
| private: |
| bool might_be_collected_ = false; |
| }; |
| |
| bool MightBeCollectedVisitor::might_be_collected() const { |
| return might_be_collected_; |
| } |
| |
| void MightBeCollectedVisitor::VisitMember(Member* edge) { |
| might_be_collected_ = true; |
| } |
| |
| void MightBeCollectedVisitor::VisitCollection(Collection* edge) { |
| if (edge->on_heap()) { |
| might_be_collected_ = true; |
| } else { |
| edge->AcceptMembers(this); |
| } |
| } |
| |
| } // namespace |
| |
| CheckFinalizerVisitor::CheckFinalizerVisitor(RecordCache* cache) |
| : blocklist_context_(false), |
| cache_(cache) { |
| } |
| |
| CheckFinalizerVisitor::Errors& CheckFinalizerVisitor::finalized_fields() { |
| return finalized_fields_; |
| } |
| |
| bool CheckFinalizerVisitor::WalkUpFromCXXOperatorCallExpr( |
| CXXOperatorCallExpr* expr) { |
| // Only continue the walk-up if the operator is a blocklisted one. |
| switch (expr->getOperator()) { |
| case OO_Arrow: |
| case OO_Subscript: |
| this->WalkUpFromCallExpr(expr); |
| return true; |
| default: |
| return true; |
| } |
| } |
| |
| bool CheckFinalizerVisitor::WalkUpFromCallExpr(CallExpr* expr) { |
| // We consider all non-operator calls to be blocklisted contexts. |
| bool prev_blocklist_context = blocklist_context_; |
| blocklist_context_ = true; |
| for (size_t i = 0; i < expr->getNumArgs(); ++i) |
| this->TraverseStmt(expr->getArg(i)); |
| blocklist_context_ = prev_blocklist_context; |
| return true; |
| } |
| |
| bool CheckFinalizerVisitor::VisitMemberExpr(MemberExpr* member) { |
| FieldDecl* field = dyn_cast<FieldDecl>(member->getMemberDecl()); |
| if (!field) |
| return true; |
| |
| RecordInfo* info = cache_->Lookup(field->getParent()); |
| if (!info) |
| return true; |
| |
| RecordInfo::Fields::iterator it = info->GetFields().find(field); |
| if (it == info->GetFields().end()) |
| return true; |
| |
| if (seen_members_.find(member) != seen_members_.end()) |
| return true; |
| |
| if (blocklist_context_ && |
| MightBeCollected(&it->second)) { |
| finalized_fields_.push_back( |
| Error(member, &it->second)); |
| seen_members_.insert(member); |
| } |
| return true; |
| } |
| |
| bool CheckFinalizerVisitor::MightBeCollected(FieldPoint* point) { |
| MightBeCollectedVisitor visitor; |
| point->edge()->Accept(&visitor); |
| return visitor.might_be_collected(); |
| } |