| //===--- SemaFixItUtils.cpp - Sema FixIts ---------------------------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file defines helper classes for generation of Sema FixItHints. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "clang/AST/ASTContext.h" |
| #include "clang/AST/ExprCXX.h" |
| #include "clang/AST/ExprObjC.h" |
| #include "clang/Lex/Preprocessor.h" |
| #include "clang/Sema/Sema.h" |
| #include "clang/Sema/SemaFixItUtils.h" |
| |
| using namespace clang; |
| |
| bool ConversionFixItGenerator::compareTypesSimple(CanQualType From, |
| CanQualType To, |
| Sema &S, |
| SourceLocation Loc, |
| ExprValueKind FromVK) { |
| if (!To.isAtLeastAsQualifiedAs(From)) |
| return false; |
| |
| From = From.getNonReferenceType(); |
| To = To.getNonReferenceType(); |
| |
| // If both are pointer types, work with the pointee types. |
| if (isa<PointerType>(From) && isa<PointerType>(To)) { |
| From = S.Context.getCanonicalType( |
| (cast<PointerType>(From))->getPointeeType()); |
| To = S.Context.getCanonicalType( |
| (cast<PointerType>(To))->getPointeeType()); |
| } |
| |
| const CanQualType FromUnq = From.getUnqualifiedType(); |
| const CanQualType ToUnq = To.getUnqualifiedType(); |
| |
| if ((FromUnq == ToUnq || (S.IsDerivedFrom(Loc, FromUnq, ToUnq)) ) && |
| To.isAtLeastAsQualifiedAs(From)) |
| return true; |
| return false; |
| } |
| |
| bool ConversionFixItGenerator::tryToFixConversion(const Expr *FullExpr, |
| const QualType FromTy, |
| const QualType ToTy, |
| Sema &S) { |
| if (!FullExpr) |
| return false; |
| |
| const CanQualType FromQTy = S.Context.getCanonicalType(FromTy); |
| const CanQualType ToQTy = S.Context.getCanonicalType(ToTy); |
| const SourceLocation Begin = FullExpr->getSourceRange().getBegin(); |
| const SourceLocation End = S.getLocForEndOfToken(FullExpr->getSourceRange() |
| .getEnd()); |
| |
| // Strip the implicit casts - those are implied by the compiler, not the |
| // original source code. |
| const Expr* Expr = FullExpr->IgnoreImpCasts(); |
| |
| bool NeedParen = true; |
| if (isa<ArraySubscriptExpr>(Expr) || |
| isa<CallExpr>(Expr) || |
| isa<DeclRefExpr>(Expr) || |
| isa<CastExpr>(Expr) || |
| isa<CXXNewExpr>(Expr) || |
| isa<CXXConstructExpr>(Expr) || |
| isa<CXXDeleteExpr>(Expr) || |
| isa<CXXNoexceptExpr>(Expr) || |
| isa<CXXPseudoDestructorExpr>(Expr) || |
| isa<CXXScalarValueInitExpr>(Expr) || |
| isa<CXXThisExpr>(Expr) || |
| isa<CXXTypeidExpr>(Expr) || |
| isa<CXXUnresolvedConstructExpr>(Expr) || |
| isa<ObjCMessageExpr>(Expr) || |
| isa<ObjCPropertyRefExpr>(Expr) || |
| isa<ObjCProtocolExpr>(Expr) || |
| isa<MemberExpr>(Expr) || |
| isa<ParenExpr>(FullExpr) || |
| isa<ParenListExpr>(Expr) || |
| isa<SizeOfPackExpr>(Expr) || |
| isa<UnaryOperator>(Expr)) |
| NeedParen = false; |
| |
| // Check if the argument needs to be dereferenced: |
| // (type * -> type) or (type * -> type &). |
| if (const PointerType *FromPtrTy = dyn_cast<PointerType>(FromQTy)) { |
| OverloadFixItKind FixKind = OFIK_Dereference; |
| |
| bool CanConvert = CompareTypes( |
| S.Context.getCanonicalType(FromPtrTy->getPointeeType()), ToQTy, |
| S, Begin, VK_LValue); |
| if (CanConvert) { |
| // Do not suggest dereferencing a Null pointer. |
| if (Expr->IgnoreParenCasts()-> |
| isNullPointerConstant(S.Context, Expr::NPC_ValueDependentIsNotNull)) |
| return false; |
| |
| if (const UnaryOperator *UO = dyn_cast<UnaryOperator>(Expr)) { |
| if (UO->getOpcode() == UO_AddrOf) { |
| FixKind = OFIK_RemoveTakeAddress; |
| Hints.push_back(FixItHint::CreateRemoval( |
| CharSourceRange::getTokenRange(Begin, Begin))); |
| } |
| } else if (NeedParen) { |
| Hints.push_back(FixItHint::CreateInsertion(Begin, "*(")); |
| Hints.push_back(FixItHint::CreateInsertion(End, ")")); |
| } else { |
| Hints.push_back(FixItHint::CreateInsertion(Begin, "*")); |
| } |
| |
| NumConversionsFixed++; |
| if (NumConversionsFixed == 1) |
| Kind = FixKind; |
| return true; |
| } |
| } |
| |
| // Check if the pointer to the argument needs to be passed: |
| // (type -> type *) or (type & -> type *). |
| if (isa<PointerType>(ToQTy)) { |
| bool CanConvert = false; |
| OverloadFixItKind FixKind = OFIK_TakeAddress; |
| |
| // Only suggest taking address of L-values. |
| if (!Expr->isLValue() || Expr->getObjectKind() != OK_Ordinary) |
| return false; |
| |
| CanConvert = CompareTypes(S.Context.getPointerType(FromQTy), ToQTy, |
| S, Begin, VK_RValue); |
| if (CanConvert) { |
| |
| if (const UnaryOperator *UO = dyn_cast<UnaryOperator>(Expr)) { |
| if (UO->getOpcode() == UO_Deref) { |
| FixKind = OFIK_RemoveDereference; |
| Hints.push_back(FixItHint::CreateRemoval( |
| CharSourceRange::getTokenRange(Begin, Begin))); |
| } |
| } else if (NeedParen) { |
| Hints.push_back(FixItHint::CreateInsertion(Begin, "&(")); |
| Hints.push_back(FixItHint::CreateInsertion(End, ")")); |
| } else { |
| Hints.push_back(FixItHint::CreateInsertion(Begin, "&")); |
| } |
| |
| NumConversionsFixed++; |
| if (NumConversionsFixed == 1) |
| Kind = FixKind; |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| static bool isMacroDefined(const Sema &S, SourceLocation Loc, StringRef Name) { |
| return (bool)S.PP.getMacroDefinitionAtLoc(&S.getASTContext().Idents.get(Name), |
| Loc); |
| } |
| |
| static std::string getScalarZeroExpressionForType( |
| const Type &T, SourceLocation Loc, const Sema &S) { |
| assert(T.isScalarType() && "use scalar types only"); |
| // Suggest "0" for non-enumeration scalar types, unless we can find a |
| // better initializer. |
| if (T.isEnumeralType()) |
| return std::string(); |
| if ((T.isObjCObjectPointerType() || T.isBlockPointerType()) && |
| isMacroDefined(S, Loc, "nil")) |
| return "nil"; |
| if (T.isRealFloatingType()) |
| return "0.0"; |
| if (T.isBooleanType() && |
| (S.LangOpts.CPlusPlus || isMacroDefined(S, Loc, "false"))) |
| return "false"; |
| if (T.isPointerType() || T.isMemberPointerType()) { |
| if (S.LangOpts.CPlusPlus11) |
| return "nullptr"; |
| if (isMacroDefined(S, Loc, "NULL")) |
| return "NULL"; |
| } |
| if (T.isCharType()) |
| return "'\\0'"; |
| if (T.isWideCharType()) |
| return "L'\\0'"; |
| if (T.isChar16Type()) |
| return "u'\\0'"; |
| if (T.isChar32Type()) |
| return "U'\\0'"; |
| return "0"; |
| } |
| |
| std::string |
| Sema::getFixItZeroInitializerForType(QualType T, SourceLocation Loc) const { |
| if (T->isScalarType()) { |
| std::string s = getScalarZeroExpressionForType(*T, Loc, *this); |
| if (!s.empty()) |
| s = " = " + s; |
| return s; |
| } |
| |
| const CXXRecordDecl *RD = T->getAsCXXRecordDecl(); |
| if (!RD || !RD->hasDefinition()) |
| return std::string(); |
| if (LangOpts.CPlusPlus11 && !RD->hasUserProvidedDefaultConstructor()) |
| return "{}"; |
| if (RD->isAggregate()) |
| return " = {}"; |
| return std::string(); |
| } |
| |
| std::string |
| Sema::getFixItZeroLiteralForType(QualType T, SourceLocation Loc) const { |
| return getScalarZeroExpressionForType(*T, Loc, *this); |
| } |