brintos

brintos / llvm-project-archived public Read only

0
0
Text · 7.5 KiB · c0d38dc Raw
196 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 "TaggedUnionMemberCountCheck.h"10#include "../utils/OptionsUtils.h"11#include "clang/ASTMatchers/ASTMatchFinder.h"12#include "llvm/ADT/STLExtras.h"13#include "llvm/ADT/SmallSet.h"14 15using namespace clang::ast_matchers;16 17namespace clang::tidy::bugprone {18 19static constexpr llvm::StringLiteral StrictModeOptionName = "StrictMode";20static constexpr llvm::StringLiteral EnableCountingEnumHeuristicOptionName =21    "EnableCountingEnumHeuristic";22static constexpr llvm::StringLiteral CountingEnumPrefixesOptionName =23    "CountingEnumPrefixes";24static constexpr llvm::StringLiteral CountingEnumSuffixesOptionName =25    "CountingEnumSuffixes";26 27static constexpr bool StrictModeOptionDefaultValue = false;28static constexpr bool EnableCountingEnumHeuristicOptionDefaultValue = true;29static constexpr llvm::StringLiteral CountingEnumPrefixesOptionDefaultValue =30    "";31static constexpr llvm::StringLiteral CountingEnumSuffixesOptionDefaultValue =32    "count";33 34static constexpr llvm::StringLiteral RootMatchBindName = "root";35static constexpr llvm::StringLiteral UnionMatchBindName = "union";36static constexpr llvm::StringLiteral TagMatchBindName = "tags";37 38namespace {39 40AST_MATCHER_P2(RecordDecl, fieldCountOfKindIsOne,41               ast_matchers::internal::Matcher<FieldDecl>, InnerMatcher,42               StringRef, BindName) {43  // BoundNodesTreeBuilder resets itself when a match occurs.44  // So to avoid losing previously saved binds, a temporary instance45  // is used for matching.46  //47  // For precedence, see commit: 5b07de1a5faf4a22ae6fd982b877c5e7e3a7655948  clang::ast_matchers::internal::BoundNodesTreeBuilder TempBuilder;49 50  const FieldDecl *FirstMatch = nullptr;51  for (const FieldDecl *Field : Node.fields()) {52    if (InnerMatcher.matches(*Field, Finder, &TempBuilder)) {53      if (FirstMatch) {54        return false;55      }56      FirstMatch = Field;57    }58  }59 60  if (FirstMatch) {61    Builder->setBinding(BindName, clang::DynTypedNode::create(*FirstMatch));62    return true;63  }64  return false;65}66 67} // namespace68 69TaggedUnionMemberCountCheck::TaggedUnionMemberCountCheck(70    StringRef Name, ClangTidyContext *Context)71    : ClangTidyCheck(Name, Context),72      StrictMode(73          Options.get(StrictModeOptionName, StrictModeOptionDefaultValue)),74      EnableCountingEnumHeuristic(75          Options.get(EnableCountingEnumHeuristicOptionName,76                      EnableCountingEnumHeuristicOptionDefaultValue)),77      CountingEnumPrefixes(utils::options::parseStringList(78          Options.get(CountingEnumPrefixesOptionName,79                      CountingEnumPrefixesOptionDefaultValue))),80      CountingEnumSuffixes(utils::options::parseStringList(81          Options.get(CountingEnumSuffixesOptionName,82                      CountingEnumSuffixesOptionDefaultValue))) {83  if (!EnableCountingEnumHeuristic) {84    if (Options.get(CountingEnumPrefixesOptionName))85      configurationDiag("%0: Counting enum heuristic is disabled but "86                        "%1 is set")87          << Name << CountingEnumPrefixesOptionName;88    if (Options.get(CountingEnumSuffixesOptionName))89      configurationDiag("%0: Counting enum heuristic is disabled but "90                        "%1 is set")91          << Name << CountingEnumSuffixesOptionName;92  }93}94 95void TaggedUnionMemberCountCheck::storeOptions(96    ClangTidyOptions::OptionMap &Opts) {97  Options.store(Opts, StrictModeOptionName, StrictMode);98  Options.store(Opts, EnableCountingEnumHeuristicOptionName,99                EnableCountingEnumHeuristic);100  Options.store(Opts, CountingEnumPrefixesOptionName,101                utils::options::serializeStringList(CountingEnumPrefixes));102  Options.store(Opts, CountingEnumSuffixesOptionName,103                utils::options::serializeStringList(CountingEnumSuffixes));104}105 106void TaggedUnionMemberCountCheck::registerMatchers(MatchFinder *Finder) {107  auto NotFromSystemHeaderOrStdNamespace =108      unless(anyOf(isExpansionInSystemHeader(), isInStdNamespace()));109 110  auto UnionField =111      fieldDecl(hasType(qualType(hasCanonicalType(recordType(hasDeclaration(112          recordDecl(isUnion(), NotFromSystemHeaderOrStdNamespace)))))));113 114  auto EnumField = fieldDecl(hasType(qualType(hasCanonicalType(115      enumType(hasDeclaration(enumDecl(NotFromSystemHeaderOrStdNamespace)))))));116 117  auto HasOneUnionField = fieldCountOfKindIsOne(UnionField, UnionMatchBindName);118  auto HasOneEnumField = fieldCountOfKindIsOne(EnumField, TagMatchBindName);119 120  Finder->addMatcher(recordDecl(anyOf(isStruct(), isClass()), HasOneUnionField,121                                HasOneEnumField, unless(isImplicit()))122                         .bind(RootMatchBindName),123                     this);124}125 126bool TaggedUnionMemberCountCheck::isCountingEnumLikeName(StringRef Name) const {127  if (llvm::any_of(CountingEnumPrefixes, [Name](StringRef Prefix) -> bool {128        return Name.starts_with_insensitive(Prefix);129      }))130    return true;131  if (llvm::any_of(CountingEnumSuffixes, [Name](StringRef Suffix) -> bool {132        return Name.ends_with_insensitive(Suffix);133      }))134    return true;135  return false;136}137 138std::pair<const std::size_t, const EnumConstantDecl *>139TaggedUnionMemberCountCheck::getNumberOfEnumValues(const EnumDecl *ED) {140  llvm::SmallSet<llvm::APSInt, 16> EnumValues;141 142  const EnumConstantDecl *LastEnumConstant = nullptr;143  for (const EnumConstantDecl *Enumerator : ED->enumerators()) {144    EnumValues.insert(Enumerator->getInitVal());145    LastEnumConstant = Enumerator;146  }147 148  if (EnableCountingEnumHeuristic && LastEnumConstant &&149      isCountingEnumLikeName(LastEnumConstant->getName()) &&150      llvm::APSInt::isSameValue(LastEnumConstant->getInitVal(),151                                llvm::APSInt::get(EnumValues.size() - 1))) {152    return {EnumValues.size() - 1, LastEnumConstant};153  }154 155  return {EnumValues.size(), nullptr};156}157 158void TaggedUnionMemberCountCheck::check(159    const MatchFinder::MatchResult &Result) {160  const auto *Root = Result.Nodes.getNodeAs<RecordDecl>(RootMatchBindName);161  const auto *UnionField =162      Result.Nodes.getNodeAs<FieldDecl>(UnionMatchBindName);163  const auto *TagField = Result.Nodes.getNodeAs<FieldDecl>(TagMatchBindName);164 165  assert(Root && "Root is missing!");166  assert(UnionField && "UnionField is missing!");167  assert(TagField && "TagField is missing!");168  if (!Root || !UnionField || !TagField)169    return;170 171  const auto *UnionDef = UnionField->getType()->castAsRecordDecl();172  const auto *EnumDef = TagField->getType()->castAsEnumDecl();173 174  const std::size_t UnionMemberCount = llvm::range_size(UnionDef->fields());175  auto [TagCount, CountingEnumConstantDecl] = getNumberOfEnumValues(EnumDef);176 177  if (UnionMemberCount > TagCount) {178    diag(Root->getLocation(),179         "tagged union has more data members (%0) than tags (%1)!")180        << UnionMemberCount << TagCount;181  } else if (StrictMode && UnionMemberCount < TagCount) {182    diag(Root->getLocation(),183         "tagged union has fewer data members (%0) than tags (%1)!")184        << UnionMemberCount << TagCount;185  }186 187  if (CountingEnumConstantDecl) {188    diag(CountingEnumConstantDecl->getLocation(),189         "assuming that this constant is just an auxiliary value and not "190         "used for indicating a valid union data member",191         DiagnosticIDs::Note);192  }193}194 195} // namespace clang::tidy::bugprone196