[AMDGPU][AsmParser][NFC] Translate parsed MIMG instructions to MCInsts automatically.
[llvm-project.git] / clang-tools-extra / clang-tidy / utils / FixItHintUtils.cpp
bloba0758fc58ee7bb330957351fa1cc7c25c40ea848
1 //===--- FixItHintUtils.cpp - clang-tidy-----------------------------------===//
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 //===----------------------------------------------------------------------===//
9 #include "FixItHintUtils.h"
10 #include "LexerUtils.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/AST/ExprCXX.h"
13 #include "clang/AST/Type.h"
14 #include "clang/Tooling/FixIt.h"
15 #include <optional>
17 namespace clang::tidy::utils::fixit {
19 FixItHint changeVarDeclToReference(const VarDecl &Var, ASTContext &Context) {
20 SourceLocation AmpLocation = Var.getLocation();
21 auto Token = utils::lexer::getPreviousToken(
22 AmpLocation, Context.getSourceManager(), Context.getLangOpts());
23 if (!Token.is(tok::unknown))
24 AmpLocation = Lexer::getLocForEndOfToken(Token.getLocation(), 0,
25 Context.getSourceManager(),
26 Context.getLangOpts());
27 return FixItHint::CreateInsertion(AmpLocation, "&");
30 static bool isValueType(const Type *T) {
31 return !(isa<PointerType>(T) || isa<ReferenceType>(T) || isa<ArrayType>(T) ||
32 isa<MemberPointerType>(T) || isa<ObjCObjectPointerType>(T));
34 static bool isValueType(QualType QT) { return isValueType(QT.getTypePtr()); }
35 static bool isMemberOrFunctionPointer(QualType QT) {
36 return (QT->isPointerType() && QT->isFunctionPointerType()) ||
37 isa<MemberPointerType>(QT.getTypePtr());
40 static bool locDangerous(SourceLocation S) {
41 return S.isInvalid() || S.isMacroID();
44 static std::optional<SourceLocation>
45 skipLParensBackwards(SourceLocation Start, const ASTContext &Context) {
46 if (locDangerous(Start))
47 return std::nullopt;
49 auto PreviousTokenLParen = [&Start, &Context]() {
50 Token T;
51 T = lexer::getPreviousToken(Start, Context.getSourceManager(),
52 Context.getLangOpts());
53 return T.is(tok::l_paren);
56 while (Start.isValid() && PreviousTokenLParen())
57 Start = lexer::findPreviousTokenStart(Start, Context.getSourceManager(),
58 Context.getLangOpts());
60 if (locDangerous(Start))
61 return std::nullopt;
62 return Start;
65 static std::optional<FixItHint> fixIfNotDangerous(SourceLocation Loc,
66 StringRef Text) {
67 if (locDangerous(Loc))
68 return std::nullopt;
69 return FixItHint::CreateInsertion(Loc, Text);
72 // Build a string that can be emitted as FixIt with either a space in before
73 // or after the qualifier, either ' const' or 'const '.
74 static std::string buildQualifier(DeclSpec::TQ Qualifier,
75 bool WhitespaceBefore = false) {
76 if (WhitespaceBefore)
77 return (llvm::Twine(' ') + DeclSpec::getSpecifierName(Qualifier)).str();
78 return (llvm::Twine(DeclSpec::getSpecifierName(Qualifier)) + " ").str();
81 static std::optional<FixItHint> changeValue(const VarDecl &Var,
82 DeclSpec::TQ Qualifier,
83 QualifierTarget QualTarget,
84 QualifierPolicy QualPolicy,
85 const ASTContext &Context) {
86 switch (QualPolicy) {
87 case QualifierPolicy::Left:
88 return fixIfNotDangerous(Var.getTypeSpecStartLoc(),
89 buildQualifier(Qualifier));
90 case QualifierPolicy::Right:
91 std::optional<SourceLocation> IgnoredParens =
92 skipLParensBackwards(Var.getLocation(), Context);
94 if (IgnoredParens)
95 return fixIfNotDangerous(*IgnoredParens, buildQualifier(Qualifier));
96 return std::nullopt;
98 llvm_unreachable("Unknown QualifierPolicy enum");
101 static std::optional<FixItHint> changePointerItself(const VarDecl &Var,
102 DeclSpec::TQ Qualifier,
103 const ASTContext &Context) {
104 if (locDangerous(Var.getLocation()))
105 return std::nullopt;
107 std::optional<SourceLocation> IgnoredParens =
108 skipLParensBackwards(Var.getLocation(), Context);
109 if (IgnoredParens)
110 return fixIfNotDangerous(*IgnoredParens, buildQualifier(Qualifier));
111 return std::nullopt;
114 static std::optional<FixItHint>
115 changePointer(const VarDecl &Var, DeclSpec::TQ Qualifier, const Type *Pointee,
116 QualifierTarget QualTarget, QualifierPolicy QualPolicy,
117 const ASTContext &Context) {
118 // The pointer itself shall be marked as `const`. This is always to the right
119 // of the '*' or in front of the identifier.
120 if (QualTarget == QualifierTarget::Value)
121 return changePointerItself(Var, Qualifier, Context);
123 // Mark the pointee `const` that is a normal value (`int* p = nullptr;`).
124 if (QualTarget == QualifierTarget::Pointee && isValueType(Pointee)) {
125 // Adding the `const` on the left side is just the beginning of the type
126 // specification. (`const int* p = nullptr;`)
127 if (QualPolicy == QualifierPolicy::Left)
128 return fixIfNotDangerous(Var.getTypeSpecStartLoc(),
129 buildQualifier(Qualifier));
131 // Adding the `const` on the right side of the value type requires finding
132 // the `*` token and placing the `const` left of it.
133 // (`int const* p = nullptr;`)
134 if (QualPolicy == QualifierPolicy::Right) {
135 SourceLocation BeforeStar = lexer::findPreviousTokenKind(
136 Var.getLocation(), Context.getSourceManager(), Context.getLangOpts(),
137 tok::star);
138 if (locDangerous(BeforeStar))
139 return std::nullopt;
141 std::optional<SourceLocation> IgnoredParens =
142 skipLParensBackwards(BeforeStar, Context);
144 if (IgnoredParens)
145 return fixIfNotDangerous(*IgnoredParens,
146 buildQualifier(Qualifier, true));
147 return std::nullopt;
151 if (QualTarget == QualifierTarget::Pointee && Pointee->isPointerType()) {
152 // Adding the `const` to the pointee if the pointee is a pointer
153 // is the same as 'QualPolicy == Right && isValueType(Pointee)'.
154 // The `const` must be left of the last `*` token.
155 // (`int * const* p = nullptr;`)
156 SourceLocation BeforeStar = lexer::findPreviousTokenKind(
157 Var.getLocation(), Context.getSourceManager(), Context.getLangOpts(),
158 tok::star);
159 return fixIfNotDangerous(BeforeStar, buildQualifier(Qualifier, true));
162 return std::nullopt;
165 static std::optional<FixItHint>
166 changeReferencee(const VarDecl &Var, DeclSpec::TQ Qualifier, QualType Pointee,
167 QualifierTarget QualTarget, QualifierPolicy QualPolicy,
168 const ASTContext &Context) {
169 if (QualPolicy == QualifierPolicy::Left && isValueType(Pointee))
170 return fixIfNotDangerous(Var.getTypeSpecStartLoc(),
171 buildQualifier(Qualifier));
173 SourceLocation BeforeRef = lexer::findPreviousAnyTokenKind(
174 Var.getLocation(), Context.getSourceManager(), Context.getLangOpts(),
175 tok::amp, tok::ampamp);
176 std::optional<SourceLocation> IgnoredParens =
177 skipLParensBackwards(BeforeRef, Context);
178 if (IgnoredParens)
179 return fixIfNotDangerous(*IgnoredParens, buildQualifier(Qualifier, true));
181 return std::nullopt;
184 std::optional<FixItHint> addQualifierToVarDecl(const VarDecl &Var,
185 const ASTContext &Context,
186 DeclSpec::TQ Qualifier,
187 QualifierTarget QualTarget,
188 QualifierPolicy QualPolicy) {
189 assert((QualPolicy == QualifierPolicy::Left ||
190 QualPolicy == QualifierPolicy::Right) &&
191 "Unexpected Insertion Policy");
192 assert((QualTarget == QualifierTarget::Pointee ||
193 QualTarget == QualifierTarget::Value) &&
194 "Unexpected Target");
196 QualType ParenStrippedType = Var.getType().IgnoreParens();
197 if (isValueType(ParenStrippedType))
198 return changeValue(Var, Qualifier, QualTarget, QualPolicy, Context);
200 if (ParenStrippedType->isReferenceType())
201 return changeReferencee(Var, Qualifier, Var.getType()->getPointeeType(),
202 QualTarget, QualPolicy, Context);
204 if (isMemberOrFunctionPointer(ParenStrippedType))
205 return changePointerItself(Var, Qualifier, Context);
207 if (ParenStrippedType->isPointerType())
208 return changePointer(Var, Qualifier,
209 ParenStrippedType->getPointeeType().getTypePtr(),
210 QualTarget, QualPolicy, Context);
212 if (ParenStrippedType->isArrayType()) {
213 const Type *AT = ParenStrippedType->getBaseElementTypeUnsafe();
214 assert(AT && "Did not retrieve array element type for an array.");
216 if (isValueType(AT))
217 return changeValue(Var, Qualifier, QualTarget, QualPolicy, Context);
219 if (AT->isPointerType())
220 return changePointer(Var, Qualifier, AT->getPointeeType().getTypePtr(),
221 QualTarget, QualPolicy, Context);
224 return std::nullopt;
227 // Return true if expr needs to be put in parens when it is an argument of a
228 // prefix unary operator, e.g. when it is a binary or ternary operator
229 // syntactically.
230 static bool needParensAfterUnaryOperator(const Expr &ExprNode) {
231 if (isa<clang::BinaryOperator>(&ExprNode) ||
232 isa<clang::ConditionalOperator>(&ExprNode)) {
233 return true;
235 if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(&ExprNode)) {
236 return Op->getNumArgs() == 2 && Op->getOperator() != OO_PlusPlus &&
237 Op->getOperator() != OO_MinusMinus && Op->getOperator() != OO_Call &&
238 Op->getOperator() != OO_Subscript;
240 return false;
243 // Format a pointer to an expression: prefix with '*' but simplify
244 // when it already begins with '&'. Return empty string on failure.
245 std::string formatDereference(const Expr &ExprNode, const ASTContext &Context) {
246 if (const auto *Op = dyn_cast<clang::UnaryOperator>(&ExprNode)) {
247 if (Op->getOpcode() == UO_AddrOf) {
248 // Strip leading '&'.
249 return std::string(
250 tooling::fixit::getText(*Op->getSubExpr()->IgnoreParens(), Context));
253 StringRef Text = tooling::fixit::getText(ExprNode, Context);
255 if (Text.empty())
256 return std::string();
258 // Remove remaining '->' from overloaded operator call
259 Text.consume_back("->");
261 // Add leading '*'.
262 if (needParensAfterUnaryOperator(ExprNode)) {
263 return (llvm::Twine("*(") + Text + ")").str();
265 return (llvm::Twine("*") + Text).str();
268 } // namespace clang::tidy::utils::fixit