//===--- BracesAroundStatement.cpp - clang-tidy -------- ------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// /// \file /// This file provides utilities to put braces around a statement. /// //===----------------------------------------------------------------------===// #include "BracesAroundStatement.h" #include "../utils/LexerUtils.h" #include "LexerUtils.h" #include "clang/AST/ASTContext.h" #include "clang/Basic/CharInfo.h" #include "clang/Basic/LangOptions.h" #include "clang/Lex/Lexer.h" namespace clang::tidy::utils { BraceInsertionHints::operator bool() const { return DiagnosticPos.isValid(); } bool BraceInsertionHints::offersFixIts() const { return OpeningBracePos.isValid() && ClosingBracePos.isValid(); } unsigned BraceInsertionHints::resultingCompoundLineExtent( const SourceManager &SourceMgr) const { return SourceMgr.getSpellingLineNumber(ClosingBracePos) - SourceMgr.getSpellingLineNumber(OpeningBracePos); } FixItHint BraceInsertionHints::openingBraceFixIt() const { return OpeningBracePos.isValid() ? FixItHint::CreateInsertion(OpeningBracePos, " {") : FixItHint(); } FixItHint BraceInsertionHints::closingBraceFixIt() const { return ClosingBracePos.isValid() ? FixItHint::CreateInsertion(ClosingBracePos, ClosingBrace) : FixItHint(); } static tok::TokenKind getTokenKind(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts) { Token Tok; SourceLocation Beginning = Lexer::GetBeginningOfToken(Loc, SM, LangOpts); const bool Invalid = Lexer::getRawToken(Beginning, Tok, SM, LangOpts); assert(!Invalid && "Expected a valid token."); if (Invalid) return tok::NUM_TOKENS; return Tok.getKind(); } static SourceLocation findEndLocation(const Stmt &S, const SourceManager &SM, const LangOptions &LangOpts) { SourceLocation Loc = lexer::getUnifiedEndLoc(S, SM, LangOpts); if (!Loc.isValid()) return Loc; // Start searching right after S. Loc = Loc.getLocWithOffset(1); for (;;) { assert(Loc.isValid()); while (isHorizontalWhitespace(*SM.getCharacterData(Loc))) { Loc = Loc.getLocWithOffset(1); } if (isVerticalWhitespace(*SM.getCharacterData(Loc))) { // EOL, insert brace before. break; } tok::TokenKind TokKind = getTokenKind(Loc, SM, LangOpts); if (TokKind != tok::comment) { // Non-comment token, insert brace before. break; } SourceLocation TokEndLoc = Lexer::getLocForEndOfToken(Loc, 0, SM, LangOpts); SourceRange TokRange(Loc, TokEndLoc); StringRef Comment = Lexer::getSourceText( CharSourceRange::getTokenRange(TokRange), SM, LangOpts); if (Comment.starts_with("/*") && Comment.contains('\n')) { // Multi-line block comment, insert brace before. break; } // else: Trailing comment, insert brace after the newline. // Fast-forward current token. Loc = TokEndLoc; } return Loc; } BraceInsertionHints getBraceInsertionsHints(const Stmt *const S, const LangOptions &LangOpts, const SourceManager &SM, SourceLocation StartLoc, SourceLocation EndLocHint) { // 1) If there's a corresponding "else" or "while", the check inserts "} " // right before that token. // 2) If there's a multi-line block comment starting on the same line after // the location we're inserting the closing brace at, or there's a non-comment // token, the check inserts "\n}" right before that token. // 3) Otherwise the check finds the end of line (possibly after some block or // line comments) and inserts "\n}" right before that EOL. if (!S || isa(S)) { // Already inside braces. return {}; } // When TreeTransform, Stmt in constexpr IfStmt will be transform to NullStmt. // This NullStmt can be detected according to beginning token. const SourceLocation StmtBeginLoc = S->getBeginLoc(); if (isa(S) && StmtBeginLoc.isValid() && getTokenKind(StmtBeginLoc, SM, LangOpts) == tok::l_brace) return {}; if (StartLoc.isInvalid()) return {}; // Convert StartLoc to file location, if it's on the same macro expansion // level as the start of the statement. We also need file locations for // Lexer::getLocForEndOfToken working properly. StartLoc = Lexer::makeFileCharRange( CharSourceRange::getCharRange(StartLoc, S->getBeginLoc()), SM, LangOpts) .getBegin(); if (StartLoc.isInvalid()) return {}; StartLoc = Lexer::getLocForEndOfToken(StartLoc, 0, SM, LangOpts); // StartLoc points at the location of the opening brace to be inserted. SourceLocation EndLoc; std::string ClosingInsertion; if (EndLocHint.isValid()) { EndLoc = EndLocHint; ClosingInsertion = "} "; } else { EndLoc = findEndLocation(*S, SM, LangOpts); ClosingInsertion = "\n}"; } assert(StartLoc.isValid()); // Change only if StartLoc and EndLoc are on the same macro expansion level. // This will also catch invalid EndLoc. // Example: LLVM_DEBUG( for(...) do_something() ); // In this case fix-it cannot be provided as the semicolon which is not // visible here is part of the macro. Adding braces here would require adding // another semicolon. if (Lexer::makeFileCharRange( CharSourceRange::getTokenRange(SourceRange( SM.getSpellingLoc(StartLoc), SM.getSpellingLoc(EndLoc))), SM, LangOpts) .isInvalid()) return {StartLoc}; return {StartLoc, EndLoc, ClosingInsertion}; } } // namespace clang::tidy::utils