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