================ @@ -0,0 +1,312 @@ +//===- StdVariantChecker.cpp -------------------------------------*- C++ -*-==// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/Type.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "llvm/ADT/FoldingSet.h" + +#include "TaggedUnionModeling.h" + +using namespace clang; +using namespace ento; +using namespace variant_modeling; + +REGISTER_MAP_WITH_PROGRAMSTATE(VariantHeldTypeMap, const MemRegion *, QualType) + +namespace clang { +namespace ento { +namespace variant_modeling { + +// Returns the CallEvent representing the caller of the function +// It is needed because the CallEvent class does not contain enough information +// to tell who called it. Checker context is needed. +CallEventRef<> getCaller(const CallEvent &Call, const ProgramStateRef &State) { + const auto *CallLocationContext = Call.getLocationContext(); + if (!CallLocationContext) { + return nullptr; + } + + if (CallLocationContext->inTopFrame()) { + return nullptr; + } + const auto *CallStackFrameContext = CallLocationContext->getStackFrame(); + if (!CallStackFrameContext) { + return nullptr; + } + + CallEventManager &CEMgr = State->getStateManager().getCallEventManager(); + return CEMgr.getCaller(CallStackFrameContext, State); +} + +const CXXConstructorDecl * +getConstructorDeclarationForCall(const CallEvent &Call) { + const auto *ConstructorCall = dyn_cast<CXXConstructorCall>(&Call); + if (!ConstructorCall) { + return nullptr; + } + return ConstructorCall->getDecl(); +} + +bool isCopyConstructorCall(const CallEvent &Call) { + const CXXConstructorDecl *ConstructorDecl = + getConstructorDeclarationForCall(Call); + if (!ConstructorDecl) { + return false; + } + return ConstructorDecl->isCopyConstructor(); +} + +bool isCopyAssignmentCall(const CallEvent &Call) { + const Decl *CopyAssignmentDecl = Call.getDecl(); + if (!CopyAssignmentDecl) { + return false; + } + const auto *AsMethodDecl = dyn_cast<CXXMethodDecl>(CopyAssignmentDecl); + if (!AsMethodDecl) { + return false; + } + return AsMethodDecl->isCopyAssignmentOperator(); +} + +bool isMoveConstructorCall(const CallEvent &Call) { + const CXXConstructorDecl *ConstructorDecl = + getConstructorDeclarationForCall(Call); + if (!ConstructorDecl) { + return false; + } + return ConstructorDecl->isMoveConstructor(); +} + +bool isMoveAssignmentCall(const CallEvent &Call) { + const Decl *CopyAssignmentDecl = Call.getDecl(); + if (!CopyAssignmentDecl) { + return false; + } + const auto *AsMethodDecl = dyn_cast<CXXMethodDecl>(CopyAssignmentDecl); + if (!AsMethodDecl) { + return false; + } + return AsMethodDecl->isMoveAssignmentOperator(); +} + +const TemplateArgument &getFirstTemplateArgument(const CallEvent &Call) { + const CallExpr *CE = cast<CallExpr>(Call.getOriginExpr()); + const FunctionDecl *FD = CE->getDirectCallee(); + assert(1 <= FD->getTemplateSpecializationArgs()->asArray().size() && + "std::get should have at least 1 template argument!"); + return FD->getTemplateSpecializationArgs()->asArray()[0]; +} + +bool isStdType(const Type *Type, const std::string &TypeName) { + auto *Decl = Type->getAsRecordDecl(); + if (!Decl) { + return false; + } + + return (Decl->getNameAsString() == TypeName) && Decl->isInStdNamespace(); +} + +bool isStdVariant(const Type *Type) { + return isStdType(Type, std::string("variant")); +} + +bool calledFromSystemHeader(const CallEvent &Call, + const ProgramStateRef &State) { + auto Caller = getCaller(Call, State); + if (Caller) { + return Caller->isInSystemHeader(); + } + return false; +} + +bool calledFromSystemHeader(const CallEvent &Call, CheckerContext &C) { + return calledFromSystemHeader(Call, C.getState()); +} + +} // end of namespace variant_modeling +} // end of namespace ento +} // end of namespace clang + +static ArrayRef<TemplateArgument> +getTemplateArgsFromVariant(const Type *VariantType) { + const auto *TempSpecType = VariantType->getAs<TemplateSpecializationType>(); + assert(TempSpecType && + "We are in a variant instance. It must be a template specialization!"); + return TempSpecType->template_arguments(); +} + +static QualType getNthTemplateTypeArgFromVariant(const Type *varType, + unsigned i) { + return getTemplateArgsFromVariant(varType)[i].getAsType(); +} + +class StdVariantChecker : public Checker<eval::Call, check::RegionChanges> { + // Call descriptors to find relevant calls + CallDescription VariantConstructor{{"std", "variant", "variant"}}; + CallDescription VariantAsOp{{"std", "variant", "operator="}}; + CallDescription StdGet{{"std", "get"}, 1, 1}; + + BugType BadVariantType{this, "BadVariantType", "BadVariantType"}; + +public: + ProgramStateRef checkRegionChanges(ProgramStateRef State, + const InvalidatedSymbols *, + ArrayRef<const MemRegion *>, + ArrayRef<const MemRegion *> Regions, + const LocationContext *, + const CallEvent *Call) const { + return removeInformationStoredForDeadInstances<VariantHeldTypeMap>( + Call, State, Regions); + } + + bool evalCall(const CallEvent &Call, CheckerContext &C) const { + // Check if the call was not made from a system header. If it was then + // we do an early return because it is part of the implementation + if (calledFromSystemHeader(Call, C)) { + return false; + } + + if (StdGet.matches(Call)) { + return handleStdGetCall(Call, C); + } + + bool IsVariantConstructor = + isa<CXXConstructorCall>(Call) && VariantConstructor.matches(Call); + bool IsVariantAssignmentOperatorCall = + isa<CXXMemberOperatorCall>(Call) && VariantAsOp.matches(Call); + + if (IsVariantConstructor || IsVariantAssignmentOperatorCall) { + if (IsVariantConstructor && Call.getNumArgs() == 0) { + handleDefaultConstructor(Call, C); + return true; + } + if (Call.getNumArgs() != 1) { + return true; + } + SVal thisSVal = [&]() { + if (IsVariantConstructor) { + const auto *AsConstructorCall = dyn_cast<CXXConstructorCall>(&Call); + return AsConstructorCall->getCXXThisVal(); ---------------- spaits wrote:
In line 184 there is a variable `IsVariantConstructor`. It can only be true if the call is a constructor and it is a variant constructor (the `CallDescription` is needed to decide if it is for std::variant) the same is true for the next case but there std::variant's assignment operator is the one being checked for. So in these cases `dyn_cast` should not return null. https://github.com/llvm/llvm-project/pull/66481 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits