FixItHintUtils.cpp 8.9 KB

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