blob: a05e228520c9ef1b05aaa55087d7baa7f7564cd8 [file] [log] [blame]
//===--- ContainerDataPointerCheck.cpp - clang-tidy -----------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "ContainerDataPointerCheck.h"
#include "../utils/Matchers.h"
#include "../utils/OptionsUtils.h"
#include "clang/Lex/Lexer.h"
#include "llvm/ADT/StringRef.h"
using namespace clang::ast_matchers;
namespace clang::tidy::readability {
constexpr llvm::StringLiteral ContainerExprName = "container-expr";
constexpr llvm::StringLiteral DerefContainerExprName = "deref-container-expr";
constexpr llvm::StringLiteral AddrOfContainerExprName =
"addr-of-container-expr";
constexpr llvm::StringLiteral AddressOfName = "address-of";
void ContainerDataPointerCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "IgnoredContainers",
utils::options::serializeStringList(IgnoredContainers));
}
ContainerDataPointerCheck::ContainerDataPointerCheck(StringRef Name,
ClangTidyContext *Context)
: ClangTidyCheck(Name, Context),
IgnoredContainers(utils::options::parseStringList(
Options.get("IgnoredContainers", ""))) {}
void ContainerDataPointerCheck::registerMatchers(MatchFinder *Finder) {
const auto Record =
cxxRecordDecl(
unless(matchers::matchesAnyListedName(IgnoredContainers)),
isSameOrDerivedFrom(
namedDecl(
has(cxxMethodDecl(isPublic(), hasName("data")).bind("data")))
.bind("container")))
.bind("record");
const auto NonTemplateContainerType =
qualType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(Record))));
const auto TemplateContainerType =
qualType(hasUnqualifiedDesugaredType(templateSpecializationType(
hasDeclaration(classTemplateDecl(has(Record))))));
const auto Container =
qualType(anyOf(NonTemplateContainerType, TemplateContainerType));
const auto ContainerExpr = anyOf(
unaryOperator(
hasOperatorName("*"),
hasUnaryOperand(
expr(hasType(pointsTo(Container))).bind(DerefContainerExprName)))
.bind(ContainerExprName),
unaryOperator(hasOperatorName("&"),
hasUnaryOperand(expr(anyOf(hasType(Container),
hasType(references(Container))))
.bind(AddrOfContainerExprName)))
.bind(ContainerExprName),
expr(anyOf(hasType(Container), hasType(pointsTo(Container)),
hasType(references(Container))))
.bind(ContainerExprName));
const auto Zero = integerLiteral(equals(0));
const auto SubscriptOperator = callee(cxxMethodDecl(hasName("operator[]")));
Finder->addMatcher(
unaryOperator(
unless(isExpansionInSystemHeader()), hasOperatorName("&"),
hasUnaryOperand(expr(
anyOf(cxxOperatorCallExpr(SubscriptOperator, argumentCountIs(2),
hasArgument(0, ContainerExpr),
hasArgument(1, Zero)),
cxxMemberCallExpr(SubscriptOperator, on(ContainerExpr),
argumentCountIs(1), hasArgument(0, Zero)),
arraySubscriptExpr(hasLHS(ContainerExpr), hasRHS(Zero))))))
.bind(AddressOfName),
this);
}
void ContainerDataPointerCheck::check(const MatchFinder::MatchResult &Result) {
const auto *UO = Result.Nodes.getNodeAs<UnaryOperator>(AddressOfName);
const auto *CE = Result.Nodes.getNodeAs<Expr>(ContainerExprName);
const auto *DCE = Result.Nodes.getNodeAs<Expr>(DerefContainerExprName);
const auto *ACE = Result.Nodes.getNodeAs<Expr>(AddrOfContainerExprName);
if (!UO || !CE)
return;
if (DCE && !CE->getType()->isPointerType())
CE = DCE;
else if (ACE)
CE = ACE;
SourceRange SrcRange = CE->getSourceRange();
std::string ReplacementText{
Lexer::getSourceText(CharSourceRange::getTokenRange(SrcRange),
*Result.SourceManager, getLangOpts())};
if (!isa<DeclRefExpr, ArraySubscriptExpr, CXXOperatorCallExpr, CallExpr,
MemberExpr>(CE))
ReplacementText = "(" + ReplacementText + ")";
if (CE->getType()->isPointerType())
ReplacementText += "->data()";
else
ReplacementText += ".data()";
FixItHint Hint =
FixItHint::CreateReplacement(UO->getSourceRange(), ReplacementText);
diag(UO->getBeginLoc(),
"'data' should be used for accessing the data pointer instead of taking "
"the address of the 0-th element")
<< Hint;
}
} // namespace clang::tidy::readability