132 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			132 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			C++
		
	
	
	
//===--- UseTransparentFunctorsCheck.cpp - clang-tidy----------------------===//
 | 
						|
//
 | 
						|
//                     The LLVM Compiler Infrastructure
 | 
						|
//
 | 
						|
// This file is distributed under the University of Illinois Open Source
 | 
						|
// License. See LICENSE.TXT for details.
 | 
						|
//
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
 | 
						|
#include "UseTransparentFunctorsCheck.h"
 | 
						|
#include "clang/AST/ASTContext.h"
 | 
						|
#include "clang/ASTMatchers/ASTMatchFinder.h"
 | 
						|
 | 
						|
using namespace clang::ast_matchers;
 | 
						|
 | 
						|
namespace clang {
 | 
						|
namespace tidy {
 | 
						|
namespace modernize {
 | 
						|
 | 
						|
UseTransparentFunctorsCheck::UseTransparentFunctorsCheck(
 | 
						|
    StringRef Name, ClangTidyContext *Context)
 | 
						|
    : ClangTidyCheck(Name, Context), SafeMode(Options.get("SafeMode", 0)) {}
 | 
						|
 | 
						|
void UseTransparentFunctorsCheck::storeOptions(
 | 
						|
    ClangTidyOptions::OptionMap &Opts) {
 | 
						|
  Options.store(Opts, "SafeMode", SafeMode ? 1 : 0);
 | 
						|
}
 | 
						|
 | 
						|
void UseTransparentFunctorsCheck::registerMatchers(MatchFinder *Finder) {
 | 
						|
  if (!getLangOpts().CPlusPlus14)
 | 
						|
    return;
 | 
						|
 | 
						|
  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->getLocStart(), Message)
 | 
						|
          << (FuncClass->getName() + "<>").str();
 | 
						|
    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->getNumArgs(); ++ArgNum) {
 | 
						|
    const TemplateArgument &Arg = FunctorParentType->getArg(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->getNumArgs())
 | 
						|
    return;
 | 
						|
  TemplateArgumentLoc FunctorLoc = FunctorParentLoc.getArgLoc(ArgNum);
 | 
						|
  auto FunctorTypeLoc = getInnerTypeLocAs<TemplateSpecializationTypeLoc>(
 | 
						|
      FunctorLoc.getTypeSourceInfo()->getTypeLoc());
 | 
						|
  if (FunctorTypeLoc.isNull())
 | 
						|
    return;
 | 
						|
 | 
						|
  SourceLocation ReportLoc = FunctorLoc.getLocation();
 | 
						|
  diag(ReportLoc, Message) << (FuncClass->getName() + "<>").str()
 | 
						|
                           << FixItHint::CreateRemoval(
 | 
						|
                                  FunctorTypeLoc.getArgLoc(0).getSourceRange());
 | 
						|
}
 | 
						|
 | 
						|
} // namespace modernize
 | 
						|
} // namespace tidy
 | 
						|
} // namespace clang
 |