bool FunctionMergePass::areTypesCompatible(const Type * from, const Type * to) { if (from->typeClass() != to->typeClass()) { if ((from->typeClass() == Type::Class || from->typeClass() == Type::Interface) && (to->typeClass() == Type::Class || to->typeClass() == Type::Interface)) { return true; } return false; } switch (from->typeClass()) { case Type::Primitive: { const PrimitiveType * pFrom = static_cast<const PrimitiveType *>(from); const PrimitiveType * pTo = static_cast<const PrimitiveType *>(to); if (pFrom->typeId() == pTo->typeId()) { return true; } return false; } case Type::Class: case Type::Interface: // Any reference type is compatible with any other. return true; case Type::Struct: return TypeRelation::isEqual(from, to); case Type::Tuple: { const TupleType * ttFrom = static_cast<const TupleType *>(from); const TupleType * ttTo = static_cast<const TupleType *>(to); if (ttFrom->members().size() != ttTo->members().size()) { return false; } size_t memberSize = ttFrom->members().size(); for (size_t i = 0; i < memberSize; ++i) { if (!areTypesCompatible(ttFrom->members()[i].type(), ttTo->members()[i].type())) { return false; } } return true; } case Type::Union: { const UnionType * fromType = cast<UnionType>(from); const UnionType * toType = cast<UnionType>(to); return areTypesCompatible(fromType->typeArgs(), toType->typeArgs()); } default: diag.debug() << "Type not handled: " << from; return false; } }
bool FunctionMergePass::visitUnionCtorCast(CastExpr * from, CastExpr * to) { if (!areTypesCompatible(from->type(), to->type())) { reportDifference("Difference in type of union cast expr", from, to); return false; } return visitExpr(from->arg(), to->arg()); }
bool FunctionMergePass::areTypesCompatible(QualifiedType from, QualifiedType to) { return areTypesCompatible(from.unqualified(), to.unqualified()); }
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; }