llvm-project/clang-tools-extra/clang-tidy/modernize/UseTransparentFunctorsCheck.cpp
Matheus Izvekov 5b6f71b8f6
WIP: [clang] store sugared converted arguments on TemplateSpecializationType
Not ready for review

This is a quite large patch, half of which will
be redone following a different approach.

Although it improves sugar retention in template argument
deduction on its own, this is an enabler for resugaring.

This stores the sugared converted template arguments in
a TST, in addition to the existing as-written ones,
so this is quite wasteful.

This is the biggest performance impact on the whole
of resugaring so far, although it is hoped the
new approach will have negligible impact.

This is a continuation of https://reviews.llvm.org/D134113
2025-04-03 14:29:45 -03:00

126 lines
4.8 KiB
C++

//===--- UseTransparentFunctorsCheck.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 "UseTransparentFunctorsCheck.h"
#include "clang/AST/ASTContext.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
using namespace clang::ast_matchers;
namespace clang::tidy::modernize {
UseTransparentFunctorsCheck::UseTransparentFunctorsCheck(
StringRef Name, ClangTidyContext *Context)
: ClangTidyCheck(Name, Context), SafeMode(Options.get("SafeMode", false)) {}
void UseTransparentFunctorsCheck::storeOptions(
ClangTidyOptions::OptionMap &Opts) {
Options.store(Opts, "SafeMode", SafeMode);
}
void UseTransparentFunctorsCheck::registerMatchers(MatchFinder *Finder) {
const auto TransparentFunctors =
classTemplateSpecializationDecl(
unless(hasAnyTemplateArgument(refersToType(voidType()))),
hasAnyName("::std::plus", "::std::minus", "::std::multiplies",
"::std::divides", "::std::modulus", "::std::negate",
"::std::equal_to", "::std::not_equal_to", "::std::greater",
"::std::less", "::std::greater_equal", "::std::less_equal",
"::std::logical_and", "::std::logical_or",
"::std::logical_not", "::std::bit_and", "::std::bit_or",
"::std::bit_xor", "::std::bit_not"))
.bind("FunctorClass");
// Non-transparent functor mentioned as a template parameter. FIXIT.
Finder->addMatcher(
loc(qualType(
unless(elaboratedType()),
hasDeclaration(classTemplateSpecializationDecl(
unless(hasAnyTemplateArgument(templateArgument(refersToType(
qualType(pointsTo(qualType(isAnyCharacter()))))))),
hasAnyTemplateArgument(
templateArgument(refersToType(qualType(hasDeclaration(
TransparentFunctors))))
.bind("Functor"))))))
.bind("FunctorParentLoc"),
this);
if (SafeMode)
return;
// Non-transparent functor constructed. No FIXIT. There is no easy way
// to rule out the problematic char* vs string case.
Finder->addMatcher(cxxConstructExpr(hasDeclaration(cxxMethodDecl(
ofClass(TransparentFunctors))),
unless(isInTemplateInstantiation()))
.bind("FuncInst"),
this);
}
static const StringRef Message = "prefer transparent functors '%0<>'";
template <typename T> static T getInnerTypeLocAs(TypeLoc Loc) {
T Result;
while (Result.isNull() && !Loc.isNull()) {
Result = Loc.getAs<T>();
Loc = Loc.getNextTypeLoc();
}
return Result;
}
void UseTransparentFunctorsCheck::check(
const MatchFinder::MatchResult &Result) {
const auto *FuncClass =
Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("FunctorClass");
if (const auto *FuncInst =
Result.Nodes.getNodeAs<CXXConstructExpr>("FuncInst")) {
diag(FuncInst->getBeginLoc(), Message) << FuncClass->getName();
return;
}
const auto *Functor = Result.Nodes.getNodeAs<TemplateArgument>("Functor");
const auto FunctorParentLoc =
Result.Nodes.getNodeAs<TypeLoc>("FunctorParentLoc")
->getAs<TemplateSpecializationTypeLoc>();
if (!FunctorParentLoc)
return;
unsigned ArgNum = 0;
const auto *FunctorParentType =
FunctorParentLoc.getType()->castAs<TemplateSpecializationType>();
for (; ArgNum < FunctorParentType->getSpecifiedArguments().size(); ++ArgNum) {
const TemplateArgument &Arg =
FunctorParentType->getSpecifiedArguments()[ArgNum];
if (Arg.getKind() != TemplateArgument::Type)
continue;
QualType ParentArgType = Arg.getAsType();
if (ParentArgType->isRecordType() &&
ParentArgType->getAsCXXRecordDecl() ==
Functor->getAsType()->getAsCXXRecordDecl())
break;
}
// Functor is a default template argument.
if (ArgNum == FunctorParentType->getSpecifiedArguments().size())
return;
TemplateArgumentLoc FunctorLoc = FunctorParentLoc.getArgLoc(ArgNum);
auto FunctorTypeLoc = getInnerTypeLocAs<TemplateSpecializationTypeLoc>(
FunctorLoc.getTypeSourceInfo()->getTypeLoc());
if (FunctorTypeLoc.isNull())
return;
SourceLocation ReportLoc = FunctorLoc.getLocation();
if (ReportLoc.isInvalid())
return;
diag(ReportLoc, Message) << FuncClass->getName()
<< FixItHint::CreateRemoval(
FunctorTypeLoc.getArgLoc(0).getSourceRange());
}
} // namespace clang::tidy::modernize