brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.0 KiB · 450961c Raw
140 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 "MisleadingIndentationCheck.h"10#include "../utils/LexerUtils.h"11#include "clang/AST/ASTContext.h"12#include "clang/ASTMatchers/ASTMatchFinder.h"13 14using namespace clang::ast_matchers;15 16namespace clang::tidy::readability {17 18static const IfStmt *getPrecedingIf(const SourceManager &SM,19                                    ASTContext *Context, const IfStmt *If) {20  auto Parents = Context->getParents(*If);21  if (Parents.size() != 1)22    return nullptr;23  if (const auto *PrecedingIf = Parents[0].get<IfStmt>()) {24    const SourceLocation PreviousElseLoc = PrecedingIf->getElseLoc();25    if (SM.getExpansionLineNumber(PreviousElseLoc) ==26        SM.getExpansionLineNumber(If->getIfLoc()))27      return PrecedingIf;28  }29  return nullptr;30}31 32void MisleadingIndentationCheck::danglingElseCheck(const SourceManager &SM,33                                                   ASTContext *Context,34                                                   const IfStmt *If) {35  SourceLocation IfLoc = If->getIfLoc();36  const SourceLocation ElseLoc = If->getElseLoc();37 38  if (IfLoc.isMacroID() || ElseLoc.isMacroID())39    return;40 41  if (SM.getExpansionLineNumber(If->getThen()->getEndLoc()) ==42      SM.getExpansionLineNumber(ElseLoc))43    return;44 45  // Find location of first 'if' in a 'if else if' chain.46  for (const auto *PrecedingIf = getPrecedingIf(SM, Context, If); PrecedingIf;47       PrecedingIf = getPrecedingIf(SM, Context, PrecedingIf))48    IfLoc = PrecedingIf->getIfLoc();49 50  if (SM.getExpansionColumnNumber(IfLoc) !=51      SM.getExpansionColumnNumber(ElseLoc))52    diag(ElseLoc, "different indentation for 'if' and corresponding 'else'");53}54 55static bool isAtStartOfLineIncludingEmptyMacro(SourceLocation NextLoc,56                                               const SourceManager &SM,57                                               const LangOptions &LangOpts) {58  const SourceLocation BeforeLoc =59      utils::lexer::getPreviousTokenAndStart(NextLoc, SM, LangOpts).second;60  if (BeforeLoc.isInvalid())61    return false;62  return SM.getExpansionLineNumber(BeforeLoc) !=63         SM.getExpansionLineNumber(NextLoc);64}65 66void MisleadingIndentationCheck::missingBracesCheck(67    const SourceManager &SM, const CompoundStmt *CStmt,68    const LangOptions &LangOpts) {69  const static StringRef StmtNames[] = {"if", "for", "while"};70  for (unsigned int I = 0; I < CStmt->size() - 1; I++) {71    const Stmt *CurrentStmt = CStmt->body_begin()[I];72    const Stmt *Inner = nullptr;73    int StmtKind = 0;74 75    if (const auto *CurrentIf = dyn_cast<IfStmt>(CurrentStmt)) {76      StmtKind = 0;77      Inner =78          CurrentIf->getElse() ? CurrentIf->getElse() : CurrentIf->getThen();79    } else if (const auto *CurrentFor = dyn_cast<ForStmt>(CurrentStmt)) {80      StmtKind = 1;81      Inner = CurrentFor->getBody();82    } else if (const auto *CurrentWhile = dyn_cast<WhileStmt>(CurrentStmt)) {83      StmtKind = 2;84      Inner = CurrentWhile->getBody();85    } else {86      continue;87    }88 89    if (isa<CompoundStmt>(Inner))90      continue;91 92    const SourceLocation InnerLoc = Inner->getBeginLoc();93    const SourceLocation OuterLoc = CurrentStmt->getBeginLoc();94 95    if (InnerLoc.isInvalid() || InnerLoc.isMacroID() || OuterLoc.isInvalid() ||96        OuterLoc.isMacroID())97      continue;98 99    if (SM.getExpansionLineNumber(InnerLoc) ==100        SM.getExpansionLineNumber(OuterLoc))101      continue;102 103    const Stmt *NextStmt = CStmt->body_begin()[I + 1];104    const SourceLocation NextLoc = NextStmt->getBeginLoc();105 106    if (NextLoc.isInvalid() || NextLoc.isMacroID())107      continue;108    if (!isAtStartOfLineIncludingEmptyMacro(NextLoc, SM, LangOpts))109      continue;110 111    if (SM.getExpansionColumnNumber(InnerLoc) ==112        SM.getExpansionColumnNumber(NextLoc)) {113      diag(NextLoc, "misleading indentation: statement is indented too deeply");114      diag(OuterLoc, "did you mean this line to be inside this '%0'",115           DiagnosticIDs::Note)116          << StmtNames[StmtKind];117    }118  }119}120 121void MisleadingIndentationCheck::registerMatchers(MatchFinder *Finder) {122  Finder->addMatcher(123      ifStmt(unless(hasThen(nullStmt())), hasElse(stmt())).bind("if"), this);124  Finder->addMatcher(125      compoundStmt(has(stmt(anyOf(ifStmt(), forStmt(), whileStmt()))))126          .bind("compound"),127      this);128}129 130void MisleadingIndentationCheck::check(const MatchFinder::MatchResult &Result) {131  if (const auto *If = Result.Nodes.getNodeAs<IfStmt>("if"))132    danglingElseCheck(*Result.SourceManager, Result.Context, If);133 134  if (const auto *CStmt = Result.Nodes.getNodeAs<CompoundStmt>("compound"))135    missingBracesCheck(*Result.SourceManager, CStmt,136                       Result.Context->getLangOpts());137}138 139} // namespace clang::tidy::readability140