mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-18 03:56:41 +00:00

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
126 lines
4.8 KiB
C++
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
|