blob: 3d9f51e2e17b0945bec574145a46a3437f9402df [file] [log] [blame]
#include "../clang-tidy/utils/DeclRefExprUtils.h"
#include "ClangTidyDiagnosticConsumer.h"
#include "ClangTidyTest.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Tooling/Tooling.h"
#include "gtest/gtest.h"
namespace clang {
namespace tidy {
namespace {
using namespace clang::ast_matchers;
template <int Indirections>
class ConstReferenceDeclRefExprsTransform : public ClangTidyCheck {
public:
ConstReferenceDeclRefExprsTransform(StringRef CheckName,
ClangTidyContext *Context)
: ClangTidyCheck(CheckName, Context) {}
void registerMatchers(MatchFinder *Finder) override {
Finder->addMatcher(varDecl(hasName("target")).bind("var"), this);
}
void check(const MatchFinder::MatchResult &Result) override {
const auto *D = Result.Nodes.getNodeAs<VarDecl>("var");
using utils::decl_ref_expr::constReferenceDeclRefExprs;
const auto const_decrefexprs = constReferenceDeclRefExprs(
*D, *cast<FunctionDecl>(D->getDeclContext())->getBody(),
*Result.Context, Indirections);
for (const DeclRefExpr *const Expr : const_decrefexprs) {
assert(Expr);
diag(Expr->getBeginLoc(), "const usage")
<< FixItHint::CreateInsertion(Expr->getBeginLoc(), "/*const*/");
}
}
};
} // namespace
namespace test {
template <int Indirections> void RunTest(StringRef Snippet) {
StringRef CommonCode = R"(
struct ConstTag{};
struct NonConstTag{};
struct S {
void constMethod() const;
void nonConstMethod();
static void staticMethod();
void operator()(ConstTag) const;
void operator()(NonConstTag);
void operator[](int);
void operator[](int) const;
bool operator==(const S&) const;
int int_member;
// We consider a mutation of the `*ptr_member` to be a const use of
// `*this`. This is consistent with the semantics of `const`-qualified
// methods, which prevent modifying `ptr_member` but not `*ptr_member`.
int* ptr_member;
};
struct Derived : public S {
};
void useVal(S);
void useRef(S&);
void usePtr(S*);
void usePtrPtr(S**);
void usePtrConstPtr(S* const*);
void useConstRef(const S&);
void useConstPtr(const S*);
void useConstPtrRef(const S*&);
void useConstPtrPtr(const S**);
void useConstPtrConstRef(const S* const&);
void useConstPtrConstPtr(const S* const*);
void useInt(int);
void useIntRef(int&);
void useIntConstRef(const int&);
void useIntPtr(int*);
void useIntConstPtr(const int*);
)";
std::string Code = (CommonCode + Snippet).str();
llvm::SmallVector<StringRef, 1> Parts;
StringRef(Code).split(Parts, "/*const*/");
EXPECT_EQ(Code,
runCheckOnCode<ConstReferenceDeclRefExprsTransform<Indirections>>(
join(Parts, "")));
}
TEST(ConstReferenceDeclRefExprsTest, ConstValueVar) {
RunTest<0>(R"(
void f(const S target) {
useVal(/*const*/target);
useConstRef(/*const*/target);
useConstPtr(&/*const*/target);
useConstPtrConstRef(&/*const*/target);
/*const*/target.constMethod();
/*const*/target.staticMethod();
/*const*/target(ConstTag{});
/*const*/target[42];
useConstRef((/*const*/target));
(/*const*/target).constMethod();
/*const*/target.staticMethod();
(void)(/*const*/target == /*const*/target);
(void)/*const*/target;
(void)&/*const*/target;
(void)*&/*const*/target;
/*const*/target;
S copy1 = /*const*/target;
S copy2(/*const*/target);
/*const*/target.int_member;
useInt(/*const*/target.int_member);
useIntConstRef(/*const*/target.int_member);
useIntPtr(/*const*/target.ptr_member);
useIntConstPtr(&/*const*/target.int_member);
const S& const_target_ref = /*const*/target;
const S* const_target_ptr = &/*const*/target;
}
)");
}
TEST(ConstReferenceDeclRefExprsTest, ConstRefVar) {
RunTest<0>(R"(
void f(const S& target) {
useVal(/*const*/target);
useConstRef(/*const*/target);
useConstPtr(&/*const*/target);
useConstPtrConstRef(&/*const*/target);
/*const*/target.constMethod();
/*const*/target.staticMethod();
/*const*/target(ConstTag{});
/*const*/target[42];
useConstRef((/*const*/target));
(/*const*/target).constMethod();
(void)(/*const*/target == /*const*/target);
(void)/*const*/target;
(void)&/*const*/target;
(void)*&/*const*/target;
/*const*/target;
S copy1 = /*const*/target;
S copy2(/*const*/target);
/*const*/target.int_member;
useInt(/*const*/target.int_member);
useIntConstRef(/*const*/target.int_member);
useIntPtr(/*const*/target.ptr_member);
useIntConstPtr(&/*const*/target.int_member);
const S& const_target_ref = /*const*/target;
const S* const_target_ptr = &/*const*/target;
}
)");
}
TEST(ConstReferenceDeclRefExprsTest, DEBUGREMOVEME) {
RunTest<0>(R"(
void f(S target, const S& other) {
S* target_ptr = &target;
}
)");
}
TEST(ConstReferenceDeclRefExprsTest, ValueVar) {
RunTest<0>(R"(
void f(S target, const S& other) {
useConstRef(/*const*/target);
useVal(/*const*/target);
useConstPtr(&/*const*/target);
useConstPtrConstRef(&/*const*/target);
/*const*/target.constMethod();
/*const*/target.staticMethod();
target.nonConstMethod();
/*const*/target(ConstTag{});
target[42];
/*const*/target(ConstTag{});
target(NonConstTag{});
useRef(target);
usePtr(&target);
useConstRef((/*const*/target));
(/*const*/target).constMethod();
(void)(/*const*/target == /*const*/target);
(void)(/*const*/target == other);
(void)/*const*/target;
(void)&/*const*/target;
(void)*&/*const*/target;
/*const*/target;
S copy1 = /*const*/target;
S copy2(/*const*/target);
/*const*/target.int_member;
useInt(/*const*/target.int_member);
useIntConstRef(/*const*/target.int_member);
useIntPtr(/*const*/target.ptr_member);
useIntConstPtr(&/*const*/target.int_member);
const S& const_target_ref = /*const*/target;
const S* const_target_ptr = &/*const*/target;
S* target_ptr = &target;
}
)");
}
TEST(ConstReferenceDeclRefExprsTest, RefVar) {
RunTest<0>(R"(
void f(S& target) {
useVal(/*const*/target);
usePtr(&target);
useConstRef(/*const*/target);
useConstPtr(&/*const*/target);
useConstPtrConstRef(&/*const*/target);
/*const*/target.constMethod();
/*const*/target.staticMethod();
target.nonConstMethod();
/*const*/target(ConstTag{});
target[42];
useConstRef((/*const*/target));
(/*const*/target).constMethod();
(void)(/*const*/target == /*const*/target);
(void)/*const*/target;
(void)&/*const*/target;
(void)*&/*const*/target;
/*const*/target;
S copy1 = /*const*/target;
S copy2(/*const*/target);
/*const*/target.int_member;
useInt(/*const*/target.int_member);
useIntConstRef(/*const*/target.int_member);
useIntPtr(/*const*/target.ptr_member);
useIntConstPtr(&/*const*/target.int_member);
(void)(&/*const*/target)->int_member;
useIntRef((&target)->int_member);
const S& const_target_ref = /*const*/target;
const S* const_target_ptr = &/*const*/target;
S* target_ptr = &target;
}
)");
}
TEST(ConstReferenceDeclRefExprsTest, PtrVar) {
RunTest<1>(R"(
void f(S* target) {
useVal(*/*const*/target);
usePtr(target);
useConstRef(*/*const*/target);
useConstPtr(/*const*/target);
useConstPtrConstRef(/*const*/target);
usePtrConstPtr(&target);
/*const*/target->constMethod();
/*const*/target->staticMethod();
target->nonConstMethod();
(*/*const*/target)(ConstTag{});
(*target)[42];
target->operator[](42);
useConstRef((*/*const*/target));
(/*const*/target)->constMethod();
(void)(*/*const*/target == */*const*/target);
(void)*/*const*/target;
(void)/*const*/target;
/*const*/target;
S copy1 = */*const*/target;
S copy2(*/*const*/target);
/*const*/target->int_member;
useInt(/*const*/target->int_member);
useIntConstRef(/*const*/target->int_member);
useIntPtr(/*const*/target->ptr_member);
useIntConstPtr(&/*const*/target->int_member);
const S& const_target_ref = */*const*/target;
const S* const_target_ptr = /*const*/target;
S* target_ptr = target; // FIXME: we could chect const usage of `target_ptr`.
}
)");
}
TEST(ConstReferenceDeclRefExprsTest, ConstPtrVar) {
RunTest<1>(R"(
void f(const S* target) {
useVal(*/*const*/target);
useConstRef(*/*const*/target);
useConstPtr(/*const*/target);
useConstPtrRef(/*const*/target);
useConstPtrPtr(&/*const*/target);
useConstPtrConstPtr(&/*const*/target);
useConstPtrConstRef(/*const*/target);
/*const*/target->constMethod();
/*const*/target->staticMethod();
(*/*const*/target)(ConstTag{});
(*/*const*/target)[42];
/*const*/target->operator[](42);
(void)(*/*const*/target == */*const*/target);
(void)/*const*/target;
(void)*/*const*/target;
/*const*/target;
if(/*const*/target) {}
S copy1 = */*const*/target;
S copy2(*/*const*/target);
/*const*/target->int_member;
useInt(/*const*/target->int_member);
useIntConstRef(/*const*/target->int_member);
useIntPtr(/*const*/target->ptr_member);
useIntConstPtr(&/*const*/target->int_member);
const S& const_target_ref = */*const*/target;
const S* const_target_ptr = /*const*/target;
}
)");
}
TEST(ConstReferenceDeclRefExprsTest, ConstPtrPtrVar) {
RunTest<2>(R"(
void f(const S** target) {
useVal(**/*const*/target);
useConstRef(**/*const*/target);
useConstPtr(*/*const*/target);
useConstPtrRef(*/*const*/target);
useConstPtrPtr(/*const*/target);
useConstPtrConstPtr(/*const*/target);
useConstPtrConstRef(*/*const*/target);
(void)/*const*/target;
(void)*/*const*/target;
(void)**/*const*/target;
/*const*/target;
if(/*const*/target) {}
if(*/*const*/target) {}
S copy1 = **/*const*/target;
S copy2(**/*const*/target);
(*/*const*/target)->int_member;
useInt((*/*const*/target)->int_member);
useIntConstRef((*/*const*/target)->int_member);
useIntPtr((*/*const*/target)->ptr_member);
useIntConstPtr(&(*/*const*/target)->int_member);
const S& const_target_ref = **/*const*/target;
const S* const_target_ptr = */*const*/target;
}
)");
}
TEST(ConstReferenceDeclRefExprsTest, ConstPtrConstPtrVar) {
RunTest<2>(R"(
void f(const S* const* target) {
useVal(**/*const*/target);
useConstRef(**/*const*/target);
useConstPtr(*/*const*/target);
useConstPtrConstPtr(/*const*/target);
useConstPtrConstRef(*/*const*/target);
(void)/*const*/target;
(void)*/*const*/target;
(void)**/*const*/target;
/*const*/target;
if(/*const*/target) {}
if(*/*const*/target) {}
S copy1 = **/*const*/target;
S copy2(**/*const*/target);
(*/*const*/target)->int_member;
useInt((*/*const*/target)->int_member);
useIntConstRef((*/*const*/target)->int_member);
useIntPtr((*/*const*/target)->ptr_member);
useIntConstPtr(&(*/*const*/target)->int_member);
const S& const_target_ref = **/*const*/target;
const S* const_target_ptr = */*const*/target;
}
)");
}
} // namespace test
} // namespace tidy
} // namespace clang