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