| // Part of the Crubit project, under the Apache License v2.0 with LLVM | |
| // Exceptions. See /LICENSE for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| #include "nullability/pointer_nullability_lattice.h" | |
| #include <cassert> | |
| #include <memory> | |
| #include "absl/base/nullability.h" | |
| #include "clang/AST/Decl.h" | |
| #include "clang/AST/DeclBase.h" | |
| #include "clang/AST/DeclCXX.h" | |
| #include "clang/AST/Expr.h" | |
| #include "clang/AST/Type.h" | |
| #include "clang/ASTMatchers/ASTMatchFinder.h" | |
| #include "clang/ASTMatchers/ASTMatchers.h" | |
| #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h" | |
| #include "clang/Analysis/FlowSensitive/DataflowLattice.h" | |
| #include "clang/Analysis/FlowSensitive/StorageLocation.h" | |
| #include "clang/Analysis/FlowSensitive/Value.h" | |
| #include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h" | |
| #include "clang/Basic/LLVM.h" | |
| #include "clang/Testing/TestAST.h" | |
| #include "external/llvm-project/third-party/unittest/googletest/include/gtest/gtest.h" | |
| namespace clang::tidy::nullability { | |
| namespace { | |
| using ast_matchers::callee; | |
| using ast_matchers::cxxMemberCallExpr; | |
| using ast_matchers::functionDecl; | |
| using ast_matchers::hasName; | |
| using ast_matchers::match; | |
| using ast_matchers::selectFirst; | |
| using dataflow::DataflowAnalysisContext; | |
| using dataflow::Environment; | |
| using dataflow::LatticeJoinEffect; | |
| using dataflow::RecordStorageLocation; | |
| using dataflow::Value; | |
| using dataflow::WatchedLiteralsSolver; | |
| NamedDecl *absl_nonnull lookup(StringRef Name, const DeclContext &DC) { | |
| auto Result = DC.lookup(&DC.getParentASTContext().Idents.get(Name)); | |
| EXPECT_TRUE(Result.isSingleResult()) << Name; | |
| return Result.front(); | |
| } | |
| class PointerNullabilityLatticeTest : public ::testing::Test { | |
| protected: | |
| DataflowAnalysisContext DACtx{std::make_unique<WatchedLiteralsSolver>()}; | |
| Environment Env{DACtx}; | |
| PointerNullabilityLattice::NonFlowSensitiveState NFS; | |
| }; | |
| TEST_F(PointerNullabilityLatticeTest, ConstMethodProducesNewValueAfterJoin) { | |
| TestAST AST(R"cpp( | |
| struct S { | |
| int *property() const; | |
| }; | |
| void target() { | |
| S s; | |
| s.property(); | |
| } | |
| )cpp"); | |
| auto *SDecl = | |
| cast<CXXRecordDecl>(lookup("S", *AST.context().getTranslationUnitDecl())); | |
| QualType SType = AST.context().getCanonicalTagType(SDecl); | |
| auto *CE = selectFirst<CallExpr>( | |
| "call", match(cxxMemberCallExpr(callee(functionDecl(hasName("property")))) | |
| .bind("call"), | |
| AST.context())); | |
| assert(CE != nullptr); | |
| RecordStorageLocation Loc(SType, RecordStorageLocation::FieldToLoc(), {}); | |
| PointerNullabilityLattice Lattice1(NFS); | |
| Value *Val1 = Lattice1.getOrCreateConstMethodReturnValue(Loc, CE, Env); | |
| PointerNullabilityLattice Lattice2(NFS); | |
| Value *Val2 = Lattice2.getOrCreateConstMethodReturnValue(Loc, CE, Env); | |
| EXPECT_EQ(Lattice1.join(Lattice2), LatticeJoinEffect::Changed); | |
| Value *ValAfterJoin = | |
| Lattice1.getOrCreateConstMethodReturnValue(Loc, CE, Env); | |
| EXPECT_NE(ValAfterJoin, Val1); | |
| EXPECT_NE(ValAfterJoin, Val2); | |
| } | |
| } // namespace | |
| } // namespace clang::tidy::nullability |