| //===--- TypeUtils.cpp - Type helper functions ----------------------------===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "TypeUtils.h" |
| #include "clang/AST/Expr.h" |
| #include "clang/AST/NSAPI.h" |
| #include "clang/AST/RecursiveASTVisitor.h" |
| |
| using namespace clang; |
| |
| namespace { |
| |
| /// Returns false if a BOOL expression is found. |
| class BOOLUseFinder : public RecursiveASTVisitor<BOOLUseFinder> { |
| public: |
| NSAPI API; |
| |
| BOOLUseFinder(const ASTContext &Context) |
| : API(const_cast<ASTContext &>(Context)) {} |
| |
| bool VisitStmt(const Stmt *S) { |
| if (const auto *E = dyn_cast<Expr>(S)) |
| return !API.isObjCBOOLType(E->getType()); |
| return true; |
| } |
| |
| static bool hasUseOfObjCBOOL(const ASTContext &Ctx, const Expr *E) { |
| return !BOOLUseFinder(Ctx).TraverseStmt(const_cast<Expr *>(E)); |
| } |
| }; |
| |
| } // end anonymous namespace |
| |
| static QualType preferredBoolType(const Decl *FunctionLikeParentDecl, |
| const Expr *E, QualType T, |
| const PrintingPolicy &Policy, |
| const ASTContext &Ctx) { |
| // We want to target expressions that return either 'int' or 'bool' |
| const auto *BTy = T->getAs<BuiltinType>(); |
| if (!BTy) |
| return T; |
| switch (BTy->getKind()) { |
| case BuiltinType::Int: |
| case BuiltinType::Bool: |
| // In Objective-C[++] we want to try to use 'BOOL' when the 'BOOL' typedef |
| // is defined. |
| if (Ctx.getLangOpts().ObjC1 && Ctx.getBOOLDecl()) { |
| if (Ctx.getLangOpts().CPlusPlus && FunctionLikeParentDecl) { |
| // When extracting expression from a standalone function in |
| // Objective-C++ we should use BOOL when expression uses BOOL, otherwise |
| // we should use bool. |
| if (isa<FunctionDecl>(FunctionLikeParentDecl)) { |
| if (BOOLUseFinder::hasUseOfObjCBOOL(Ctx, E)) |
| return Ctx.getBOOLType(); |
| return T; |
| } |
| } |
| return Ctx.getBOOLType(); |
| } |
| // In C mode we want to use 'bool' instead of 'int' when the 'bool' macro |
| // is defined. |
| if (!Ctx.getLangOpts().CPlusPlus && Policy.Bool) |
| return Ctx.BoolTy; |
| break; |
| default: |
| break; |
| } |
| return T; |
| } |
| |
| static bool isInStdNamespace(const Decl *D) { |
| const DeclContext *DC = D->getDeclContext()->getEnclosingNamespaceContext(); |
| const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(DC); |
| if (!ND) |
| return false; |
| |
| while (const DeclContext *Parent = ND->getParent()) { |
| if (!isa<NamespaceDecl>(Parent)) |
| break; |
| ND = cast<NamespaceDecl>(Parent); |
| } |
| |
| return ND->isStdNamespace(); |
| } |
| |
| static QualType desugarStdTypedef(QualType T) { |
| const auto *TT = T->getAs<TypedefType>(); |
| if (!TT) |
| return QualType(); |
| const TypedefNameDecl *TND = TT->getDecl(); |
| if (!isInStdNamespace(TND)) |
| return QualType(); |
| return TT->desugar(); |
| } |
| |
| // Desugars a typedef of a typedef that are both defined in STL. |
| // |
| // This is used to find the right type for a c_str() call on a std::string |
| // object: we want to return const char *, not const value_type *. |
| static QualType desugarStdType(QualType T) { |
| QualType DesugaredType = T; |
| if (const auto *PT = T->getAs<PointerType>()) |
| DesugaredType = PT->getPointeeType(); |
| DesugaredType = desugarStdTypedef(DesugaredType); |
| if (DesugaredType.isNull()) |
| return T; |
| if (const auto *ET = DesugaredType->getAs<ElaboratedType>()) |
| DesugaredType = ET->desugar(); |
| DesugaredType = desugarStdTypedef(DesugaredType); |
| if (DesugaredType.isNull()) |
| return T; |
| return T.getCanonicalType(); |
| } |
| |
| // Given an operator call like std::string() + "", we would like to ensure |
| // that we return std::string instead of std::basic_string. |
| static QualType canonicalizeStdOperatorReturnType(const Expr *E, QualType T) { |
| const auto *OCE = dyn_cast<CXXOperatorCallExpr>(E->IgnoreParenImpCasts()); |
| if (!OCE) |
| return T; |
| if (OCE->getNumArgs() < 2 || !isInStdNamespace(OCE->getCalleeDecl())) |
| return T; |
| QualType CanonicalReturn = T.getCanonicalType(); |
| if (const auto *RD = CanonicalReturn->getAsCXXRecordDecl()) { |
| if (!isInStdNamespace(RD)) |
| return T; |
| } else |
| return T; |
| for (unsigned I = 0, E = OCE->getNumArgs(); I < E; ++I) { |
| const Expr *Arg = OCE->getArgs()[I]; |
| QualType T = Arg->getType(); |
| if (const auto *ET = dyn_cast<ElaboratedType>(T)) |
| T = ET->desugar(); |
| if (desugarStdTypedef(T).isNull()) |
| continue; |
| QualType CanonicalArg = Arg->getType().getCanonicalType(); |
| CanonicalArg.removeLocalFastQualifiers(); |
| if (CanonicalArg == CanonicalReturn) { |
| QualType Result = Arg->getType(); |
| Result.removeLocalFastQualifiers(); |
| return Result; |
| } |
| } |
| return T; |
| } |
| |
| namespace clang { |
| namespace tooling { |
| |
| /// Tthe return type of the extracted function should match user's intent, |
| /// e.g. we want to use bool type whenever possible. |
| QualType findExpressionLexicalType(const Decl *FunctionLikeParentDecl, |
| const Expr *E, QualType T, |
| const PrintingPolicy &Policy, |
| const ASTContext &Ctx) { |
| // Get the correct property type. |
| if (const auto *PRE = dyn_cast<ObjCPropertyRefExpr>(E)) { |
| if (PRE->isMessagingGetter()) { |
| if (PRE->isExplicitProperty()) { |
| QualType ReceiverType = PRE->getReceiverType(Ctx); |
| return PRE->getExplicitProperty()->getUsageType(ReceiverType); |
| } |
| if (const ObjCMethodDecl *M = PRE->getImplicitPropertyGetter()) { |
| if (!PRE->isObjectReceiver()) |
| return M->getSendResultType(PRE->getReceiverType(Ctx)); |
| const Expr *Base = PRE->getBase(); |
| return M->getSendResultType(findExpressionLexicalType( |
| FunctionLikeParentDecl, Base, Base->getType(), Policy, Ctx)); |
| } |
| } |
| } |
| |
| // Perform STL-specific type corrections. |
| if (Ctx.getLangOpts().CPlusPlus) { |
| T = desugarStdType(T); |
| T = canonicalizeStdOperatorReturnType(E, T); |
| } |
| |
| // The bool type adjustment is required only in C or Objective-C[++]. |
| if (Ctx.getLangOpts().CPlusPlus && !Ctx.getLangOpts().ObjC1) |
| return T; |
| E = E->IgnoreParenImpCasts(); |
| if (const auto *BinOp = dyn_cast<BinaryOperator>(E)) { |
| if (BinOp->isLogicalOp() || BinOp->isComparisonOp()) |
| return preferredBoolType(FunctionLikeParentDecl, E, T, Policy, Ctx); |
| } else if (const auto *UnOp = dyn_cast<UnaryOperator>(E)) { |
| if (UnOp->getOpcode() == UO_LNot) |
| return preferredBoolType(FunctionLikeParentDecl, E, T, Policy, Ctx); |
| } |
| return T; |
| } |
| |
| } // end namespace tooling |
| } // end namespace clang |