brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.8 KiB · c79320f Raw
177 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 "MacroRepeatedSideEffectsCheck.h"10#include "clang/Basic/Builtins.h"11#include "clang/Frontend/CompilerInstance.h"12#include "clang/Lex/MacroArgs.h"13#include "clang/Lex/PPCallbacks.h"14#include "clang/Lex/Preprocessor.h"15#include <stack>16 17namespace clang::tidy::bugprone {18 19namespace {20class MacroRepeatedPPCallbacks : public PPCallbacks {21public:22  MacroRepeatedPPCallbacks(ClangTidyCheck &Check, Preprocessor &PP)23      : Check(Check), PP(PP) {}24 25  void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD,26                    SourceRange Range, const MacroArgs *Args) override;27 28private:29  ClangTidyCheck &Check;30  Preprocessor &PP;31 32  unsigned countArgumentExpansions(const MacroInfo *MI,33                                   const IdentifierInfo *Arg) const;34 35  bool hasSideEffects(const Token *ResultArgToks) const;36};37} // End of anonymous namespace.38 39void MacroRepeatedPPCallbacks::MacroExpands(const Token &MacroNameTok,40                                            const MacroDefinition &MD,41                                            SourceRange Range,42                                            const MacroArgs *Args) {43  // Ignore macro argument expansions.44  if (!Range.getBegin().isFileID())45    return;46 47  const MacroInfo *MI = MD.getMacroInfo();48 49  // Bail out if the contents of the macro are containing keywords that are50  // making the macro too complex.51  if (llvm::any_of(MI->tokens(), [](const Token &T) {52        return T.isOneOf(tok::kw_if, tok::kw_else, tok::kw_switch, tok::kw_case,53                         tok::kw_break, tok::kw_while, tok::kw_do, tok::kw_for,54                         tok::kw_continue, tok::kw_goto, tok::kw_return);55      }))56    return;57 58  for (unsigned ArgNo = 0U; ArgNo < MI->getNumParams(); ++ArgNo) {59    const IdentifierInfo *Arg = *(MI->param_begin() + ArgNo);60    const Token *ResultArgToks = Args->getUnexpArgument(ArgNo);61 62    if (hasSideEffects(ResultArgToks) &&63        countArgumentExpansions(MI, Arg) >= 2) {64      Check.diag(ResultArgToks->getLocation(),65                 "side effects in the %ordinal0 macro argument %1 are "66                 "repeated in macro expansion")67          << (ArgNo + 1) << Arg;68      Check.diag(MI->getDefinitionLoc(), "macro %0 defined here",69                 DiagnosticIDs::Note)70          << MacroNameTok.getIdentifierInfo();71    }72  }73}74 75unsigned MacroRepeatedPPCallbacks::countArgumentExpansions(76    const MacroInfo *MI, const IdentifierInfo *Arg) const {77  // Current argument count. When moving forward to a different control-flow78  // path this can decrease.79  unsigned Current = 0;80  // Max argument count.81  unsigned Max = 0;82  bool SkipParen = false;83  int SkipParenCount = 0;84  // Has a __builtin_constant_p been found?85  bool FoundBuiltin = false;86  bool PrevTokenIsHash = false;87  // Count when "?" is reached. The "Current" will get this value when the ":"88  // is reached.89  std::stack<unsigned, SmallVector<unsigned, 8>> CountAtQuestion;90  for (const auto &T : MI->tokens()) {91    // The result of __builtin_constant_p(x) is 0 if x is a macro argument92    // with side effects. If we see a __builtin_constant_p(x) followed by a93    // "?" "&&" or "||", then we need to reason about control flow to report94    // warnings correctly. Until such reasoning is added, bail out when this95    // happens.96    if (FoundBuiltin && T.isOneOf(tok::question, tok::ampamp, tok::pipepipe))97      return Max;98 99    // Skip stringified tokens.100    if (T.is(tok::hash)) {101      PrevTokenIsHash = true;102      continue;103    }104    if (PrevTokenIsHash) {105      PrevTokenIsHash = false;106      continue;107    }108 109    // Handling of ? and :.110    if (T.is(tok::question)) {111      CountAtQuestion.push(Current);112    } else if (T.is(tok::colon)) {113      if (CountAtQuestion.empty())114        return 0;115      Current = CountAtQuestion.top();116      CountAtQuestion.pop();117    }118 119    // If current token is a parenthesis, skip it.120    if (SkipParen) {121      if (T.is(tok::l_paren))122        SkipParenCount++;123      else if (T.is(tok::r_paren))124        SkipParenCount--;125      SkipParen = (SkipParenCount != 0);126      if (SkipParen)127        continue;128    }129 130    const IdentifierInfo *TII = T.getIdentifierInfo();131    // If not existent, skip it.132    if (TII == nullptr)133      continue;134 135    // If a __builtin_constant_p is found within the macro definition, don't136    // count arguments inside the parentheses and remember that it has been137    // seen in case there are "?", "&&" or "||" operators later.138    if (TII->getBuiltinID() == Builtin::BI__builtin_constant_p) {139      FoundBuiltin = true;140      SkipParen = true;141      continue;142    }143 144    // If another macro is found within the macro definition, skip the macro145    // and the eventual arguments.146    if (TII->hasMacroDefinition()) {147      const MacroInfo *M = PP.getMacroDefinition(TII).getMacroInfo();148      if (M != nullptr && M->isFunctionLike())149        SkipParen = true;150      continue;151    }152 153    // Count argument.154    if (TII == Arg) {155      Current++;156      Max = std::max(Max, Current);157    }158  }159  return Max;160}161 162bool MacroRepeatedPPCallbacks::hasSideEffects(163    const Token *ResultArgToks) const {164  for (; ResultArgToks->isNot(tok::eof); ++ResultArgToks) {165    if (ResultArgToks->isOneOf(tok::plusplus, tok::minusminus))166      return true;167  }168  return false;169}170 171void MacroRepeatedSideEffectsCheck::registerPPCallbacks(172    const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) {173  PP->addPPCallbacks(::std::make_unique<MacroRepeatedPPCallbacks>(*this, *PP));174}175 176} // namespace clang::tidy::bugprone177