127 lines
4.8 KiB
C++
127 lines
4.8 KiB
C++
|
//===--- 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
|