1 //===- SemaFixItUtils.cpp - Sema FixIts -----------------------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 //  This file defines helper classes for generation of Sema FixItHints.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "clang/Sema/SemaFixItUtils.h"
15 #include "clang/AST/ASTContext.h"
16 #include "clang/AST/DeclCXX.h"
17 #include "clang/AST/DeclarationName.h"
18 #include "clang/AST/Expr.h"
19 #include "clang/AST/ExprCXX.h"
20 #include "clang/AST/ExprObjC.h"
21 #include "clang/AST/Type.h"
22 #include "clang/Basic/Diagnostic.h"
23 #include "clang/Basic/LLVM.h"
24 #include "clang/Basic/LangOptions.h"
25 #include "clang/Basic/SourceLocation.h"
26 #include "clang/Lex/Preprocessor.h"
27 #include "clang/Sema/Sema.h"
28 #include "llvm/ADT/StringRef.h"
29 #include "llvm/Support/Casting.h"
30 #include <cassert>
31 #include <string>
32 
33 using namespace clang;
34 
35 bool ConversionFixItGenerator::compareTypesSimple(CanQualType From,
36                                                   CanQualType To,
37                                                   Sema &S,
38                                                   SourceLocation Loc,
39                                                   ExprValueKind FromVK) {
40   if (!To.isAtLeastAsQualifiedAs(From))
41     return false;
42 
43   From = From.getNonReferenceType();
44   To = To.getNonReferenceType();
45 
46   // If both are pointer types, work with the pointee types.
47   if (isa<PointerType>(From) && isa<PointerType>(To)) {
48     From = S.Context.getCanonicalType(
49         (cast<PointerType>(From))->getPointeeType());
50     To = S.Context.getCanonicalType(
51         (cast<PointerType>(To))->getPointeeType());
52   }
53 
54   const CanQualType FromUnq = From.getUnqualifiedType();
55   const CanQualType ToUnq = To.getUnqualifiedType();
56 
57   if ((FromUnq == ToUnq || (S.IsDerivedFrom(Loc, FromUnq, ToUnq)) ) &&
58       To.isAtLeastAsQualifiedAs(From))
59     return true;
60   return false;
61 }
62 
63 bool ConversionFixItGenerator::tryToFixConversion(const Expr *FullExpr,
64                                                   const QualType FromTy,
65                                                   const QualType ToTy,
66                                                   Sema &S) {
67   if (!FullExpr)
68     return false;
69 
70   const CanQualType FromQTy = S.Context.getCanonicalType(FromTy);
71   const CanQualType ToQTy = S.Context.getCanonicalType(ToTy);
72   const SourceLocation Begin = FullExpr->getSourceRange().getBegin();
73   const SourceLocation End = S.getLocForEndOfToken(FullExpr->getSourceRange()
74                                                    .getEnd());
75 
76   // Strip the implicit casts - those are implied by the compiler, not the
77   // original source code.
78   const Expr* Expr = FullExpr->IgnoreImpCasts();
79 
80   bool NeedParen = true;
81   if (isa<ArraySubscriptExpr>(Expr) ||
82       isa<CallExpr>(Expr) ||
83       isa<DeclRefExpr>(Expr) ||
84       isa<CastExpr>(Expr) ||
85       isa<CXXNewExpr>(Expr) ||
86       isa<CXXConstructExpr>(Expr) ||
87       isa<CXXDeleteExpr>(Expr) ||
88       isa<CXXNoexceptExpr>(Expr) ||
89       isa<CXXPseudoDestructorExpr>(Expr) ||
90       isa<CXXScalarValueInitExpr>(Expr) ||
91       isa<CXXThisExpr>(Expr) ||
92       isa<CXXTypeidExpr>(Expr) ||
93       isa<CXXUnresolvedConstructExpr>(Expr) ||
94       isa<ObjCMessageExpr>(Expr) ||
95       isa<ObjCPropertyRefExpr>(Expr) ||
96       isa<ObjCProtocolExpr>(Expr) ||
97       isa<MemberExpr>(Expr) ||
98       isa<ParenExpr>(FullExpr) ||
99       isa<ParenListExpr>(Expr) ||
100       isa<SizeOfPackExpr>(Expr) ||
101       isa<UnaryOperator>(Expr))
102     NeedParen = false;
103 
104   // Check if the argument needs to be dereferenced:
105   //   (type * -> type) or (type * -> type &).
106   if (const auto *FromPtrTy = dyn_cast<PointerType>(FromQTy)) {
107     OverloadFixItKind FixKind = OFIK_Dereference;
108 
109     bool CanConvert = CompareTypes(
110       S.Context.getCanonicalType(FromPtrTy->getPointeeType()), ToQTy,
111                                  S, Begin, VK_LValue);
112     if (CanConvert) {
113       // Do not suggest dereferencing a Null pointer.
114       if (Expr->IgnoreParenCasts()->
115           isNullPointerConstant(S.Context, Expr::NPC_ValueDependentIsNotNull))
116         return false;
117 
118       if (const auto *UO = dyn_cast<UnaryOperator>(Expr)) {
119         if (UO->getOpcode() == UO_AddrOf) {
120           FixKind = OFIK_RemoveTakeAddress;
121           Hints.push_back(FixItHint::CreateRemoval(
122                             CharSourceRange::getTokenRange(Begin, Begin)));
123         }
124       } else if (NeedParen) {
125         Hints.push_back(FixItHint::CreateInsertion(Begin, "*("));
126         Hints.push_back(FixItHint::CreateInsertion(End, ")"));
127       } else {
128         Hints.push_back(FixItHint::CreateInsertion(Begin, "*"));
129       }
130 
131       NumConversionsFixed++;
132       if (NumConversionsFixed == 1)
133         Kind = FixKind;
134       return true;
135     }
136   }
137 
138   // Check if the pointer to the argument needs to be passed:
139   //   (type -> type *) or (type & -> type *).
140   if (isa<PointerType>(ToQTy)) {
141     bool CanConvert = false;
142     OverloadFixItKind FixKind = OFIK_TakeAddress;
143 
144     // Only suggest taking address of L-values.
145     if (!Expr->isLValue() || Expr->getObjectKind() != OK_Ordinary)
146       return false;
147 
148     CanConvert = CompareTypes(S.Context.getPointerType(FromQTy), ToQTy,
149                               S, Begin, VK_RValue);
150     if (CanConvert) {
151       if (const auto *UO = dyn_cast<UnaryOperator>(Expr)) {
152         if (UO->getOpcode() == UO_Deref) {
153           FixKind = OFIK_RemoveDereference;
154           Hints.push_back(FixItHint::CreateRemoval(
155                             CharSourceRange::getTokenRange(Begin, Begin)));
156         }
157       } else if (NeedParen) {
158         Hints.push_back(FixItHint::CreateInsertion(Begin, "&("));
159         Hints.push_back(FixItHint::CreateInsertion(End, ")"));
160       } else {
161         Hints.push_back(FixItHint::CreateInsertion(Begin, "&"));
162       }
163 
164       NumConversionsFixed++;
165       if (NumConversionsFixed == 1)
166         Kind = FixKind;
167       return true;
168     }
169   }
170 
171   return false;
172 }
173 
174 static bool isMacroDefined(const Sema &S, SourceLocation Loc, StringRef Name) {
175   return (bool)S.PP.getMacroDefinitionAtLoc(&S.getASTContext().Idents.get(Name),
176                                             Loc);
177 }
178 
179 static std::string getScalarZeroExpressionForType(
180     const Type &T, SourceLocation Loc, const Sema &S) {
181   assert(T.isScalarType() && "use scalar types only");
182   // Suggest "0" for non-enumeration scalar types, unless we can find a
183   // better initializer.
184   if (T.isEnumeralType())
185     return {};
186   if ((T.isObjCObjectPointerType() || T.isBlockPointerType()) &&
187       isMacroDefined(S, Loc, "nil"))
188     return "nil";
189   if (T.isRealFloatingType())
190     return "0.0";
191   if (T.isBooleanType() &&
192       (S.LangOpts.CPlusPlus || isMacroDefined(S, Loc, "false")))
193     return "false";
194   if (T.isPointerType() || T.isMemberPointerType()) {
195     if (S.LangOpts.CPlusPlus11)
196       return "nullptr";
197     if (isMacroDefined(S, Loc, "NULL"))
198       return "NULL";
199   }
200   if (T.isCharType())
201     return "'\\0'";
202   if (T.isWideCharType())
203     return "L'\\0'";
204   if (T.isChar16Type())
205     return "u'\\0'";
206   if (T.isChar32Type())
207     return "U'\\0'";
208   return "0";
209 }
210 
211 std::string
212 Sema::getFixItZeroInitializerForType(QualType T, SourceLocation Loc) const {
213   if (T->isScalarType()) {
214     std::string s = getScalarZeroExpressionForType(*T, Loc, *this);
215     if (!s.empty())
216       s = " = " + s;
217     return s;
218   }
219 
220   const CXXRecordDecl *RD = T->getAsCXXRecordDecl();
221   if (!RD || !RD->hasDefinition())
222     return {};
223   if (LangOpts.CPlusPlus11 && !RD->hasUserProvidedDefaultConstructor())
224     return "{}";
225   if (RD->isAggregate())
226     return " = {}";
227   return {};
228 }
229 
230 std::string
231 Sema::getFixItZeroLiteralForType(QualType T, SourceLocation Loc) const {
232   return getScalarZeroExpressionForType(*T, Loc, *this);
233 }
234