//===--- 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 static T getInnerTypeLocAs(TypeLoc Loc) { T Result; while (Result.isNull() && !Loc.isNull()) { Result = Loc.getAs(); Loc = Loc.getNextTypeLoc(); } return Result; } void UseTransparentFunctorsCheck::check( const MatchFinder::MatchResult &Result) { const auto *FuncClass = Result.Nodes.getNodeAs("FunctorClass"); if (const auto *FuncInst = Result.Nodes.getNodeAs("FuncInst")) { diag(FuncInst->getBeginLoc(), Message) << FuncClass->getName(); return; } const auto *Functor = Result.Nodes.getNodeAs("Functor"); const auto FunctorParentLoc = Result.Nodes.getNodeAs("FunctorParentLoc") ->getAs(); if (!FunctorParentLoc) return; unsigned ArgNum = 0; const auto *FunctorParentType = FunctorParentLoc.getType()->castAs(); for (; ArgNum < FunctorParentType->template_arguments().size(); ++ArgNum) { const TemplateArgument &Arg = FunctorParentType->template_arguments()[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->template_arguments().size()) return; TemplateArgumentLoc FunctorLoc = FunctorParentLoc.getArgLoc(ArgNum); auto FunctorTypeLoc = getInnerTypeLocAs( 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