Path: blob/main/contrib/llvm-project/clang/lib/Analysis/FlowSensitive/ASTOps.cpp
35266 views
//===-- ASTOps.cc -------------------------------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// Operations on AST nodes that are used in flow-sensitive analysis.9//10//===----------------------------------------------------------------------===//1112#include "clang/Analysis/FlowSensitive/ASTOps.h"13#include "clang/AST/ComputeDependence.h"14#include "clang/AST/Decl.h"15#include "clang/AST/DeclBase.h"16#include "clang/AST/DeclCXX.h"17#include "clang/AST/Expr.h"18#include "clang/AST/ExprCXX.h"19#include "clang/AST/Stmt.h"20#include "clang/AST/Type.h"21#include "clang/Analysis/FlowSensitive/StorageLocation.h"22#include "clang/Basic/LLVM.h"23#include "llvm/ADT/DenseSet.h"24#include "llvm/ADT/STLExtras.h"25#include <cassert>26#include <iterator>27#include <vector>2829#define DEBUG_TYPE "dataflow"3031namespace clang::dataflow {3233const Expr &ignoreCFGOmittedNodes(const Expr &E) {34const Expr *Current = &E;35const Expr *Last = nullptr;36while (Current != Last) {37Last = Current;38if (auto *EWC = dyn_cast<ExprWithCleanups>(Current)) {39Current = EWC->getSubExpr();40assert(Current != nullptr);41}42if (auto *CE = dyn_cast<ConstantExpr>(Current)) {43Current = CE->getSubExpr();44assert(Current != nullptr);45}46Current = Current->IgnoreParens();47assert(Current != nullptr);48}49return *Current;50}5152const Stmt &ignoreCFGOmittedNodes(const Stmt &S) {53if (auto *E = dyn_cast<Expr>(&S))54return ignoreCFGOmittedNodes(*E);55return S;56}5758// FIXME: Does not precisely handle non-virtual diamond inheritance. A single59// field decl will be modeled for all instances of the inherited field.60static void getFieldsFromClassHierarchy(QualType Type, FieldSet &Fields) {61if (Type->isIncompleteType() || Type->isDependentType() ||62!Type->isRecordType())63return;6465for (const FieldDecl *Field : Type->getAsRecordDecl()->fields())66Fields.insert(Field);67if (auto *CXXRecord = Type->getAsCXXRecordDecl())68for (const CXXBaseSpecifier &Base : CXXRecord->bases())69getFieldsFromClassHierarchy(Base.getType(), Fields);70}7172/// Gets the set of all fields in the type.73FieldSet getObjectFields(QualType Type) {74FieldSet Fields;75getFieldsFromClassHierarchy(Type, Fields);76return Fields;77}7879bool containsSameFields(const FieldSet &Fields,80const RecordStorageLocation::FieldToLoc &FieldLocs) {81if (Fields.size() != FieldLocs.size())82return false;83for ([[maybe_unused]] auto [Field, Loc] : FieldLocs)84if (!Fields.contains(cast_or_null<FieldDecl>(Field)))85return false;86return true;87}8889/// Returns the fields of a `RecordDecl` that are initialized by an90/// `InitListExpr` or `CXXParenListInitExpr`, in the order in which they appear91/// in `InitListExpr::inits()` / `CXXParenListInitExpr::getInitExprs()`.92/// `InitList->getType()` must be a record type.93template <class InitListT>94static std::vector<const FieldDecl *>95getFieldsForInitListExpr(const InitListT *InitList) {96const RecordDecl *RD = InitList->getType()->getAsRecordDecl();97assert(RD != nullptr);9899std::vector<const FieldDecl *> Fields;100101if (InitList->getType()->isUnionType()) {102if (const FieldDecl *Field = InitList->getInitializedFieldInUnion())103Fields.push_back(Field);104return Fields;105}106107// Unnamed bitfields are only used for padding and do not appear in108// `InitListExpr`'s inits. However, those fields do appear in `RecordDecl`'s109// field list, and we thus need to remove them before mapping inits to110// fields to avoid mapping inits to the wrongs fields.111llvm::copy_if(112RD->fields(), std::back_inserter(Fields),113[](const FieldDecl *Field) { return !Field->isUnnamedBitField(); });114return Fields;115}116117RecordInitListHelper::RecordInitListHelper(const InitListExpr *InitList)118: RecordInitListHelper(InitList->getType(),119getFieldsForInitListExpr(InitList),120InitList->inits()) {}121122RecordInitListHelper::RecordInitListHelper(123const CXXParenListInitExpr *ParenInitList)124: RecordInitListHelper(ParenInitList->getType(),125getFieldsForInitListExpr(ParenInitList),126ParenInitList->getInitExprs()) {}127128RecordInitListHelper::RecordInitListHelper(129QualType Ty, std::vector<const FieldDecl *> Fields,130ArrayRef<Expr *> Inits) {131auto *RD = Ty->getAsCXXRecordDecl();132assert(RD != nullptr);133134// Unions initialized with an empty initializer list need special treatment.135// For structs/classes initialized with an empty initializer list, Clang136// puts `ImplicitValueInitExpr`s in `InitListExpr::inits()`, but for unions,137// it doesn't do this -- so we create an `ImplicitValueInitExpr` ourselves.138SmallVector<Expr *> InitsForUnion;139if (Ty->isUnionType() && Inits.empty()) {140assert(Fields.size() <= 1);141if (!Fields.empty()) {142ImplicitValueInitForUnion.emplace(Fields.front()->getType());143InitsForUnion.push_back(&*ImplicitValueInitForUnion);144}145Inits = InitsForUnion;146}147148size_t InitIdx = 0;149150assert(Fields.size() + RD->getNumBases() == Inits.size());151for (const CXXBaseSpecifier &Base : RD->bases()) {152assert(InitIdx < Inits.size());153Expr *Init = Inits[InitIdx++];154BaseInits.emplace_back(&Base, Init);155}156157assert(Fields.size() == Inits.size() - InitIdx);158for (const FieldDecl *Field : Fields) {159assert(InitIdx < Inits.size());160Expr *Init = Inits[InitIdx++];161FieldInits.emplace_back(Field, Init);162}163}164165static void insertIfGlobal(const Decl &D,166llvm::DenseSet<const VarDecl *> &Globals) {167if (auto *V = dyn_cast<VarDecl>(&D))168if (V->hasGlobalStorage())169Globals.insert(V);170}171172static void insertIfFunction(const Decl &D,173llvm::DenseSet<const FunctionDecl *> &Funcs) {174if (auto *FD = dyn_cast<FunctionDecl>(&D))175Funcs.insert(FD);176}177178static MemberExpr *getMemberForAccessor(const CXXMemberCallExpr &C) {179// Use getCalleeDecl instead of getMethodDecl in order to handle180// pointer-to-member calls.181const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(C.getCalleeDecl());182if (!MethodDecl)183return nullptr;184auto *Body = dyn_cast_or_null<CompoundStmt>(MethodDecl->getBody());185if (!Body || Body->size() != 1)186return nullptr;187if (auto *RS = dyn_cast<ReturnStmt>(*Body->body_begin()))188if (auto *Return = RS->getRetValue())189return dyn_cast<MemberExpr>(Return->IgnoreParenImpCasts());190return nullptr;191}192193class ReferencedDeclsVisitor194: public AnalysisASTVisitor<ReferencedDeclsVisitor> {195public:196ReferencedDeclsVisitor(ReferencedDecls &Referenced)197: Referenced(Referenced) {}198199void TraverseConstructorInits(const CXXConstructorDecl *Ctor) {200for (const CXXCtorInitializer *Init : Ctor->inits()) {201if (Init->isMemberInitializer()) {202Referenced.Fields.insert(Init->getMember());203} else if (Init->isIndirectMemberInitializer()) {204for (const auto *I : Init->getIndirectMember()->chain())205Referenced.Fields.insert(cast<FieldDecl>(I));206}207208Expr *InitExpr = Init->getInit();209210// Also collect declarations referenced in `InitExpr`.211TraverseStmt(InitExpr);212213// If this is a `CXXDefaultInitExpr`, also collect declarations referenced214// within the default expression.215if (auto *DefaultInit = dyn_cast<CXXDefaultInitExpr>(InitExpr))216TraverseStmt(DefaultInit->getExpr());217}218}219220bool VisitDecl(Decl *D) {221insertIfGlobal(*D, Referenced.Globals);222insertIfFunction(*D, Referenced.Functions);223return true;224}225226bool VisitDeclRefExpr(DeclRefExpr *E) {227insertIfGlobal(*E->getDecl(), Referenced.Globals);228insertIfFunction(*E->getDecl(), Referenced.Functions);229return true;230}231232bool VisitCXXMemberCallExpr(CXXMemberCallExpr *C) {233// If this is a method that returns a member variable but does nothing else,234// model the field of the return value.235if (MemberExpr *E = getMemberForAccessor(*C))236if (const auto *FD = dyn_cast<FieldDecl>(E->getMemberDecl()))237Referenced.Fields.insert(FD);238return true;239}240241bool VisitMemberExpr(MemberExpr *E) {242// FIXME: should we be using `E->getFoundDecl()`?243const ValueDecl *VD = E->getMemberDecl();244insertIfGlobal(*VD, Referenced.Globals);245insertIfFunction(*VD, Referenced.Functions);246if (const auto *FD = dyn_cast<FieldDecl>(VD))247Referenced.Fields.insert(FD);248return true;249}250251bool VisitInitListExpr(InitListExpr *InitList) {252if (InitList->getType()->isRecordType())253for (const auto *FD : getFieldsForInitListExpr(InitList))254Referenced.Fields.insert(FD);255return true;256}257258bool VisitCXXParenListInitExpr(CXXParenListInitExpr *ParenInitList) {259if (ParenInitList->getType()->isRecordType())260for (const auto *FD : getFieldsForInitListExpr(ParenInitList))261Referenced.Fields.insert(FD);262return true;263}264265private:266ReferencedDecls &Referenced;267};268269ReferencedDecls getReferencedDecls(const FunctionDecl &FD) {270ReferencedDecls Result;271ReferencedDeclsVisitor Visitor(Result);272Visitor.TraverseStmt(FD.getBody());273if (const auto *CtorDecl = dyn_cast<CXXConstructorDecl>(&FD))274Visitor.TraverseConstructorInits(CtorDecl);275276return Result;277}278279ReferencedDecls getReferencedDecls(const Stmt &S) {280ReferencedDecls Result;281ReferencedDeclsVisitor Visitor(Result);282Visitor.TraverseStmt(const_cast<Stmt *>(&S));283return Result;284}285286} // namespace clang::dataflow287288289