void InefficientAlgorithmCheck::check(const MatchFinder::MatchResult &Result) { const auto *AlgCall = Result.Nodes.getNodeAs<CallExpr>("IneffAlg"); const auto *IneffCont = Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("IneffCont"); bool PtrToContainer = false; if (!IneffCont) { IneffCont = Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("IneffContPtr"); PtrToContainer = true; } const llvm::StringRef IneffContName = IneffCont->getName(); const bool Unordered = IneffContName.find("unordered") != llvm::StringRef::npos; const bool Maplike = IneffContName.find("map") != llvm::StringRef::npos; // Store if the key type of the container is compatible with the value // that is searched for. QualType ValueType = AlgCall->getArg(2)->getType(); QualType KeyType = IneffCont->getTemplateArgs()[0].getAsType().getCanonicalType(); const bool CompatibleTypes = areTypesCompatible(KeyType, ValueType); // Check if the comparison type for the algorithm and the container matches. if (AlgCall->getNumArgs() == 4 && !Unordered) { const Expr *Arg = AlgCall->getArg(3); const QualType AlgCmp = Arg->getType().getUnqualifiedType().getCanonicalType(); const unsigned CmpPosition = (IneffContName.find("map") == llvm::StringRef::npos) ? 1 : 2; const QualType ContainerCmp = IneffCont->getTemplateArgs()[CmpPosition] .getAsType() .getUnqualifiedType() .getCanonicalType(); if (AlgCmp != ContainerCmp) { diag(Arg->getLocStart(), "different comparers used in the algorithm and the container"); return; } } const auto *AlgDecl = AlgCall->getDirectCallee(); if (!AlgDecl) return; if (Unordered && AlgDecl->getName().find("bound") != llvm::StringRef::npos) return; const auto *AlgParam = Result.Nodes.getNodeAs<Expr>("AlgParam"); const auto *IneffContExpr = Result.Nodes.getNodeAs<Expr>("IneffContExpr"); FixItHint Hint; SourceManager &SM = *Result.SourceManager; LangOptions LangOpts = Result.Context->getLangOpts(); CharSourceRange CallRange = CharSourceRange::getTokenRange(AlgCall->getSourceRange()); // FIXME: Create a common utility to extract a file range that the given token // sequence is exactly spelled at (without macro argument expansions etc.). // We can't use Lexer::makeFileCharRange here, because for // // #define F(x) x // x(a b c); // // it will return "x(a b c)", when given the range "a"-"c". It makes sense for // removals, but not for replacements. // // This code is over-simplified, but works for many real cases. if (SM.isMacroArgExpansion(CallRange.getBegin()) && SM.isMacroArgExpansion(CallRange.getEnd())) { CallRange.setBegin(SM.getSpellingLoc(CallRange.getBegin())); CallRange.setEnd(SM.getSpellingLoc(CallRange.getEnd())); } if (!CallRange.getBegin().isMacroID() && !Maplike && CompatibleTypes) { StringRef ContainerText = Lexer::getSourceText( CharSourceRange::getTokenRange(IneffContExpr->getSourceRange()), SM, LangOpts); StringRef ParamText = Lexer::getSourceText( CharSourceRange::getTokenRange(AlgParam->getSourceRange()), SM, LangOpts); std::string ReplacementText = (llvm::Twine(ContainerText) + (PtrToContainer ? "->" : ".") + AlgDecl->getName() + "(" + ParamText + ")") .str(); Hint = FixItHint::CreateReplacement(CallRange, ReplacementText); } diag(AlgCall->getLocStart(), "this STL algorithm call should be replaced with a container method") << Hint; }