I suspect many of them must slipped under the radar in CI because they were absent in diff (like redundant includes). Other cases must be some fixed FN in existing checks.
128 lines
4.8 KiB
C++
128 lines
4.8 KiB
C++
//===----------------------------------------------------------------------===//
|
|
//
|
|
// 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"
|
|
|
|
using namespace clang::ast_matchers;
|
|
|
|
namespace clang::tidy::readability {
|
|
|
|
constexpr StringRef ContainerExprName = "container-expr";
|
|
constexpr StringRef DerefContainerExprName = "deref-container-expr";
|
|
constexpr StringRef AddrOfContainerExprName = "addr-of-container-expr";
|
|
constexpr StringRef 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::matchesAnyListedRegexName(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(
|
|
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;
|
|
|
|
const SourceRange SrcRange = CE->getSourceRange();
|
|
|
|
std::string ReplacementText{
|
|
Lexer::getSourceText(CharSourceRange::getTokenRange(SrcRange),
|
|
*Result.SourceManager, getLangOpts())};
|
|
|
|
const auto *OpCall = dyn_cast<CXXOperatorCallExpr>(CE);
|
|
const bool NeedsParens =
|
|
OpCall ? (OpCall->getOperator() != OO_Subscript)
|
|
: !isa<DeclRefExpr, MemberExpr, ArraySubscriptExpr, CallExpr>(CE);
|
|
if (NeedsParens)
|
|
ReplacementText = "(" + ReplacementText + ")";
|
|
|
|
if (CE->getType()->isPointerType())
|
|
ReplacementText += "->data()";
|
|
else
|
|
ReplacementText += ".data()";
|
|
|
|
const 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
|