[clang] [analyzer] Add std::any checker (PR #76580)
Balazs Benics via cfe-commits
cfe-commits at lists.llvm.org
Fri Dec 29 12:19:53 PST 2023
================
@@ -0,0 +1,201 @@
+//===- StdAnyChecker.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/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/Support/ErrorHandling.h"
+
+#include "TaggedUnionModeling.h"
+
+using namespace clang;
+using namespace ento;
+using namespace tagged_union_modeling;
+
+REGISTER_MAP_WITH_PROGRAMSTATE(AnyHeldTypeMap, const MemRegion *, QualType)
+
+class StdAnyChecker : public Checker<eval::Call, check::RegionChanges> {
+ CallDescription AnyConstructor{{"std", "any", "any"}};
+ CallDescription AnyAsOp{{"std", "any", "operator="}};
+ CallDescription AnyReset{{"std", "any", "reset"}, 0, 0};
+ CallDescription AnyCast{{"std", "any_cast"}, 1, 1};
+
+ BugType BadAnyType{this, "BadAnyType", "BadAnyType"};
+ BugType NullAnyType{this, "NullAnyType", "NullAnyType"};
+
+public:
+ ProgramStateRef
+ checkRegionChanges(ProgramStateRef State,
+ const InvalidatedSymbols *Invalidated,
+ ArrayRef<const MemRegion *> ExplicitRegions,
+ ArrayRef<const MemRegion *> Regions,
+ const LocationContext *LCtx, const CallEvent *Call) const {
+ if (!Call)
+ return State;
+
+ return removeInformationStoredForDeadInstances<AnyHeldTypeMap>(*Call, State,
+ Regions);
+ }
+
+ bool evalCall(const CallEvent &Call, CheckerContext &C) const {
+ // Do not take implementation details into consideration
+ if (Call.isCalledFromSystemHeader())
+ return false;
+
+ if (AnyCast.matches(Call))
+ return handleAnyCastCall(Call, C);
+
+ if (AnyReset.matches(Call)) {
+ const auto *AsMemberCall = dyn_cast<CXXMemberCall>(&Call);
+ if (!AsMemberCall)
+ return false;
+
+ const auto *ThisMemRegion = AsMemberCall->getCXXThisVal().getAsRegion();
+ if (!ThisMemRegion)
+ return false;
+
+ setNullTypeAny(ThisMemRegion, C);
+ return true;
+ }
+
+ bool IsAnyConstructor =
+ isa<CXXConstructorCall>(Call) && AnyConstructor.matches(Call);
+ bool IsAnyAssignmentOperatorCall =
+ isa<CXXMemberOperatorCall>(Call) && AnyAsOp.matches(Call);
+
+ if (IsAnyConstructor || IsAnyAssignmentOperatorCall) {
+ auto State = C.getState();
+ SVal ThisSVal = [&]() {
+ if (IsAnyConstructor) {
+ const auto *AsConstructorCall = dyn_cast<CXXConstructorCall>(&Call);
+ return AsConstructorCall->getCXXThisVal();
+ }
+ if (IsAnyAssignmentOperatorCall) {
+ const auto *AsMemberOpCall = dyn_cast<CXXMemberOperatorCall>(&Call);
+ return AsMemberOpCall->getCXXThisVal();
+ }
+ llvm_unreachable("We must have an assignment operator or constructor");
+ }();
+
+ // default constructor call
+ // in this case the any holds a null type
+ if (Call.getNumArgs() == 0) {
+ const auto *ThisMemRegion = ThisSVal.getAsRegion();
+ setNullTypeAny(ThisMemRegion, C);
+ return true;
+ }
+
+ if (Call.getNumArgs() != 1)
+ return false;
+
+ handleConstructorAndAssignment<AnyHeldTypeMap>(Call, C, ThisSVal);
+ return true;
+ }
+ return false;
+ }
+
+private:
+ // When an std::any is rested or default constructed it has a null type.
+ // We represent it by storing a null QualType.
+ void setNullTypeAny(const MemRegion *Mem, CheckerContext &C) const {
+ auto State = C.getState();
+ State = State->set<AnyHeldTypeMap>(Mem, QualType{});
+ C.addTransition(State);
+ }
+
+ // this function name is terrible
+ bool handleAnyCastCall(const CallEvent &Call, CheckerContext &C) const {
+ auto State = C.getState();
+
+ if (Call.getNumArgs() != 1)
+ return false;
+
+ auto ArgSVal = Call.getArgSVal(0);
+
+ // The argument is aether a const reference or a right value reference
+ // We need the type referred
+ const auto *ArgType = ArgSVal.getType(C.getASTContext())
+ .getTypePtr()
+ ->getPointeeType()
+ .getTypePtr();
----------------
steakhal wrote:
Generally, `SVal::getType` should not be used, unless the corresponding expression is no longer accessible in code.
Prefer `Call.getArgExpr(0)->getType()->getPointeeType().getTypePtr()` in this context.
`either` typo in the comment.
I'd also prefer a check and an early return if the type is neither a pointer not a reference. Make sure you check the canonical type, to look through typedefs and friends.
https://github.com/llvm/llvm-project/pull/76580
More information about the cfe-commits
mailing list