brintos

brintos / llvm-project-archived public Read only

0
0
Text · 7.7 KiB · e798f6f Raw
205 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 "UseDesignatedInitializersCheck.h"10#include "../utils/DesignatedInitializers.h"11#include "clang/AST/APValue.h"12#include "clang/AST/Decl.h"13#include "clang/AST/Expr.h"14#include "clang/AST/Stmt.h"15#include "clang/ASTMatchers/ASTMatchFinder.h"16#include "clang/ASTMatchers/ASTMatchers.h"17#include "clang/ASTMatchers/ASTMatchersMacros.h"18#include "clang/Basic/Diagnostic.h"19#include "clang/Lex/Lexer.h"20 21using namespace clang::ast_matchers;22 23namespace clang::tidy::modernize {24 25static constexpr char IgnoreSingleElementAggregatesName[] =26    "IgnoreSingleElementAggregates";27static constexpr bool IgnoreSingleElementAggregatesDefault = true;28 29static constexpr char RestrictToPODTypesName[] = "RestrictToPODTypes";30static constexpr bool RestrictToPODTypesDefault = false;31 32static constexpr char IgnoreMacrosName[] = "IgnoreMacros";33static constexpr bool IgnoreMacrosDefault = true;34 35static constexpr char StrictCStandardComplianceName[] =36    "StrictCStandardCompliance";37static constexpr bool StrictCStandardComplianceDefault = true;38 39static constexpr char StrictCppStandardComplianceName[] =40    "StrictCppStandardCompliance";41static constexpr bool StrictCppStandardComplianceDefault = true;42 43static unsigned getNumberOfDesignated(const InitListExpr *SyntacticInitList) {44  return llvm::count_if(*SyntacticInitList, [](auto *InitExpr) {45    return isa<DesignatedInitExpr>(InitExpr);46  });47}48 49namespace {50 51struct Designators {52  Designators(const InitListExpr *InitList) : InitList(InitList) {53    assert(InitList->isSyntacticForm());54  };55 56  unsigned size() { return getCached().size(); }57 58  std::optional<llvm::StringRef> operator[](const SourceLocation &Location) {59    const auto &Designators = getCached();60    const auto Result = Designators.find(Location);61    if (Result == Designators.end())62      return {};63    const llvm::StringRef Designator = Result->getSecond();64    return (Designator.front() == '.' ? Designator.substr(1) : Designator)65        .trim("\0"); // Trim NULL characters appearing on Windows in the66                     // name.67  }68 69private:70  using LocationToNameMap = llvm::DenseMap<clang::SourceLocation, std::string>;71 72  std::optional<LocationToNameMap> CachedDesignators;73  const InitListExpr *InitList;74 75  LocationToNameMap &getCached() {76    return CachedDesignators ? *CachedDesignators77                             : CachedDesignators.emplace(78                                   utils::getUnwrittenDesignators(InitList));79  }80};81 82AST_MATCHER(CXXRecordDecl, isAggregate) {83  return Node.hasDefinition() && Node.isAggregate();84}85 86AST_MATCHER(CXXRecordDecl, isPOD) {87  return Node.hasDefinition() && Node.isPOD();88}89 90AST_MATCHER(InitListExpr, isFullyDesignated) {91  if (const InitListExpr *SyntacticForm =92          Node.isSyntacticForm() ? &Node : Node.getSyntacticForm())93    return getNumberOfDesignated(SyntacticForm) == SyntacticForm->getNumInits();94  return true;95}96 97AST_MATCHER(InitListExpr, hasMoreThanOneElement) {98  return Node.getNumInits() > 1;99}100 101} // namespace102 103UseDesignatedInitializersCheck::UseDesignatedInitializersCheck(104    StringRef Name, ClangTidyContext *Context)105    : ClangTidyCheck(Name, Context), IgnoreSingleElementAggregates(Options.get(106                                         IgnoreSingleElementAggregatesName,107                                         IgnoreSingleElementAggregatesDefault)),108      RestrictToPODTypes(109          Options.get(RestrictToPODTypesName, RestrictToPODTypesDefault)),110      IgnoreMacros(Options.get(IgnoreMacrosName, IgnoreMacrosDefault)),111      StrictCStandardCompliance(Options.get(StrictCStandardComplianceName,112                                            StrictCStandardComplianceDefault)),113      StrictCppStandardCompliance(114          Options.get(StrictCppStandardComplianceName,115                      StrictCppStandardComplianceDefault)) {}116 117void UseDesignatedInitializersCheck::registerMatchers(MatchFinder *Finder) {118  const auto HasBaseWithFields =119      hasAnyBase(hasType(cxxRecordDecl(has(fieldDecl()))));120  Finder->addMatcher(121      initListExpr(122          hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(123              cxxRecordDecl(124                  RestrictToPODTypes ? isPOD() : isAggregate(),125                  unless(anyOf(HasBaseWithFields, hasName("::std::array"))))126                  .bind("type"))))),127          IgnoreSingleElementAggregates ? hasMoreThanOneElement() : anything(),128          unless(isFullyDesignated()))129          .bind("init"),130      this);131}132 133void UseDesignatedInitializersCheck::check(134    const MatchFinder::MatchResult &Result) {135  const auto *InitList = Result.Nodes.getNodeAs<InitListExpr>("init");136  const auto *Type = Result.Nodes.getNodeAs<CXXRecordDecl>("type");137  if (!Type || !InitList)138    return;139  const auto *SyntacticInitList = InitList->getSyntacticForm();140  if (!SyntacticInitList)141    return;142  Designators Designators{SyntacticInitList};143  const unsigned NumberOfDesignated = getNumberOfDesignated(SyntacticInitList);144  if (SyntacticInitList->getNumInits() - NumberOfDesignated >145      Designators.size())146    return;147 148  // If the whole initializer list is un-designated, issue only one warning and149  // a single fix-it for the whole expression.150  if (0 == NumberOfDesignated) {151    if (IgnoreMacros && InitList->getBeginLoc().isMacroID())152      return;153    {154      const DiagnosticBuilder Diag =155          diag(InitList->getLBraceLoc(),156               "use designated initializer list to initialize %0");157      Diag << InitList->getType() << InitList->getSourceRange();158      for (const Stmt *InitExpr : *SyntacticInitList) {159        const auto Designator = Designators[InitExpr->getBeginLoc()];160        if (Designator && !Designator->empty())161          Diag << FixItHint::CreateInsertion(InitExpr->getBeginLoc(),162                                             ("." + *Designator + "=").str());163      }164    }165    diag(Type->getBeginLoc(), "aggregate type is defined here",166         DiagnosticIDs::Note);167    return;168  }169 170  // In case that only a few elements are un-designated (not all as before), the171  // check offers dedicated issues and fix-its for each of them.172  for (const auto *InitExpr : *SyntacticInitList) {173    if (isa<DesignatedInitExpr>(InitExpr))174      continue;175    if (IgnoreMacros && InitExpr->getBeginLoc().isMacroID())176      continue;177    const auto Designator = Designators[InitExpr->getBeginLoc()];178    if (!Designator || Designator->empty()) {179      // There should always be a designator. If there's unexpectedly none, we180      // at least report a generic diagnostic.181      diag(InitExpr->getBeginLoc(), "use designated init expression")182          << InitExpr->getSourceRange();183    } else {184      diag(InitExpr->getBeginLoc(),185           "use designated init expression to initialize field '%0'")186          << InitExpr->getSourceRange() << *Designator187          << FixItHint::CreateInsertion(InitExpr->getBeginLoc(),188                                        ("." + *Designator + "=").str());189    }190  }191}192 193void UseDesignatedInitializersCheck::storeOptions(194    ClangTidyOptions::OptionMap &Opts) {195  Options.store(Opts, IgnoreSingleElementAggregatesName,196                IgnoreSingleElementAggregates);197  Options.store(Opts, RestrictToPODTypesName, RestrictToPODTypes);198  Options.store(Opts, IgnoreMacrosName, IgnoreMacros);199  Options.store(Opts, StrictCStandardComplianceName, StrictCStandardCompliance);200  Options.store(Opts, StrictCppStandardComplianceName,201                StrictCppStandardCompliance);202}203 204} // namespace clang::tidy::modernize205