xref: /openbsd-src/gnu/llvm/clang/lib/Sema/SemaFixItUtils.cpp (revision 12c855180aad702bbcca06e0398d774beeafb155)
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