brintos

brintos / llvm-project-archived public Read only

0
0
Text · 6.0 KiB · d0659ad Raw
171 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/// \file10/// This file provides utilities to put braces around a statement.11///12//===----------------------------------------------------------------------===//13 14#include "BracesAroundStatement.h"15#include "../utils/LexerUtils.h"16#include "LexerUtils.h"17#include "clang/AST/ASTContext.h"18#include "clang/Basic/CharInfo.h"19#include "clang/Basic/LangOptions.h"20#include "clang/Lex/Lexer.h"21 22namespace clang::tidy::utils {23 24BraceInsertionHints::operator bool() const { return DiagnosticPos.isValid(); }25 26bool BraceInsertionHints::offersFixIts() const {27  return OpeningBracePos.isValid() && ClosingBracePos.isValid();28}29 30unsigned BraceInsertionHints::resultingCompoundLineExtent(31    const SourceManager &SourceMgr) const {32  return SourceMgr.getSpellingLineNumber(ClosingBracePos) -33         SourceMgr.getSpellingLineNumber(OpeningBracePos);34}35 36FixItHint BraceInsertionHints::openingBraceFixIt() const {37  return OpeningBracePos.isValid()38             ? FixItHint::CreateInsertion(OpeningBracePos, " {")39             : FixItHint();40}41 42FixItHint BraceInsertionHints::closingBraceFixIt() const {43  return ClosingBracePos.isValid()44             ? FixItHint::CreateInsertion(ClosingBracePos, ClosingBrace)45             : FixItHint();46}47 48static tok::TokenKind getTokenKind(SourceLocation Loc, const SourceManager &SM,49                                   const LangOptions &LangOpts) {50  Token Tok;51  const SourceLocation Beginning =52      Lexer::GetBeginningOfToken(Loc, SM, LangOpts);53  const bool Invalid = Lexer::getRawToken(Beginning, Tok, SM, LangOpts);54  assert(!Invalid && "Expected a valid token.");55 56  if (Invalid)57    return tok::NUM_TOKENS;58 59  return Tok.getKind();60}61 62static SourceLocation findEndLocation(const Stmt &S, const SourceManager &SM,63                                      const LangOptions &LangOpts) {64  SourceLocation Loc = lexer::getUnifiedEndLoc(S, SM, LangOpts);65  if (!Loc.isValid())66    return Loc;67 68  // Start searching right after S.69  Loc = Loc.getLocWithOffset(1);70 71  for (;;) {72    assert(Loc.isValid());73    while (isHorizontalWhitespace(*SM.getCharacterData(Loc))) {74      Loc = Loc.getLocWithOffset(1);75    }76 77    if (isVerticalWhitespace(*SM.getCharacterData(Loc))) {78      // EOL, insert brace before.79      break;80    }81    const tok::TokenKind TokKind = getTokenKind(Loc, SM, LangOpts);82    if (TokKind != tok::comment) {83      // Non-comment token, insert brace before.84      break;85    }86 87    const SourceLocation TokEndLoc =88        Lexer::getLocForEndOfToken(Loc, 0, SM, LangOpts);89    const SourceRange TokRange(Loc, TokEndLoc);90    const StringRef Comment = Lexer::getSourceText(91        CharSourceRange::getTokenRange(TokRange), SM, LangOpts);92    if (Comment.starts_with("/*") && Comment.contains('\n')) {93      // Multi-line block comment, insert brace before.94      break;95    }96    // else: Trailing comment, insert brace after the newline.97 98    // Fast-forward current token.99    Loc = TokEndLoc;100  }101  return Loc;102}103 104BraceInsertionHints getBraceInsertionsHints(const Stmt *const S,105                                            const LangOptions &LangOpts,106                                            const SourceManager &SM,107                                            SourceLocation StartLoc,108                                            SourceLocation EndLocHint) {109  // 1) If there's a corresponding "else" or "while", the check inserts "} "110  // right before that token.111  // 2) If there's a multi-line block comment starting on the same line after112  // the location we're inserting the closing brace at, or there's a non-comment113  // token, the check inserts "\n}" right before that token.114  // 3) Otherwise the check finds the end of line (possibly after some block or115  // line comments) and inserts "\n}" right before that EOL.116  if (!S || isa<CompoundStmt>(S)) {117    // Already inside braces.118    return {};119  }120 121  // When TreeTransform, Stmt in constexpr IfStmt will be transform to NullStmt.122  // This NullStmt can be detected according to beginning token.123  const SourceLocation StmtBeginLoc = S->getBeginLoc();124  if (isa<NullStmt>(S) && StmtBeginLoc.isValid() &&125      getTokenKind(StmtBeginLoc, SM, LangOpts) == tok::l_brace)126    return {};127 128  if (StartLoc.isInvalid())129    return {};130 131  // Convert StartLoc to file location, if it's on the same macro expansion132  // level as the start of the statement. We also need file locations for133  // Lexer::getLocForEndOfToken working properly.134  StartLoc = Lexer::makeFileCharRange(135                 CharSourceRange::getCharRange(StartLoc, S->getBeginLoc()), SM,136                 LangOpts)137                 .getBegin();138  if (StartLoc.isInvalid())139    return {};140  StartLoc = Lexer::getLocForEndOfToken(StartLoc, 0, SM, LangOpts);141 142  // StartLoc points at the location of the opening brace to be inserted.143  SourceLocation EndLoc;144  StringRef ClosingInsertion;145  if (EndLocHint.isValid()) {146    EndLoc = EndLocHint;147    ClosingInsertion = "} ";148  } else {149    EndLoc = findEndLocation(*S, SM, LangOpts);150    ClosingInsertion = "\n}";151  }152 153  assert(StartLoc.isValid());154 155  // Change only if StartLoc and EndLoc are on the same macro expansion level.156  // This will also catch invalid EndLoc.157  // Example: LLVM_DEBUG( for(...) do_something() );158  // In this case fix-it cannot be provided as the semicolon which is not159  // visible here is part of the macro. Adding braces here would require adding160  // another semicolon.161  if (Lexer::makeFileCharRange(162          CharSourceRange::getTokenRange(SourceRange(163              SM.getSpellingLoc(StartLoc), SM.getSpellingLoc(EndLoc))),164          SM, LangOpts)165          .isInvalid())166    return {StartLoc};167  return {StartLoc, EndLoc, ClosingInsertion};168}169 170} // namespace clang::tidy::utils171