[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");
+ }();
----------------
steakhal wrote:
The immediately called lambda functions are generally not pretty.
I'd prefer (ab)using ternaries instead, like this:
```c++
const auto *AsCtorCall = dyn_cast_or_null<CXXConstructorCall>(
AnyConstructor.matches(Call) ? &Call : nullptr);
const auto *AsAssignCall = dyn_cast_or_null<CXXMemberOperatorCall>(
AnyAsOp.matches(Call) ? &Call : nullptr);
if (!AsCtorCall && !AsAssignCall)
return false;
SVal ThisSVal = AsCtorCall ? AsCtorCall->getCXXThisVal()
: AsAssignCall->getCXXThisVal();
```
It might be only me, but I find this more readable.
https://github.com/llvm/llvm-project/pull/76580
More information about the cfe-commits
mailing list