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

source code of clang-tools-extra/clang-tidy/utils/BracesAroundStatement.cpp