Skip to content

Commit eccbae3

Browse files
committed
[𝘀𝗽𝗿] initial version
Created using spr 1.3.7
2 parents 0e0ec4c + cd0ea93 commit eccbae3

File tree

4 files changed

+427
-1
lines changed

4 files changed

+427
-1
lines changed

clang/include/clang/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "clang/ASTMatchers/ASTMatchers.h"
1414
#include "clang/Analysis/CFG.h"
1515
#include "clang/Analysis/FlowSensitive/CFGMatchSwitch.h"
16+
#include "clang/Analysis/FlowSensitive/CachedConstAccessorsLattice.h"
1617
#include "clang/Analysis/FlowSensitive/DataflowAnalysis.h"
1718
#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
1819
#include "clang/Analysis/FlowSensitive/MatchSwitch.h"
@@ -69,7 +70,8 @@ struct UncheckedStatusOrAccessModelOptions {};
6970

7071
// Dataflow analysis that discovers unsafe uses of StatusOr values.
7172
class UncheckedStatusOrAccessModel
72-
: public DataflowAnalysis<UncheckedStatusOrAccessModel, NoopLattice> {
73+
: public DataflowAnalysis<UncheckedStatusOrAccessModel,
74+
CachedConstAccessorsLattice<NoopLattice>> {
7375
public:
7476
explicit UncheckedStatusOrAccessModel(ASTContext &Ctx, Environment &Env);
7577

clang/lib/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.cpp

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,49 @@ static auto isAsStatusCallWithStatusOr() {
237237
hasArgument(0, hasType(statusOrType())));
238238
}
239239

240+
static auto possiblyReferencedStatusOrType() {
241+
using namespace ::clang::ast_matchers; // NOLINT: Too many names
242+
return anyOf(statusOrType(), referenceType(pointee(statusOrType())));
243+
}
244+
245+
static auto isConstStatusOrAccessorMemberCall() {
246+
using namespace ::clang::ast_matchers; // NOLINT: Too many names
247+
return cxxMemberCallExpr(callee(
248+
cxxMethodDecl(parameterCountIs(0), isConst(),
249+
returns(qualType(possiblyReferencedStatusOrType())))));
250+
}
251+
252+
static auto isConstStatusOrAccessorMemberOperatorCall() {
253+
using namespace ::clang::ast_matchers; // NOLINT: Too many names
254+
return cxxOperatorCallExpr(
255+
callee(cxxMethodDecl(parameterCountIs(0), isConst(),
256+
returns(possiblyReferencedStatusOrType()))));
257+
}
258+
259+
static auto isConstStatusOrPointerAccessorMemberCall() {
260+
using namespace ::clang::ast_matchers; // NOLINT: Too many names
261+
return cxxMemberCallExpr(callee(cxxMethodDecl(
262+
parameterCountIs(0), isConst(),
263+
returns(pointerType(pointee(possiblyReferencedStatusOrType()))))));
264+
}
265+
266+
static auto isConstStatusOrPointerAccessorMemberOperatorCall() {
267+
using namespace ::clang::ast_matchers; // NOLINT: Too many names
268+
return cxxOperatorCallExpr(callee(cxxMethodDecl(
269+
parameterCountIs(0), isConst(),
270+
returns(pointerType(pointee(possiblyReferencedStatusOrType()))))));
271+
}
272+
273+
static auto isNonConstMemberCall() {
274+
using namespace ::clang::ast_matchers; // NOLINT: Too many names
275+
return cxxMemberCallExpr(callee(cxxMethodDecl(unless(isConst()))));
276+
}
277+
278+
static auto isNonConstMemberOperatorCall() {
279+
using namespace ::clang::ast_matchers; // NOLINT: Too many names
280+
return cxxOperatorCallExpr(callee(cxxMethodDecl(unless(isConst()))));
281+
}
282+
240283
static auto
241284
buildDiagnoseMatchSwitch(const UncheckedStatusOrAccessModelOptions &Options) {
242285
return CFGMatchSwitchBuilder<const Environment,
@@ -697,6 +740,107 @@ static void transferPointerToBoolean(const ImplicitCastExpr *Expr,
697740
dyn_cast_or_null<BoolValue>(State.Env.getValue(*Expr->getSubExpr())))
698741
State.Env.setValue(*Expr, *SubExprVal);
699742
}
743+
static void handleConstStatusOrAccessorMemberCall(
744+
const CallExpr *Expr, RecordStorageLocation *RecordLoc,
745+
const MatchFinder::MatchResult &Result, LatticeTransferState &State) {
746+
assert(isStatusOrType(Expr->getType()));
747+
if (RecordLoc == nullptr)
748+
return;
749+
const FunctionDecl *DirectCallee = Expr->getDirectCallee();
750+
if (DirectCallee == nullptr)
751+
return;
752+
StorageLocation &Loc =
753+
State.Lattice.getOrCreateConstMethodReturnStorageLocation(
754+
*RecordLoc, DirectCallee, State.Env, [&](StorageLocation &Loc) {
755+
initializeStatusOr(cast<RecordStorageLocation>(Loc), State.Env);
756+
});
757+
if (Expr->isPRValue()) {
758+
auto &ResultLoc = State.Env.getResultObjectLocation(*Expr);
759+
copyRecord(cast<RecordStorageLocation>(Loc), ResultLoc, State.Env);
760+
} else {
761+
State.Env.setStorageLocation(*Expr, Loc);
762+
}
763+
}
764+
765+
static void handleConstStatusOrPointerAccessorMemberCall(
766+
const CallExpr *Expr, RecordStorageLocation *RecordLoc,
767+
const MatchFinder::MatchResult &Result, LatticeTransferState &State) {
768+
if (RecordLoc == nullptr)
769+
return;
770+
auto *Val = State.Lattice.getOrCreateConstMethodReturnValue(*RecordLoc, Expr,
771+
State.Env);
772+
State.Env.setValue(*Expr, *Val);
773+
}
774+
775+
static void
776+
transferConstStatusOrAccessorMemberCall(const CXXMemberCallExpr *Expr,
777+
const MatchFinder::MatchResult &Result,
778+
LatticeTransferState &State) {
779+
handleConstStatusOrAccessorMemberCall(
780+
Expr, getImplicitObjectLocation(*Expr, State.Env), Result, State);
781+
}
782+
783+
static void transferConstStatusOrAccessorMemberOperatorCall(
784+
const CXXOperatorCallExpr *Expr, const MatchFinder::MatchResult &Result,
785+
LatticeTransferState &State) {
786+
auto *RecordLoc = cast_or_null<RecordStorageLocation>(
787+
State.Env.getStorageLocation(*Expr->getArg(0)));
788+
handleConstStatusOrAccessorMemberCall(Expr, RecordLoc, Result, State);
789+
}
790+
791+
static void transferConstStatusOrPointerAccessorMemberCall(
792+
const CXXMemberCallExpr *Expr, const MatchFinder::MatchResult &Result,
793+
LatticeTransferState &State) {
794+
handleConstStatusOrPointerAccessorMemberCall(
795+
Expr, getImplicitObjectLocation(*Expr, State.Env), Result, State);
796+
}
797+
798+
static void transferConstStatusOrPointerAccessorMemberOperatorCall(
799+
const CXXOperatorCallExpr *Expr, const MatchFinder::MatchResult &Result,
800+
LatticeTransferState &State) {
801+
auto *RecordLoc = cast_or_null<RecordStorageLocation>(
802+
State.Env.getStorageLocation(*Expr->getArg(0)));
803+
handleConstStatusOrPointerAccessorMemberCall(Expr, RecordLoc, Result, State);
804+
}
805+
806+
static void transferStatusOrReturningCall(const CallExpr *Expr,
807+
LatticeTransferState &State) {
808+
RecordStorageLocation *StatusOrLoc =
809+
Expr->isPRValue() ? &State.Env.getResultObjectLocation(*Expr)
810+
: State.Env.get<RecordStorageLocation>(*Expr);
811+
if (StatusOrLoc != nullptr &&
812+
State.Env.getValue(locForOk(locForStatus(*StatusOrLoc))) == nullptr)
813+
initializeStatusOr(*StatusOrLoc, State.Env);
814+
}
815+
816+
static void handleNonConstMemberCall(const CallExpr *Expr,
817+
RecordStorageLocation *RecordLoc,
818+
const MatchFinder::MatchResult &Result,
819+
LatticeTransferState &State) {
820+
if (RecordLoc == nullptr)
821+
return;
822+
State.Lattice.clearConstMethodReturnValues(*RecordLoc);
823+
State.Lattice.clearConstMethodReturnStorageLocations(*RecordLoc);
824+
825+
if (isStatusOrType(Expr->getType()))
826+
transferStatusOrReturningCall(Expr, State);
827+
}
828+
829+
static void transferNonConstMemberCall(const CXXMemberCallExpr *Expr,
830+
const MatchFinder::MatchResult &Result,
831+
LatticeTransferState &State) {
832+
handleNonConstMemberCall(Expr, getImplicitObjectLocation(*Expr, State.Env),
833+
Result, State);
834+
}
835+
836+
static void
837+
transferNonConstMemberOperatorCall(const CXXOperatorCallExpr *Expr,
838+
const MatchFinder::MatchResult &Result,
839+
LatticeTransferState &State) {
840+
auto *RecordLoc = cast_or_null<RecordStorageLocation>(
841+
State.Env.getStorageLocation(*Expr->getArg(0)));
842+
handleNonConstMemberCall(Expr, RecordLoc, Result, State);
843+
}
700844

701845
CFGMatchSwitch<LatticeTransferState>
702846
buildTransferMatchSwitch(ASTContext &Ctx,
@@ -755,6 +899,23 @@ buildTransferMatchSwitch(ASTContext &Ctx,
755899
transferLoggingGetReferenceableValueCall)
756900
.CaseOfCFGStmt<CallExpr>(isLoggingCheckEqImpl(),
757901
transferLoggingCheckEqImpl)
902+
// const accessor calls
903+
.CaseOfCFGStmt<CXXMemberCallExpr>(isConstStatusOrAccessorMemberCall(),
904+
transferConstStatusOrAccessorMemberCall)
905+
.CaseOfCFGStmt<CXXOperatorCallExpr>(
906+
isConstStatusOrAccessorMemberOperatorCall(),
907+
transferConstStatusOrAccessorMemberOperatorCall)
908+
.CaseOfCFGStmt<CXXMemberCallExpr>(
909+
isConstStatusOrPointerAccessorMemberCall(),
910+
transferConstStatusOrPointerAccessorMemberCall)
911+
.CaseOfCFGStmt<CXXOperatorCallExpr>(
912+
isConstStatusOrPointerAccessorMemberOperatorCall(),
913+
transferConstStatusOrPointerAccessorMemberOperatorCall)
914+
// non-const member calls that may modify the state of an object.
915+
.CaseOfCFGStmt<CXXMemberCallExpr>(isNonConstMemberCall(),
916+
transferNonConstMemberCall)
917+
.CaseOfCFGStmt<CXXOperatorCallExpr>(isNonConstMemberOperatorCall(),
918+
transferNonConstMemberOperatorCall)
758919
// N.B. These need to come after all other CXXConstructExpr.
759920
// These are there to make sure that every Status and StatusOr object
760921
// have their ok boolean initialized when constructed. If we were to

clang/unittests/Analysis/FlowSensitive/MockHeaders.cpp

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2232,6 +2232,95 @@ using testing::AssertionResult;
22322232
#endif // TESTING_DEFS_H
22332233
)cc";
22342234

2235+
constexpr const char StdUniquePtrHeader[] = R"cc(
2236+
namespace std {
2237+
2238+
template <typename T>
2239+
struct default_delete {};
2240+
2241+
template <typename T, typename D = default_delete<T>>
2242+
class unique_ptr {
2243+
public:
2244+
using element_type = T;
2245+
using deleter_type = D;
2246+
2247+
constexpr unique_ptr();
2248+
constexpr unique_ptr(nullptr_t) noexcept;
2249+
unique_ptr(unique_ptr&&);
2250+
explicit unique_ptr(T*);
2251+
template <typename U, typename E>
2252+
unique_ptr(unique_ptr<U, E>&&);
2253+
2254+
~unique_ptr();
2255+
2256+
unique_ptr& operator=(unique_ptr&&);
2257+
template <typename U, typename E>
2258+
unique_ptr& operator=(unique_ptr<U, E>&&);
2259+
unique_ptr& operator=(nullptr_t);
2260+
2261+
void reset(T* = nullptr) noexcept;
2262+
T* release();
2263+
T* get() const;
2264+
2265+
T& operator*() const;
2266+
T* operator->() const;
2267+
explicit operator bool() const noexcept;
2268+
};
2269+
2270+
template <typename T, typename D>
2271+
class unique_ptr<T[], D> {
2272+
public:
2273+
T* get() const;
2274+
T& operator[](size_t i);
2275+
const T& operator[](size_t i) const;
2276+
};
2277+
2278+
template <typename T, typename... Args>
2279+
unique_ptr<T> make_unique(Args&&...);
2280+
2281+
template <class T, class D>
2282+
void swap(unique_ptr<T, D>& x, unique_ptr<T, D>& y) noexcept;
2283+
2284+
template <class T1, class D1, class T2, class D2>
2285+
bool operator==(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y);
2286+
template <class T1, class D1, class T2, class D2>
2287+
bool operator!=(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y);
2288+
template <class T1, class D1, class T2, class D2>
2289+
bool operator<(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y);
2290+
template <class T1, class D1, class T2, class D2>
2291+
bool operator<=(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y);
2292+
template <class T1, class D1, class T2, class D2>
2293+
bool operator>(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y);
2294+
template <class T1, class D1, class T2, class D2>
2295+
bool operator>=(const unique_ptr<T1, D1>& x, const unique_ptr<T2, D2>& y);
2296+
2297+
template <class T, class D>
2298+
bool operator==(const unique_ptr<T, D>& x, nullptr_t) noexcept;
2299+
template <class T, class D>
2300+
bool operator==(nullptr_t, const unique_ptr<T, D>& y) noexcept;
2301+
template <class T, class D>
2302+
bool operator!=(const unique_ptr<T, D>& x, nullptr_t) noexcept;
2303+
template <class T, class D>
2304+
bool operator!=(nullptr_t, const unique_ptr<T, D>& y) noexcept;
2305+
template <class T, class D>
2306+
bool operator<(const unique_ptr<T, D>& x, nullptr_t);
2307+
template <class T, class D>
2308+
bool operator<(nullptr_t, const unique_ptr<T, D>& y);
2309+
template <class T, class D>
2310+
bool operator<=(const unique_ptr<T, D>& x, nullptr_t);
2311+
template <class T, class D>
2312+
bool operator<=(nullptr_t, const unique_ptr<T, D>& y);
2313+
template <class T, class D>
2314+
bool operator>(const unique_ptr<T, D>& x, nullptr_t);
2315+
template <class T, class D>
2316+
bool operator>(nullptr_t, const unique_ptr<T, D>& y);
2317+
template <class T, class D>
2318+
bool operator>=(const unique_ptr<T, D>& x, nullptr_t);
2319+
template <class T, class D>
2320+
bool operator>=(nullptr_t, const unique_ptr<T, D>& y);
2321+
}
2322+
)cc";
2323+
22352324
std::vector<std::pair<std::string, std::string>> getMockHeaders() {
22362325
std::vector<std::pair<std::string, std::string>> Headers;
22372326
Headers.emplace_back("cstddef.h", CStdDefHeader);
@@ -2249,6 +2338,7 @@ std::vector<std::pair<std::string, std::string>> getMockHeaders() {
22492338
Headers.emplace_back("statusor_defs.h", StatusOrDefsHeader);
22502339
Headers.emplace_back("absl_log.h", AbslLogHeader);
22512340
Headers.emplace_back("testing_defs.h", TestingDefsHeader);
2341+
Headers.emplace_back("std_unique_ptr.h", StdUniquePtrHeader);
22522342
return Headers;
22532343
}
22542344

0 commit comments

Comments
 (0)