brintos

brintos / llvm-project-archived public Read only

0
0
Text · 11.4 KiB · c4cdf0d Raw
311 lines · cpp
1//===----------------------------------------------------------------------===//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-exception6//7//===----------------------------------------------------------------------===//8 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/Sema/DeclSpec.h"15#include "clang/Tooling/FixIt.h"16#include <optional>17 18namespace clang::tidy::utils::fixit {19 20FixItHint changeVarDeclToReference(const VarDecl &Var, ASTContext &Context) {21  SourceLocation AmpLocation = Var.getLocation();22  auto Token = utils::lexer::getPreviousToken(23      AmpLocation, Context.getSourceManager(), Context.getLangOpts());24 25  // For parameter packs the '&' must go before the '...' token26  if (Token.is(tok::ellipsis))27    return FixItHint::CreateInsertion(Token.getLocation(), "&");28 29  if (!Token.is(tok::unknown))30    AmpLocation = Lexer::getLocForEndOfToken(Token.getLocation(), 0,31                                             Context.getSourceManager(),32                                             Context.getLangOpts());33  return FixItHint::CreateInsertion(AmpLocation, "&");34}35 36static bool isValueType(const Type *T) {37  return !(isa<PointerType>(T) || isa<ReferenceType>(T) || isa<ArrayType>(T) ||38           isa<MemberPointerType>(T) || isa<ObjCObjectPointerType>(T));39}40static bool isValueType(QualType QT) { return isValueType(QT.getTypePtr()); }41static bool isMemberOrFunctionPointer(QualType QT) {42  return (QT->isPointerType() && QT->isFunctionPointerType()) ||43         isa<MemberPointerType>(QT.getTypePtr());44}45 46static bool locDangerous(SourceLocation S) {47  return S.isInvalid() || S.isMacroID();48}49 50static std::optional<SourceLocation>51skipLParensBackwards(SourceLocation Start, const ASTContext &Context) {52  if (locDangerous(Start))53    return std::nullopt;54 55  auto PreviousTokenLParen = [&Start, &Context]() {56    Token T;57    T = lexer::getPreviousToken(Start, Context.getSourceManager(),58                                Context.getLangOpts());59    return T.is(tok::l_paren);60  };61 62  while (Start.isValid() && PreviousTokenLParen())63    Start = lexer::findPreviousTokenStart(Start, Context.getSourceManager(),64                                          Context.getLangOpts());65 66  if (locDangerous(Start))67    return std::nullopt;68  return Start;69}70 71static std::optional<FixItHint> fixIfNotDangerous(SourceLocation Loc,72                                                  StringRef Text) {73  if (locDangerous(Loc))74    return std::nullopt;75  return FixItHint::CreateInsertion(Loc, Text);76}77 78// Build a string that can be emitted as FixIt with either a space in before79// or after the qualifier, either ' const' or 'const '.80static std::string buildQualifier(Qualifiers::TQ Qualifier,81                                  bool WhitespaceBefore = false) {82  if (WhitespaceBefore)83    return (llvm::Twine(' ') + Qualifiers::fromCVRMask(Qualifier).getAsString())84        .str();85  return (llvm::Twine(Qualifiers::fromCVRMask(Qualifier).getAsString()) + " ")86      .str();87}88 89static std::optional<FixItHint> changeValue(const VarDecl &Var,90                                            Qualifiers::TQ Qualifier,91                                            QualifierTarget QualTarget,92                                            QualifierPolicy QualPolicy,93                                            const ASTContext &Context) {94  switch (QualPolicy) {95  case QualifierPolicy::Left:96    return fixIfNotDangerous(Var.getTypeSpecStartLoc(),97                             buildQualifier(Qualifier));98  case QualifierPolicy::Right:99    std::optional<SourceLocation> IgnoredParens =100        skipLParensBackwards(Var.getLocation(), Context);101 102    if (IgnoredParens)103      return fixIfNotDangerous(*IgnoredParens, buildQualifier(Qualifier));104    return std::nullopt;105  }106  llvm_unreachable("Unknown QualifierPolicy enum");107}108 109static std::optional<FixItHint> changePointerItself(const VarDecl &Var,110                                                    Qualifiers::TQ Qualifier,111                                                    const ASTContext &Context) {112  if (locDangerous(Var.getLocation()))113    return std::nullopt;114 115  std::optional<SourceLocation> IgnoredParens =116      skipLParensBackwards(Var.getLocation(), Context);117  if (IgnoredParens)118    return fixIfNotDangerous(*IgnoredParens, buildQualifier(Qualifier));119  return std::nullopt;120}121 122static std::optional<FixItHint>123changePointer(const VarDecl &Var, Qualifiers::TQ Qualifier, const Type *Pointee,124              QualifierTarget QualTarget, QualifierPolicy QualPolicy,125              const ASTContext &Context) {126  // The pointer itself shall be marked as `const`. This is always to the right127  // of the '*' or in front of the identifier.128  if (QualTarget == QualifierTarget::Value)129    return changePointerItself(Var, Qualifier, Context);130 131  // Mark the pointee `const` that is a normal value (`int* p = nullptr;`).132  if (QualTarget == QualifierTarget::Pointee && isValueType(Pointee)) {133    // Adding the `const` on the left side is just the beginning of the type134    // specification. (`const int* p = nullptr;`)135    if (QualPolicy == QualifierPolicy::Left)136      return fixIfNotDangerous(Var.getTypeSpecStartLoc(),137                               buildQualifier(Qualifier));138 139    // Adding the `const` on the right side of the value type requires finding140    // the `*` token and placing the `const` left of it.141    // (`int const* p = nullptr;`)142    if (QualPolicy == QualifierPolicy::Right) {143      const SourceLocation BeforeStar = lexer::findPreviousTokenKind(144          Var.getLocation(), Context.getSourceManager(), Context.getLangOpts(),145          tok::star);146      if (locDangerous(BeforeStar))147        return std::nullopt;148 149      std::optional<SourceLocation> IgnoredParens =150          skipLParensBackwards(BeforeStar, Context);151 152      if (IgnoredParens)153        return fixIfNotDangerous(*IgnoredParens,154                                 buildQualifier(Qualifier, true));155      return std::nullopt;156    }157  }158 159  if (QualTarget == QualifierTarget::Pointee && Pointee->isPointerType()) {160    // Adding the `const` to the pointee if the pointee is a pointer161    // is the same as 'QualPolicy == Right && isValueType(Pointee)'.162    // The `const` must be left of the last `*` token.163    // (`int * const* p = nullptr;`)164    const SourceLocation BeforeStar = lexer::findPreviousTokenKind(165        Var.getLocation(), Context.getSourceManager(), Context.getLangOpts(),166        tok::star);167    return fixIfNotDangerous(BeforeStar, buildQualifier(Qualifier, true));168  }169 170  return std::nullopt;171}172 173static std::optional<FixItHint>174changeReferencee(const VarDecl &Var, Qualifiers::TQ Qualifier, QualType Pointee,175                 QualifierTarget QualTarget, QualifierPolicy QualPolicy,176                 const ASTContext &Context) {177  if (QualPolicy == QualifierPolicy::Left && isValueType(Pointee))178    return fixIfNotDangerous(Var.getTypeSpecStartLoc(),179                             buildQualifier(Qualifier));180 181  const SourceLocation BeforeRef = lexer::findPreviousAnyTokenKind(182      Var.getLocation(), Context.getSourceManager(), Context.getLangOpts(),183      tok::amp, tok::ampamp);184  std::optional<SourceLocation> IgnoredParens =185      skipLParensBackwards(BeforeRef, Context);186  if (IgnoredParens)187    return fixIfNotDangerous(*IgnoredParens, buildQualifier(Qualifier, true));188 189  return std::nullopt;190}191 192std::optional<FixItHint> addQualifierToVarDecl(const VarDecl &Var,193                                               const ASTContext &Context,194                                               Qualifiers::TQ Qualifier,195                                               QualifierTarget QualTarget,196                                               QualifierPolicy QualPolicy) {197  assert((QualPolicy == QualifierPolicy::Left ||198          QualPolicy == QualifierPolicy::Right) &&199         "Unexpected Insertion Policy");200  assert((QualTarget == QualifierTarget::Pointee ||201          QualTarget == QualifierTarget::Value) &&202         "Unexpected Target");203 204  const QualType ParenStrippedType = Var.getType().IgnoreParens();205  if (isValueType(ParenStrippedType))206    return changeValue(Var, Qualifier, QualTarget, QualPolicy, Context);207 208  if (ParenStrippedType->isReferenceType())209    return changeReferencee(Var, Qualifier, Var.getType()->getPointeeType(),210                            QualTarget, QualPolicy, Context);211 212  if (isMemberOrFunctionPointer(ParenStrippedType))213    return changePointerItself(Var, Qualifier, Context);214 215  if (ParenStrippedType->isPointerType())216    return changePointer(Var, Qualifier,217                         ParenStrippedType->getPointeeType().getTypePtr(),218                         QualTarget, QualPolicy, Context);219 220  if (ParenStrippedType->isArrayType()) {221    const Type *AT = ParenStrippedType->getBaseElementTypeUnsafe();222    assert(AT && "Did not retrieve array element type for an array.");223 224    if (isValueType(AT))225      return changeValue(Var, Qualifier, QualTarget, QualPolicy, Context);226 227    if (AT->isPointerType())228      return changePointer(Var, Qualifier, AT->getPointeeType().getTypePtr(),229                           QualTarget, QualPolicy, Context);230  }231 232  return std::nullopt;233}234 235bool areParensNeededForStatement(const Stmt &Node) {236  if (isa<ParenExpr>(&Node))237    return false;238 239  if (isa<clang::BinaryOperator>(&Node) || isa<UnaryOperator>(&Node))240    return true;241 242  if (isa<clang::ConditionalOperator>(&Node) ||243      isa<BinaryConditionalOperator>(&Node))244    return true;245 246  if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(&Node)) {247    switch (Op->getOperator()) {248    case OO_PlusPlus:249      [[fallthrough]];250    case OO_MinusMinus:251      return Op->getNumArgs() != 2;252    case OO_Call:253      [[fallthrough]];254    case OO_Subscript:255      [[fallthrough]];256    case OO_Arrow:257      return false;258    default:259      return true;260    };261  }262 263  if (isa<CStyleCastExpr>(&Node))264    return true;265 266  return false;267}268 269// Return true if expr needs to be put in parens when it is an argument of a270// prefix unary operator, e.g. when it is a binary or ternary operator271// syntactically.272static bool needParensAfterUnaryOperator(const Expr &ExprNode) {273  if (isa<clang::BinaryOperator>(&ExprNode) ||274      isa<clang::ConditionalOperator>(&ExprNode)) {275    return true;276  }277  if (const auto *Op = dyn_cast<CXXOperatorCallExpr>(&ExprNode)) {278    return Op->getNumArgs() == 2 && Op->getOperator() != OO_PlusPlus &&279           Op->getOperator() != OO_MinusMinus && Op->getOperator() != OO_Call &&280           Op->getOperator() != OO_Subscript;281  }282  return false;283}284 285// Format a pointer to an expression: prefix with '*' but simplify286// when it already begins with '&'.  Return empty string on failure.287std::string formatDereference(const Expr &ExprNode, const ASTContext &Context) {288  if (const auto *Op = dyn_cast<clang::UnaryOperator>(&ExprNode)) {289    if (Op->getOpcode() == UO_AddrOf) {290      // Strip leading '&'.291      return std::string(292          tooling::fixit::getText(*Op->getSubExpr()->IgnoreParens(), Context));293    }294  }295  StringRef Text = tooling::fixit::getText(ExprNode, Context);296 297  if (Text.empty())298    return {};299 300  // Remove remaining '->' from overloaded operator call301  Text.consume_back("->");302 303  // Add leading '*'.304  if (needParensAfterUnaryOperator(ExprNode)) {305    return (llvm::Twine("*(") + Text + ")").str();306  }307  return (llvm::Twine("*") + Text).str();308}309 310} // namespace clang::tidy::utils::fixit311