clang-tools  17.0.0git
FixItHintUtils.cpp
Go to the documentation of this file.
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 //===----------------------------------------------------------------------===//
8 
9 #include "FixItHintUtils.h"
10 #include "LexerUtils.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/AST/Type.h"
13 #include <optional>
14 
16 
17 FixItHint changeVarDeclToReference(const VarDecl &Var, ASTContext &Context) {
18  SourceLocation AmpLocation = Var.getLocation();
19  auto Token = utils::lexer::getPreviousToken(
20  AmpLocation, Context.getSourceManager(), Context.getLangOpts());
21  if (!Token.is(tok::unknown))
22  AmpLocation = Lexer::getLocForEndOfToken(Token.getLocation(), 0,
23  Context.getSourceManager(),
24  Context.getLangOpts());
25  return FixItHint::CreateInsertion(AmpLocation, "&");
26 }
27 
28 static bool isValueType(const Type *T) {
29  return !(isa<PointerType>(T) || isa<ReferenceType>(T) || isa<ArrayType>(T) ||
30  isa<MemberPointerType>(T) || isa<ObjCObjectPointerType>(T));
31 }
32 static bool isValueType(QualType QT) { return isValueType(QT.getTypePtr()); }
33 static bool isMemberOrFunctionPointer(QualType QT) {
34  return (QT->isPointerType() && QT->isFunctionPointerType()) ||
35  isa<MemberPointerType>(QT.getTypePtr());
36 }
37 
38 static bool locDangerous(SourceLocation S) {
39  return S.isInvalid() || S.isMacroID();
40 }
41 
42 static std::optional<SourceLocation>
43 skipLParensBackwards(SourceLocation Start, const ASTContext &Context) {
44  if (locDangerous(Start))
45  return std::nullopt;
46 
47  auto PreviousTokenLParen = [&Start, &Context]() {
48  Token T;
49  T = lexer::getPreviousToken(Start, Context.getSourceManager(),
50  Context.getLangOpts());
51  return T.is(tok::l_paren);
52  };
53 
54  while (Start.isValid() && PreviousTokenLParen())
55  Start = lexer::findPreviousTokenStart(Start, Context.getSourceManager(),
56  Context.getLangOpts());
57 
58  if (locDangerous(Start))
59  return std::nullopt;
60  return Start;
61 }
62 
63 static std::optional<FixItHint> fixIfNotDangerous(SourceLocation Loc,
64  StringRef Text) {
65  if (locDangerous(Loc))
66  return std::nullopt;
67  return FixItHint::CreateInsertion(Loc, Text);
68 }
69 
70 // Build a string that can be emitted as FixIt with either a space in before
71 // or after the qualifier, either ' const' or 'const '.
72 static std::string buildQualifier(DeclSpec::TQ Qualifier,
73  bool WhitespaceBefore = false) {
74  if (WhitespaceBefore)
75  return (llvm::Twine(' ') + DeclSpec::getSpecifierName(Qualifier)).str();
76  return (llvm::Twine(DeclSpec::getSpecifierName(Qualifier)) + " ").str();
77 }
78 
79 static std::optional<FixItHint> changeValue(const VarDecl &Var,
80  DeclSpec::TQ Qualifier,
81  QualifierTarget QualTarget,
82  QualifierPolicy QualPolicy,
83  const ASTContext &Context) {
84  switch (QualPolicy) {
86  return fixIfNotDangerous(Var.getTypeSpecStartLoc(),
87  buildQualifier(Qualifier));
89  std::optional<SourceLocation> IgnoredParens =
90  skipLParensBackwards(Var.getLocation(), Context);
91 
92  if (IgnoredParens)
93  return fixIfNotDangerous(*IgnoredParens, buildQualifier(Qualifier));
94  return std::nullopt;
95  }
96  llvm_unreachable("Unknown QualifierPolicy enum");
97 }
98 
99 static std::optional<FixItHint> changePointerItself(const VarDecl &Var,
100  DeclSpec::TQ Qualifier,
101  const ASTContext &Context) {
102  if (locDangerous(Var.getLocation()))
103  return std::nullopt;
104 
105  std::optional<SourceLocation> IgnoredParens =
106  skipLParensBackwards(Var.getLocation(), Context);
107  if (IgnoredParens)
108  return fixIfNotDangerous(*IgnoredParens, buildQualifier(Qualifier));
109  return std::nullopt;
110 }
111 
112 static std::optional<FixItHint>
113 changePointer(const VarDecl &Var, DeclSpec::TQ Qualifier, const Type *Pointee,
114  QualifierTarget QualTarget, QualifierPolicy QualPolicy,
115  const ASTContext &Context) {
116  // The pointer itself shall be marked as `const`. This is always to the right
117  // of the '*' or in front of the identifier.
118  if (QualTarget == QualifierTarget::Value)
119  return changePointerItself(Var, Qualifier, Context);
120 
121  // Mark the pointee `const` that is a normal value (`int* p = nullptr;`).
122  if (QualTarget == QualifierTarget::Pointee && isValueType(Pointee)) {
123  // Adding the `const` on the left side is just the beginning of the type
124  // specification. (`const int* p = nullptr;`)
125  if (QualPolicy == QualifierPolicy::Left)
126  return fixIfNotDangerous(Var.getTypeSpecStartLoc(),
127  buildQualifier(Qualifier));
128 
129  // Adding the `const` on the right side of the value type requires finding
130  // the `*` token and placing the `const` left of it.
131  // (`int const* p = nullptr;`)
132  if (QualPolicy == QualifierPolicy::Right) {
133  SourceLocation BeforeStar = lexer::findPreviousTokenKind(
134  Var.getLocation(), Context.getSourceManager(), Context.getLangOpts(),
135  tok::star);
136  if (locDangerous(BeforeStar))
137  return std::nullopt;
138 
139  std::optional<SourceLocation> IgnoredParens =
140  skipLParensBackwards(BeforeStar, Context);
141 
142  if (IgnoredParens)
143  return fixIfNotDangerous(*IgnoredParens,
144  buildQualifier(Qualifier, true));
145  return std::nullopt;
146  }
147  }
148 
149  if (QualTarget == QualifierTarget::Pointee && Pointee->isPointerType()) {
150  // Adding the `const` to the pointee if the pointee is a pointer
151  // is the same as 'QualPolicy == Right && isValueType(Pointee)'.
152  // The `const` must be left of the last `*` token.
153  // (`int * const* p = nullptr;`)
154  SourceLocation BeforeStar = lexer::findPreviousTokenKind(
155  Var.getLocation(), Context.getSourceManager(), Context.getLangOpts(),
156  tok::star);
157  return fixIfNotDangerous(BeforeStar, buildQualifier(Qualifier, true));
158  }
159 
160  return std::nullopt;
161 }
162 
163 static std::optional<FixItHint>
164 changeReferencee(const VarDecl &Var, DeclSpec::TQ Qualifier, QualType Pointee,
165  QualifierTarget QualTarget, QualifierPolicy QualPolicy,
166  const ASTContext &Context) {
167  if (QualPolicy == QualifierPolicy::Left && isValueType(Pointee))
168  return fixIfNotDangerous(Var.getTypeSpecStartLoc(),
169  buildQualifier(Qualifier));
170 
171  SourceLocation BeforeRef = lexer::findPreviousAnyTokenKind(
172  Var.getLocation(), Context.getSourceManager(), Context.getLangOpts(),
173  tok::amp, tok::ampamp);
174  std::optional<SourceLocation> IgnoredParens =
175  skipLParensBackwards(BeforeRef, Context);
176  if (IgnoredParens)
177  return fixIfNotDangerous(*IgnoredParens, buildQualifier(Qualifier, true));
178 
179  return std::nullopt;
180 }
181 
182 std::optional<FixItHint> addQualifierToVarDecl(const VarDecl &Var,
183  const ASTContext &Context,
184  DeclSpec::TQ Qualifier,
185  QualifierTarget QualTarget,
186  QualifierPolicy QualPolicy) {
187  assert((QualPolicy == QualifierPolicy::Left ||
188  QualPolicy == QualifierPolicy::Right) &&
189  "Unexpected Insertion Policy");
190  assert((QualTarget == QualifierTarget::Pointee ||
191  QualTarget == QualifierTarget::Value) &&
192  "Unexpected Target");
193 
194  QualType ParenStrippedType = Var.getType().IgnoreParens();
195  if (isValueType(ParenStrippedType))
196  return changeValue(Var, Qualifier, QualTarget, QualPolicy, Context);
197 
198  if (ParenStrippedType->isReferenceType())
199  return changeReferencee(Var, Qualifier, Var.getType()->getPointeeType(),
200  QualTarget, QualPolicy, Context);
201 
202  if (isMemberOrFunctionPointer(ParenStrippedType))
203  return changePointerItself(Var, Qualifier, Context);
204 
205  if (ParenStrippedType->isPointerType())
206  return changePointer(Var, Qualifier,
207  ParenStrippedType->getPointeeType().getTypePtr(),
208  QualTarget, QualPolicy, Context);
209 
210  if (ParenStrippedType->isArrayType()) {
211  const Type *AT = ParenStrippedType->getBaseElementTypeUnsafe();
212  assert(AT && "Did not retrieve array element type for an array.");
213 
214  if (isValueType(AT))
215  return changeValue(Var, Qualifier, QualTarget, QualPolicy, Context);
216 
217  if (AT->isPointerType())
218  return changePointer(Var, Qualifier, AT->getPointeeType().getTypePtr(),
219  QualTarget, QualPolicy, Context);
220  }
221 
222  return std::nullopt;
223 }
224 } // namespace clang::tidy::utils::fixit
clang::tidy::utils::fixit::QualifierTarget
QualifierTarget
This enum defines which entity is the target for adding the qualifier.
Definition: FixItHintUtils.h:31
Loc
SourceLocation Loc
Definition: KernelNameRestrictionCheck.cpp:43
clang::tidy::utils::fixit::QualifierPolicy::Right
@ Right
Type
NodeType Type
Definition: HTMLGenerator.cpp:75
clang::tidy::utils::fixit::changePointer
static std::optional< FixItHint > changePointer(const VarDecl &Var, DeclSpec::TQ Qualifier, const Type *Pointee, QualifierTarget QualTarget, QualifierPolicy QualPolicy, const ASTContext &Context)
Definition: FixItHintUtils.cpp:113
clang::tidy::utils::fixit::addQualifierToVarDecl
std::optional< FixItHint > addQualifierToVarDecl(const VarDecl &Var, const ASTContext &Context, DeclSpec::TQ Qualifier, QualifierTarget QualTarget, QualifierPolicy QualPolicy)
Creates fix to qualify VarDecl with the specified Qualifier.
Definition: FixItHintUtils.cpp:182
clang::tidy::utils::fixit::QualifierTarget::Pointee
@ Pointee
clang::tidy::utils::fixit::fixIfNotDangerous
static std::optional< FixItHint > fixIfNotDangerous(SourceLocation Loc, StringRef Text)
Definition: FixItHintUtils.cpp:63
Text
std::string Text
Definition: HTMLGenerator.cpp:82
FixItHintUtils.h
clang::tidy::utils::fixit
Definition: FixItHintUtils.cpp:15
clang::tidy::utils::fixit::changeReferencee
static std::optional< FixItHint > changeReferencee(const VarDecl &Var, DeclSpec::TQ Qualifier, QualType Pointee, QualifierTarget QualTarget, QualifierPolicy QualPolicy, const ASTContext &Context)
Definition: FixItHintUtils.cpp:164
clang::tidy::utils::fixit::changeVarDeclToReference
FixItHint changeVarDeclToReference(const VarDecl &Var, ASTContext &Context)
Creates fix to make VarDecl a reference by adding &.
Definition: FixItHintUtils.cpp:17
clang::tidy::utils::lexer::findPreviousTokenStart
SourceLocation findPreviousTokenStart(SourceLocation Start, const SourceManager &SM, const LangOptions &LangOpts)
Definition: LexerUtils.cpp:37
clang::tidy::utils::fixit::locDangerous
static bool locDangerous(SourceLocation S)
Definition: FixItHintUtils.cpp:38
clang::tidy::utils::fixit::changePointerItself
static std::optional< FixItHint > changePointerItself(const VarDecl &Var, DeclSpec::TQ Qualifier, const ASTContext &Context)
Definition: FixItHintUtils.cpp:99
clang::tidy::utils::lexer::getPreviousToken
Token getPreviousToken(SourceLocation Location, const SourceManager &SM, const LangOptions &LangOpts, bool SkipComments)
Returns previous token or tok::unknown if not found.
Definition: LexerUtils.cpp:16
clang::tidy::utils::lexer::findPreviousAnyTokenKind
SourceLocation findPreviousAnyTokenKind(SourceLocation Start, const SourceManager &SM, const LangOptions &LangOpts, TokenKind TK, TokenKinds... TKs)
Definition: LexerUtils.h:40
clang::tidy::utils::fixit::QualifierPolicy::Left
@ Left
clang::tidy::utils::fixit::changeValue
static std::optional< FixItHint > changeValue(const VarDecl &Var, DeclSpec::TQ Qualifier, QualifierTarget QualTarget, QualifierPolicy QualPolicy, const ASTContext &Context)
Definition: FixItHintUtils.cpp:79
clang::tidy::utils::fixit::isMemberOrFunctionPointer
static bool isMemberOrFunctionPointer(QualType QT)
Definition: FixItHintUtils.cpp:33
clang::tidy::utils::fixit::isValueType
static bool isValueType(const Type *T)
Definition: FixItHintUtils.cpp:28
LexerUtils.h
clang::tidy::utils::fixit::buildQualifier
static std::string buildQualifier(DeclSpec::TQ Qualifier, bool WhitespaceBefore=false)
Definition: FixItHintUtils.cpp:72
clang::tidy::utils::fixit::QualifierTarget::Value
@ Value
Transforming a pointer attaches to the pointee and not the pointer itself.
clang::tidy::utils::fixit::skipLParensBackwards
static std::optional< SourceLocation > skipLParensBackwards(SourceLocation Start, const ASTContext &Context)
Definition: FixItHintUtils.cpp:43
clang::tidy::utils::fixit::QualifierPolicy
QualifierPolicy
This enum defines where the qualifier shall be preferably added.
Definition: FixItHintUtils.h:23
clang::tidy::utils::lexer::findPreviousTokenKind
SourceLocation findPreviousTokenKind(SourceLocation Start, const SourceManager &SM, const LangOptions &LangOpts, tok::TokenKind TK)
Definition: LexerUtils.cpp:50