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