|
24 | 24 | #include "clang/Analysis/FlowSensitive/DataflowAnalysis.h" |
25 | 25 | #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" |
26 | 26 | #include "clang/Analysis/FlowSensitive/MatchSwitch.h" |
| 27 | +#include "clang/Analysis/FlowSensitive/RecordOps.h" |
27 | 28 | #include "clang/Analysis/FlowSensitive/StorageLocation.h" |
28 | 29 | #include "clang/Analysis/FlowSensitive/Value.h" |
29 | 30 | #include "clang/Basic/LLVM.h" |
@@ -95,6 +96,18 @@ static QualType getStatusOrValueType(ClassTemplateSpecializationDecl *TRD) { |
95 | 96 | return TRD->getTemplateArgs().get(0).getAsType(); |
96 | 97 | } |
97 | 98 |
|
| 99 | +static auto ofClassStatus() { |
| 100 | + using namespace ::clang::ast_matchers; // NOLINT: Too many names |
| 101 | + return ofClass(hasName("::absl::Status")); |
| 102 | +} |
| 103 | + |
| 104 | +static auto isStatusMemberCallWithName(llvm::StringRef member_name) { |
| 105 | + using namespace ::clang::ast_matchers; // NOLINT: Too many names |
| 106 | + return cxxMemberCallExpr( |
| 107 | + on(expr(unless(cxxThisExpr()))), |
| 108 | + callee(cxxMethodDecl(hasName(member_name), ofClassStatus()))); |
| 109 | +} |
| 110 | + |
98 | 111 | static auto isStatusOrMemberCallWithName(llvm::StringRef member_name) { |
99 | 112 | using namespace ::clang::ast_matchers; // NOLINT: Too many names |
100 | 113 | return cxxMemberCallExpr( |
@@ -244,13 +257,74 @@ static void transferStatusOrOkCall(const CXXMemberCallExpr *Expr, |
244 | 257 | State.Env.setValue(*Expr, OkVal); |
245 | 258 | } |
246 | 259 |
|
| 260 | +static void transferStatusCall(const CXXMemberCallExpr *Expr, |
| 261 | + const MatchFinder::MatchResult &, |
| 262 | + LatticeTransferState &State) { |
| 263 | + RecordStorageLocation *StatusOrLoc = |
| 264 | + getImplicitObjectLocation(*Expr, State.Env); |
| 265 | + if (StatusOrLoc == nullptr) |
| 266 | + return; |
| 267 | + |
| 268 | + RecordStorageLocation &StatusLoc = locForStatus(*StatusOrLoc); |
| 269 | + |
| 270 | + if (State.Env.getValue(locForOk(StatusLoc)) == nullptr) |
| 271 | + initializeStatusOr(*StatusOrLoc, State.Env); |
| 272 | + |
| 273 | + if (Expr->isPRValue()) |
| 274 | + copyRecord(StatusLoc, State.Env.getResultObjectLocation(*Expr), State.Env); |
| 275 | + else |
| 276 | + State.Env.setStorageLocation(*Expr, StatusLoc); |
| 277 | +} |
| 278 | + |
| 279 | +static void transferStatusOkCall(const CXXMemberCallExpr *Expr, |
| 280 | + const MatchFinder::MatchResult &, |
| 281 | + LatticeTransferState &State) { |
| 282 | + RecordStorageLocation *StatusLoc = |
| 283 | + getImplicitObjectLocation(*Expr, State.Env); |
| 284 | + if (StatusLoc == nullptr) |
| 285 | + return; |
| 286 | + |
| 287 | + if (Value *Val = State.Env.getValue(locForOk(*StatusLoc))) |
| 288 | + State.Env.setValue(*Expr, *Val); |
| 289 | +} |
| 290 | + |
| 291 | +static void transferStatusUpdateCall(const CXXMemberCallExpr *Expr, |
| 292 | + const MatchFinder::MatchResult &, |
| 293 | + LatticeTransferState &State) { |
| 294 | + // S.Update(OtherS) sets S to the error code of OtherS if it is OK, |
| 295 | + // otherwise does nothing. |
| 296 | + assert(Expr->getNumArgs() == 1); |
| 297 | + auto *Arg = Expr->getArg(0); |
| 298 | + RecordStorageLocation *ArgRecord = |
| 299 | + Arg->isPRValue() ? &State.Env.getResultObjectLocation(*Arg) |
| 300 | + : State.Env.get<RecordStorageLocation>(*Arg); |
| 301 | + RecordStorageLocation *ThisLoc = getImplicitObjectLocation(*Expr, State.Env); |
| 302 | + if (ThisLoc == nullptr || ArgRecord == nullptr) |
| 303 | + return; |
| 304 | + |
| 305 | + auto &ThisOkVal = valForOk(*ThisLoc, State.Env); |
| 306 | + auto &ArgOkVal = valForOk(*ArgRecord, State.Env); |
| 307 | + auto &A = State.Env.arena(); |
| 308 | + auto &NewVal = State.Env.makeAtomicBoolValue(); |
| 309 | + State.Env.assume(A.makeImplies(A.makeNot(ThisOkVal.formula()), |
| 310 | + A.makeNot(NewVal.formula()))); |
| 311 | + State.Env.assume(A.makeImplies(NewVal.formula(), ArgOkVal.formula())); |
| 312 | + State.Env.setValue(locForOk(*ThisLoc), NewVal); |
| 313 | +} |
| 314 | + |
247 | 315 | CFGMatchSwitch<LatticeTransferState> |
248 | 316 | buildTransferMatchSwitch(ASTContext &Ctx, |
249 | 317 | CFGMatchSwitchBuilder<LatticeTransferState> Builder) { |
250 | 318 | using namespace ::clang::ast_matchers; // NOLINT: Too many names |
251 | 319 | return std::move(Builder) |
252 | 320 | .CaseOfCFGStmt<CXXMemberCallExpr>(isStatusOrMemberCallWithName("ok"), |
253 | 321 | transferStatusOrOkCall) |
| 322 | + .CaseOfCFGStmt<CXXMemberCallExpr>(isStatusOrMemberCallWithName("status"), |
| 323 | + transferStatusCall) |
| 324 | + .CaseOfCFGStmt<CXXMemberCallExpr>(isStatusMemberCallWithName("ok"), |
| 325 | + transferStatusOkCall) |
| 326 | + .CaseOfCFGStmt<CXXMemberCallExpr>(isStatusMemberCallWithName("Update"), |
| 327 | + transferStatusUpdateCall) |
254 | 328 | .Build(); |
255 | 329 | } |
256 | 330 |
|
|
0 commit comments