blob: c07136bd709ad1c5e068c97af855bb008a95daba [file] [log] [blame]
//===--- 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