clang-tools  14.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 
14 namespace clang {
15 namespace tidy {
16 namespace utils {
17 namespace fixit {
18 
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, "&");
28 }
29 
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));
33 }
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());
38 }
39 
40 static bool locDangerous(SourceLocation S) {
41  return S.isInvalid() || S.isMacroID();
42 }
43 
44 static Optional<SourceLocation>
45 skipLParensBackwards(SourceLocation Start, const ASTContext &Context) {
46  if (locDangerous(Start))
47  return None;
48 
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);
54  };
55 
56  while (Start.isValid() && PreviousTokenLParen())
57  Start = lexer::findPreviousTokenStart(Start, Context.getSourceManager(),
58  Context.getLangOpts());
59 
60  if (locDangerous(Start))
61  return None;
62  return Start;
63 }
64 
65 static Optional<FixItHint> fixIfNotDangerous(SourceLocation Loc,
66  StringRef Text) {
67  if (locDangerous(Loc))
68  return None;
69  return FixItHint::CreateInsertion(Loc, Text);
70 }
71 
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();
79 }
80 
81 static Optional<FixItHint> changeValue(const VarDecl &Var,
82  DeclSpec::TQ Qualifier,
83  QualifierTarget QualTarget,
84  QualifierPolicy QualPolicy,
85  const ASTContext &Context) {
86  switch (QualPolicy) {
88  return fixIfNotDangerous(Var.getTypeSpecStartLoc(),
89  buildQualifier(Qualifier));
91  Optional<SourceLocation> IgnoredParens =
92  skipLParensBackwards(Var.getLocation(), Context);
93 
94  if (IgnoredParens)
95  return fixIfNotDangerous(*IgnoredParens, buildQualifier(Qualifier));
96  return None;
97  }
98  llvm_unreachable("Unknown QualifierPolicy enum");
99 }
100 
101 static Optional<FixItHint> changePointerItself(const VarDecl &Var,
102  DeclSpec::TQ Qualifier,
103  const ASTContext &Context) {
104  if (locDangerous(Var.getLocation()))
105  return None;
106 
107  Optional<SourceLocation> IgnoredParens =
108  skipLParensBackwards(Var.getLocation(), Context);
109  if (IgnoredParens)
110  return fixIfNotDangerous(*IgnoredParens, buildQualifier(Qualifier));
111  return None;
112 }
113 
114 static 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);
122 
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));
130 
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 None;
140 
141  Optional<SourceLocation> IgnoredParens =
142  skipLParensBackwards(BeforeStar, Context);
143 
144  if (IgnoredParens)
145  return fixIfNotDangerous(*IgnoredParens,
146  buildQualifier(Qualifier, true));
147  return None;
148  }
149  }
150 
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));
160  }
161 
162  return None;
163 }
164 
165 static 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));
172 
173  SourceLocation BeforeRef = lexer::findPreviousAnyTokenKind(
174  Var.getLocation(), Context.getSourceManager(), Context.getLangOpts(),
175  tok::amp, tok::ampamp);
176  Optional<SourceLocation> IgnoredParens =
177  skipLParensBackwards(BeforeRef, Context);
178  if (IgnoredParens)
179  return fixIfNotDangerous(*IgnoredParens, buildQualifier(Qualifier, true));
180 
181  return None;
182 }
183 
184 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");
195 
196  QualType ParenStrippedType = Var.getType().IgnoreParens();
197  if (isValueType(ParenStrippedType))
198  return changeValue(Var, Qualifier, QualTarget, QualPolicy, Context);
199 
200  if (ParenStrippedType->isReferenceType())
201  return changeReferencee(Var, Qualifier, Var.getType()->getPointeeType(),
202  QualTarget, QualPolicy, Context);
203 
204  if (isMemberOrFunctionPointer(ParenStrippedType))
205  return changePointerItself(Var, Qualifier, Context);
206 
207  if (ParenStrippedType->isPointerType())
208  return changePointer(Var, Qualifier,
209  ParenStrippedType->getPointeeType().getTypePtr(),
210  QualTarget, QualPolicy, Context);
211 
212  if (ParenStrippedType->isArrayType()) {
213  const Type *AT = ParenStrippedType->getBaseElementTypeUnsafe();
214  assert(AT && "Did not retrieve array element type for an array.");
215 
216  if (isValueType(AT))
217  return changeValue(Var, Qualifier, QualTarget, QualPolicy, Context);
218 
219  if (AT->isPointerType())
220  return changePointer(Var, Qualifier, AT->getPointeeType().getTypePtr(),
221  QualTarget, QualPolicy, Context);
222  }
223 
224  return None;
225 }
226 } // namespace fixit
227 } // namespace utils
228 } // namespace tidy
229 } // namespace clang
clang::tidy::utils::fixit::QualifierTarget
QualifierTarget
This enum defines which entity is the target for adding the qualifier.
Definition: FixItHintUtils.h:33
Loc
SourceLocation Loc
Definition: KernelNameRestrictionCheck.cpp:45
clang::tidy::utils::fixit::addQualifierToVarDecl
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:184
clang::tidy::utils::fixit::QualifierPolicy::Right
@ Right
Type
NodeType Type
Definition: HTMLGenerator.cpp:73
clang::tidy::utils::fixit::fixIfNotDangerous
static Optional< FixItHint > fixIfNotDangerous(SourceLocation Loc, StringRef Text)
Definition: FixItHintUtils.cpp:65
clang::tidy::utils::fixit::QualifierTarget::Pointee
@ Pointee
Text
std::string Text
Definition: HTMLGenerator.cpp:80
FixItHintUtils.h
clang::tidy::utils::fixit::changeVarDeclToReference
FixItHint changeVarDeclToReference(const VarDecl &Var, ASTContext &Context)
Creates fix to make VarDecl a reference by adding &.
Definition: FixItHintUtils.cpp:19
clang::tidy::utils::lexer::findPreviousTokenStart
SourceLocation findPreviousTokenStart(SourceLocation Start, const SourceManager &SM, const LangOptions &LangOpts)
Definition: LexerUtils.cpp:39
clang::tidy::utils::fixit::locDangerous
static bool locDangerous(SourceLocation S)
Definition: FixItHintUtils.cpp:40
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:18
clang::tidy::utils::lexer::findPreviousAnyTokenKind
SourceLocation findPreviousAnyTokenKind(SourceLocation Start, const SourceManager &SM, const LangOptions &LangOpts, TokenKind TK, TokenKinds... TKs)
Definition: LexerUtils.h:41
clang::tidy::utils::fixit::QualifierPolicy::Left
@ Left
clang::tidy::utils::fixit::isMemberOrFunctionPointer
static bool isMemberOrFunctionPointer(QualType QT)
Definition: FixItHintUtils.cpp:35
clang::tidy::utils::fixit::isValueType
static bool isValueType(const Type *T)
Definition: FixItHintUtils.cpp:30
clang::tidy::utils::fixit::changePointer
static Optional< FixItHint > changePointer(const VarDecl &Var, DeclSpec::TQ Qualifier, const Type *Pointee, QualifierTarget QualTarget, QualifierPolicy QualPolicy, const ASTContext &Context)
Definition: FixItHintUtils.cpp:115
LexerUtils.h
clang::tidy::utils::fixit::buildQualifier
static std::string buildQualifier(DeclSpec::TQ Qualifier, bool WhitespaceBefore=false)
Definition: FixItHintUtils.cpp:74
clang::tidy::utils::fixit::QualifierTarget::Value
@ Value
Transforming a pointer attaches to the pointee and not the pointer itself.
clang::tidy::utils::fixit::changeReferencee
static Optional< FixItHint > changeReferencee(const VarDecl &Var, DeclSpec::TQ Qualifier, QualType Pointee, QualifierTarget QualTarget, QualifierPolicy QualPolicy, const ASTContext &Context)
Definition: FixItHintUtils.cpp:166
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::tidy::utils::fixit::changePointerItself
static Optional< FixItHint > changePointerItself(const VarDecl &Var, DeclSpec::TQ Qualifier, const ASTContext &Context)
Definition: FixItHintUtils.cpp:101
clang::tidy::utils::fixit::changeValue
static Optional< FixItHint > changeValue(const VarDecl &Var, DeclSpec::TQ Qualifier, QualifierTarget QualTarget, QualifierPolicy QualPolicy, const ASTContext &Context)
Definition: FixItHintUtils.cpp:81
clang::tidy::utils::fixit::QualifierPolicy
QualifierPolicy
This enum defines where the qualifier shall be preferably added.
Definition: FixItHintUtils.h:25
clang::tidy::utils::fixit::skipLParensBackwards
static Optional< SourceLocation > skipLParensBackwards(SourceLocation Start, const ASTContext &Context)
Definition: FixItHintUtils.cpp:45
clang::tidy::utils::lexer::findPreviousTokenKind
SourceLocation findPreviousTokenKind(SourceLocation Start, const SourceManager &SM, const LangOptions &LangOpts, tok::TokenKind TK)
Definition: LexerUtils.cpp:52