blob: 2b525adaa9f909131a5d2cbd2354d4158ca68495 [file] [log] [blame]
// 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 "third_party/llvm/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;
absl::Nonnull<NamedDecl *> 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().getRecordType(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.getConstMethodReturnValue(Loc, CE, Env);
PointerNullabilityLattice Lattice2(NFS);
Value *Val2 = Lattice2.getConstMethodReturnValue(Loc, CE, Env);
EXPECT_EQ(Lattice1.join(Lattice2), LatticeJoinEffect::Changed);
Value *ValAfterJoin = Lattice1.getConstMethodReturnValue(Loc, CE, Env);
EXPECT_NE(ValAfterJoin, Val1);
EXPECT_NE(ValAfterJoin, Val2);
}
} // namespace
} // namespace clang::tidy::nullability