brintos

brintos / llvm-project-archived public Read only

0
0
Text · 6.2 KiB · 2b55bb8 Raw
176 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 "BracesAroundStatementsCheck.h"10#include "../utils/BracesAroundStatement.h"11#include "../utils/LexerUtils.h"12#include "clang/AST/ASTContext.h"13#include "clang/ASTMatchers/ASTMatchers.h"14#include "clang/Lex/Lexer.h"15 16using namespace clang::ast_matchers;17 18namespace clang::tidy::readability {19 20static tok::TokenKind getTokenKind(SourceLocation Loc, const SourceManager &SM,21                                   const LangOptions &LangOpts) {22  Token Tok;23  const SourceLocation Beginning =24      Lexer::GetBeginningOfToken(Loc, SM, LangOpts);25  const bool Invalid = Lexer::getRawToken(Beginning, Tok, SM, LangOpts);26  assert(!Invalid && "Expected a valid token.");27 28  if (Invalid)29    return tok::NUM_TOKENS;30 31  return Tok.getKind();32}33 34static SourceLocation35forwardSkipWhitespaceAndComments(SourceLocation Loc, const SourceManager &SM,36                                 const LangOptions &LangOpts) {37  assert(Loc.isValid());38  for (;;) {39    while (isWhitespace(*SM.getCharacterData(Loc)))40      Loc = Loc.getLocWithOffset(1);41 42    const tok::TokenKind TokKind = getTokenKind(Loc, SM, LangOpts);43    if (TokKind != tok::comment)44      return Loc;45 46    // Fast-forward current token.47    Loc = Lexer::getLocForEndOfToken(Loc, 0, SM, LangOpts);48  }49}50 51BracesAroundStatementsCheck::BracesAroundStatementsCheck(52    StringRef Name, ClangTidyContext *Context)53    : ClangTidyCheck(Name, Context),54      // Always add braces by default.55      ShortStatementLines(Options.get("ShortStatementLines", 0U)) {}56 57void BracesAroundStatementsCheck::storeOptions(58    ClangTidyOptions::OptionMap &Opts) {59  Options.store(Opts, "ShortStatementLines", ShortStatementLines);60}61 62void BracesAroundStatementsCheck::registerMatchers(MatchFinder *Finder) {63  Finder->addMatcher(ifStmt().bind("if"), this);64  Finder->addMatcher(whileStmt().bind("while"), this);65  Finder->addMatcher(doStmt().bind("do"), this);66  Finder->addMatcher(forStmt().bind("for"), this);67  Finder->addMatcher(cxxForRangeStmt().bind("for-range"), this);68}69 70void BracesAroundStatementsCheck::check(71    const MatchFinder::MatchResult &Result) {72  const SourceManager &SM = *Result.SourceManager;73  const ASTContext *Context = Result.Context;74 75  // Get location of closing parenthesis or 'do' to insert opening brace.76  if (const auto *S = Result.Nodes.getNodeAs<ForStmt>("for")) {77    checkStmt(Result, S->getBody(), S->getRParenLoc());78  } else if (const auto *S =79                 Result.Nodes.getNodeAs<CXXForRangeStmt>("for-range")) {80    checkStmt(Result, S->getBody(), S->getRParenLoc());81  } else if (const auto *S = Result.Nodes.getNodeAs<DoStmt>("do")) {82    checkStmt(Result, S->getBody(), S->getDoLoc(), S->getWhileLoc());83  } else if (const auto *S = Result.Nodes.getNodeAs<WhileStmt>("while")) {84    const SourceLocation StartLoc =85        findRParenLoc(S, SM, Context->getLangOpts());86    if (StartLoc.isInvalid())87      return;88    checkStmt(Result, S->getBody(), StartLoc);89  } else if (const auto *S = Result.Nodes.getNodeAs<IfStmt>("if")) {90    // "if consteval" always has braces.91    if (S->isConsteval())92      return;93 94    const SourceLocation StartLoc =95        findRParenLoc(S, SM, Context->getLangOpts());96    if (StartLoc.isInvalid())97      return;98    if (ForceBracesStmts.erase(S))99      ForceBracesStmts.insert(S->getThen());100    const bool BracedIf =101        checkStmt(Result, S->getThen(), StartLoc, S->getElseLoc());102    const Stmt *Else = S->getElse();103    if (Else && BracedIf)104      ForceBracesStmts.insert(Else);105    if (Else && !isa<IfStmt>(Else)) {106      // Omit 'else if' statements here, they will be handled directly.107      checkStmt(Result, Else, S->getElseLoc());108    }109  } else {110    llvm_unreachable("Invalid match");111  }112}113 114/// Find location of right parenthesis closing condition.115template <typename IfOrWhileStmt>116SourceLocation117BracesAroundStatementsCheck::findRParenLoc(const IfOrWhileStmt *S,118                                           const SourceManager &SM,119                                           const LangOptions &LangOpts) {120  // Skip macros.121  if (S->getBeginLoc().isMacroID())122    return {};123 124  SourceLocation CondEndLoc = S->getCond()->getEndLoc();125  if (const DeclStmt *CondVar = S->getConditionVariableDeclStmt())126    CondEndLoc = CondVar->getEndLoc();127 128  if (!CondEndLoc.isValid()) {129    return {};130  }131 132  const SourceLocation PastCondEndLoc =133      Lexer::getLocForEndOfToken(CondEndLoc, 0, SM, LangOpts);134  if (PastCondEndLoc.isInvalid())135    return {};136  SourceLocation RParenLoc =137      forwardSkipWhitespaceAndComments(PastCondEndLoc, SM, LangOpts);138  if (RParenLoc.isInvalid())139    return {};140  const tok::TokenKind TokKind = getTokenKind(RParenLoc, SM, LangOpts);141  if (TokKind != tok::r_paren)142    return {};143  return RParenLoc;144}145 146/// Determine if the statement needs braces around it, and add them if it does.147/// Returns true if braces where added.148bool BracesAroundStatementsCheck::checkStmt(149    const MatchFinder::MatchResult &Result, const Stmt *S,150    SourceLocation StartLoc, SourceLocation EndLocHint) {151  while (const auto *AS = dyn_cast<AttributedStmt>(S))152    S = AS->getSubStmt();153 154  const auto BraceInsertionHints = utils::getBraceInsertionsHints(155      S, Result.Context->getLangOpts(), *Result.SourceManager, StartLoc,156      EndLocHint);157  if (BraceInsertionHints) {158    if (ShortStatementLines && !ForceBracesStmts.erase(S) &&159        BraceInsertionHints.resultingCompoundLineExtent(*Result.SourceManager) <160            ShortStatementLines)161      return false;162    auto Diag = diag(BraceInsertionHints.DiagnosticPos,163                     "statement should be inside braces");164    if (BraceInsertionHints.offersFixIts())165      Diag << BraceInsertionHints.openingBraceFixIt()166           << BraceInsertionHints.closingBraceFixIt();167  }168  return true;169}170 171void BracesAroundStatementsCheck::onEndOfTranslationUnit() {172  ForceBracesStmts.clear();173}174 175} // namespace clang::tidy::readability176