brintos

brintos / llvm-project-archived public Read only

0
0
Text · 12.2 KiB · 556f7fe Raw
320 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 "QualifiedAutoCheck.h"10#include "../utils/LexerUtils.h"11#include "../utils/Matchers.h"12#include "../utils/OptionsUtils.h"13#include "clang/ASTMatchers/ASTMatchers.h"14#include "llvm/ADT/SmallVector.h"15#include <optional>16 17using namespace clang::ast_matchers;18 19namespace clang::tidy::readability {20 21namespace {22 23// FIXME move to ASTMatchers24AST_MATCHER_P(QualType, hasUnqualifiedType,25              ast_matchers::internal::Matcher<QualType>, InnerMatcher) {26  return InnerMatcher.matches(Node.getUnqualifiedType(), Finder, Builder);27}28 29enum class Qualifier { Const, Volatile, Restrict };30 31} // namespace32 33static std::optional<Token>34findQualToken(const VarDecl *Decl, Qualifier Qual,35              const MatchFinder::MatchResult &Result) {36  // Since either of the locs can be in a macro, use `makeFileCharRange` to be37  // sure that we have a consistent `CharSourceRange`, located entirely in the38  // source file.39 40  assert((Qual == Qualifier::Const || Qual == Qualifier::Volatile ||41          Qual == Qualifier::Restrict) &&42         "Invalid Qualifier");43 44  SourceLocation BeginLoc = Decl->getQualifierLoc().getBeginLoc();45  if (BeginLoc.isInvalid())46    BeginLoc = Decl->getBeginLoc();47  const SourceLocation EndLoc = Decl->getLocation();48 49  const CharSourceRange FileRange = Lexer::makeFileCharRange(50      CharSourceRange::getCharRange(BeginLoc, EndLoc), *Result.SourceManager,51      Result.Context->getLangOpts());52 53  if (FileRange.isInvalid())54    return std::nullopt;55 56  const tok::TokenKind Tok = Qual == Qualifier::Const      ? tok::kw_const57                             : Qual == Qualifier::Volatile ? tok::kw_volatile58                                                           : tok::kw_restrict;59 60  return utils::lexer::getQualifyingToken(Tok, FileRange, *Result.Context,61                                          *Result.SourceManager);62}63 64static std::optional<SourceRange>65getTypeSpecifierLocation(const VarDecl *Var,66                         const MatchFinder::MatchResult &Result) {67  SourceRange TypeSpecifier(68      Var->getTypeSpecStartLoc(),69      Var->getTypeSpecEndLoc().getLocWithOffset(Lexer::MeasureTokenLength(70          Var->getTypeSpecEndLoc(), *Result.SourceManager,71          Result.Context->getLangOpts())));72 73  if (TypeSpecifier.getBegin().isMacroID() ||74      TypeSpecifier.getEnd().isMacroID())75    return std::nullopt;76  return TypeSpecifier;77}78 79static std::optional<SourceRange>80mergeReplacementRange(SourceRange &TypeSpecifier, const Token &ConstToken) {81  if (TypeSpecifier.getBegin().getLocWithOffset(-1) == ConstToken.getEndLoc()) {82    TypeSpecifier.setBegin(ConstToken.getLocation());83    return std::nullopt;84  }85  if (TypeSpecifier.getEnd().getLocWithOffset(1) == ConstToken.getLocation()) {86    TypeSpecifier.setEnd(ConstToken.getEndLoc());87    return std::nullopt;88  }89  return SourceRange(ConstToken.getLocation(), ConstToken.getEndLoc());90}91 92static bool isPointerConst(QualType QType) {93  const QualType Pointee = QType->getPointeeType();94  assert(!Pointee.isNull() && "can't have a null Pointee");95  return Pointee.isConstQualified();96}97 98static bool isAutoPointerConst(QualType QType) {99  const QualType Pointee =100      cast<AutoType>(QType->getPointeeType().getTypePtr())->desugar();101  assert(!Pointee.isNull() && "can't have a null Pointee");102  return Pointee.isConstQualified();103}104 105QualifiedAutoCheck::QualifiedAutoCheck(StringRef Name,106                                       ClangTidyContext *Context)107    : ClangTidyCheck(Name, Context),108      AddConstToQualified(Options.get("AddConstToQualified", true)),109      AllowedTypes(110          utils::options::parseStringList(Options.get("AllowedTypes", ""))),111      IgnoreAliasing(Options.get("IgnoreAliasing", true)) {}112 113void QualifiedAutoCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {114  Options.store(Opts, "AddConstToQualified", AddConstToQualified);115  Options.store(Opts, "AllowedTypes",116                utils::options::serializeStringList(AllowedTypes));117  Options.store(Opts, "IgnoreAliasing", IgnoreAliasing);118}119 120void QualifiedAutoCheck::registerMatchers(MatchFinder *Finder) {121  auto ExplicitSingleVarDecl =122      [](const ast_matchers::internal::Matcher<VarDecl> &InnerMatcher,123         llvm::StringRef ID) {124        return declStmt(125            unless(isInTemplateInstantiation()),126            hasSingleDecl(127                varDecl(unless(isImplicit()), InnerMatcher).bind(ID)));128      };129  auto ExplicitSingleVarDeclInTemplate =130      [](const ast_matchers::internal::Matcher<VarDecl> &InnerMatcher,131         llvm::StringRef ID) {132        return declStmt(133            isInTemplateInstantiation(),134            hasSingleDecl(135                varDecl(unless(isImplicit()), InnerMatcher).bind(ID)));136      };137 138  auto IsBoundToType = refersToType(equalsBoundNode("type"));139  auto UnlessFunctionType = unless(hasUnqualifiedDesugaredType(functionType()));140 141  auto IsPointerType = [this](const auto &...InnerMatchers) {142    if (this->IgnoreAliasing) {143      return qualType(144          hasUnqualifiedDesugaredType(pointerType(pointee(InnerMatchers...))));145    }146    return qualType(anyOf(qualType(pointerType(pointee(InnerMatchers...))),147                          qualType(substTemplateTypeParmType(hasReplacementType(148                              pointerType(pointee(InnerMatchers...)))))));149  };150 151  auto IsAutoDeducedToPointer =152      [IsPointerType](const std::vector<StringRef> &AllowedTypes,153                      const auto &...InnerMatchers) {154        return autoType(hasDeducedType(155            IsPointerType(InnerMatchers...),156            unless(hasUnqualifiedType(157                matchers::matchesAnyListedTypeName(AllowedTypes, false))),158            unless(pointerType(pointee(hasUnqualifiedType(159                matchers::matchesAnyListedTypeName(AllowedTypes, false)))))));160      };161 162  Finder->addMatcher(163      ExplicitSingleVarDecl(164          hasType(IsAutoDeducedToPointer(AllowedTypes, UnlessFunctionType)),165          "auto"),166      this);167 168  Finder->addMatcher(169      ExplicitSingleVarDeclInTemplate(170          allOf(hasType(IsAutoDeducedToPointer(171                    AllowedTypes, hasUnqualifiedType(qualType().bind("type")),172                    UnlessFunctionType)),173                anyOf(hasAncestor(174                          functionDecl(hasAnyTemplateArgument(IsBoundToType))),175                      hasAncestor(classTemplateSpecializationDecl(176                          hasAnyTemplateArgument(IsBoundToType))))),177          "auto"),178      this);179  if (!AddConstToQualified)180    return;181  Finder->addMatcher(ExplicitSingleVarDecl(182                         hasType(pointerType(pointee(autoType()))), "auto_ptr"),183                     this);184  Finder->addMatcher(185      ExplicitSingleVarDecl(hasType(lValueReferenceType(pointee(autoType()))),186                            "auto_ref"),187      this);188}189 190void QualifiedAutoCheck::check(const MatchFinder::MatchResult &Result) {191  if (const auto *Var = Result.Nodes.getNodeAs<VarDecl>("auto")) {192    SourceRange TypeSpecifier;193    if (std::optional<SourceRange> TypeSpec =194            getTypeSpecifierLocation(Var, Result)) {195      TypeSpecifier = *TypeSpec;196    } else197      return;198 199    llvm::SmallVector<SourceRange, 4> RemoveQualifiersRange;200    auto CheckQualifier = [&](bool IsPresent, Qualifier Qual) {201      if (IsPresent) {202        std::optional<Token> Token = findQualToken(Var, Qual, Result);203        if (!Token || Token->getLocation().isMacroID())204          return true; // Disregard this VarDecl.205        if (std::optional<SourceRange> Result =206                mergeReplacementRange(TypeSpecifier, *Token))207          RemoveQualifiersRange.push_back(*Result);208      }209      return false;210    };211 212    bool IsLocalConst = Var->getType().isLocalConstQualified();213    bool IsLocalVolatile = Var->getType().isLocalVolatileQualified();214    bool IsLocalRestrict = Var->getType().isLocalRestrictQualified();215 216    if (CheckQualifier(IsLocalConst, Qualifier::Const) ||217        CheckQualifier(IsLocalVolatile, Qualifier::Volatile) ||218        CheckQualifier(IsLocalRestrict, Qualifier::Restrict))219      return;220 221    // Check for bridging the gap between the asterisk and name.222    if (Var->getLocation() == TypeSpecifier.getEnd().getLocWithOffset(1))223      TypeSpecifier.setEnd(TypeSpecifier.getEnd().getLocWithOffset(1));224 225    const CharSourceRange FixItRange =226        CharSourceRange::getCharRange(TypeSpecifier);227    if (FixItRange.isInvalid())228      return;229 230    SourceLocation FixitLoc = FixItRange.getBegin();231    for (const SourceRange &Range : RemoveQualifiersRange) {232      if (Range.getBegin() < FixitLoc)233        FixitLoc = Range.getBegin();234    }235 236    const std::string ReplStr = [&] {237      const StringRef PtrConst = isPointerConst(Var->getType()) ? "const " : "";238      const StringRef LocalConst = IsLocalConst ? "const " : "";239      const StringRef LocalVol = IsLocalVolatile ? "volatile " : "";240      const StringRef LocalRestrict = IsLocalRestrict ? "__restrict " : "";241      return (PtrConst + "auto *" + LocalConst + LocalVol + LocalRestrict)242          .str();243    }();244 245    const DiagnosticBuilder Diag =246        diag(FixitLoc,247             "'%select{|const }0%select{|volatile }1%select{|__restrict }2auto "248             "%3' can be declared as '%4%3'")249        << IsLocalConst << IsLocalVolatile << IsLocalRestrict << Var->getName()250        << ReplStr;251 252    for (const SourceRange &Range : RemoveQualifiersRange) {253      Diag << FixItHint::CreateRemoval(CharSourceRange::getCharRange(Range));254    }255 256    Diag << FixItHint::CreateReplacement(FixItRange, ReplStr);257    return;258  }259  if (const auto *Var = Result.Nodes.getNodeAs<VarDecl>("auto_ptr")) {260    if (!isPointerConst(Var->getType()))261      return; // Pointer isn't const, no need to add const qualifier.262    if (!isAutoPointerConst(Var->getType()))263      return; // Const isn't wrapped in the auto type, so must be declared264              // explicitly.265 266    if (Var->getType().isLocalConstQualified()) {267      std::optional<Token> Token = findQualToken(Var, Qualifier::Const, Result);268      if (!Token || Token->getLocation().isMacroID())269        return;270    }271    if (Var->getType().isLocalVolatileQualified()) {272      std::optional<Token> Token =273          findQualToken(Var, Qualifier::Volatile, Result);274      if (!Token || Token->getLocation().isMacroID())275        return;276    }277    if (Var->getType().isLocalRestrictQualified()) {278      std::optional<Token> Token =279          findQualToken(Var, Qualifier::Restrict, Result);280      if (!Token || Token->getLocation().isMacroID())281        return;282    }283 284    if (std::optional<SourceRange> TypeSpec =285            getTypeSpecifierLocation(Var, Result)) {286      if (TypeSpec->isInvalid() || TypeSpec->getBegin().isMacroID() ||287          TypeSpec->getEnd().isMacroID())288        return;289      const SourceLocation InsertPos = TypeSpec->getBegin();290      diag(InsertPos,291           "'auto *%select{|const }0%select{|volatile }1%2' can be declared as "292           "'const auto *%select{|const }0%select{|volatile }1%2'")293          << Var->getType().isLocalConstQualified()294          << Var->getType().isLocalVolatileQualified() << Var->getName()295          << FixItHint::CreateInsertion(InsertPos, "const ");296    }297    return;298  }299  if (const auto *Var = Result.Nodes.getNodeAs<VarDecl>("auto_ref")) {300    if (!isPointerConst(Var->getType()))301      return; // Pointer isn't const, no need to add const qualifier.302    if (!isAutoPointerConst(Var->getType()))303      // Const isn't wrapped in the auto type, so must be declared explicitly.304      return;305 306    if (std::optional<SourceRange> TypeSpec =307            getTypeSpecifierLocation(Var, Result)) {308      if (TypeSpec->isInvalid() || TypeSpec->getBegin().isMacroID() ||309          TypeSpec->getEnd().isMacroID())310        return;311      const SourceLocation InsertPos = TypeSpec->getBegin();312      diag(InsertPos, "'auto &%0' can be declared as 'const auto &%0'")313          << Var->getName() << FixItHint::CreateInsertion(InsertPos, "const ");314    }315    return;316  }317}318 319} // namespace clang::tidy::readability320