[clang-tools-extra] 0becc4d - fix readability-braces-around-statements Stmt type dependency

Aaron Ballman via cfe-commits cfe-commits at lists.llvm.org
Thu Mar 25 06:45:34 PDT 2021


Author: Alexander Lanin
Date: 2021-03-25T09:44:41-04:00
New Revision: 0becc4d721d0036e2e38d581bc487e27f78eb8a9

URL: https://github.com/llvm/llvm-project/commit/0becc4d721d0036e2e38d581bc487e27f78eb8a9
DIFF: https://github.com/llvm/llvm-project/commit/0becc4d721d0036e2e38d581bc487e27f78eb8a9.diff

LOG: fix readability-braces-around-statements Stmt type dependency

Replaces Token based approach to identify EndLoc of Stmt with AST traversal.
This also improves handling of macros.

Fixes Bugs 22785, 25970 and 35754.

Added: 
    

Modified: 
    clang-tools-extra/clang-tidy/readability/BracesAroundStatementsCheck.cpp
    clang-tools-extra/clang-tidy/utils/LexerUtils.cpp
    clang-tools-extra/clang-tidy/utils/LexerUtils.h
    clang-tools-extra/test/clang-tidy/checkers/readability-braces-around-statements.cpp

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clang-tidy/readability/BracesAroundStatementsCheck.cpp b/clang-tools-extra/clang-tidy/readability/BracesAroundStatementsCheck.cpp
index 1123238c186b..2c78258078ea 100644
--- a/clang-tools-extra/clang-tidy/readability/BracesAroundStatementsCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/BracesAroundStatementsCheck.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "BracesAroundStatementsCheck.h"
+#include "../utils/LexerUtils.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
 #include "clang/Lex/Lexer.h"
@@ -16,10 +17,9 @@ using namespace clang::ast_matchers;
 namespace clang {
 namespace tidy {
 namespace readability {
-namespace {
 
-tok::TokenKind getTokenKind(SourceLocation Loc, const SourceManager &SM,
-                            const ASTContext *Context) {
+static tok::TokenKind getTokenKind(SourceLocation Loc, const SourceManager &SM,
+                                   const ASTContext *Context) {
   Token Tok;
   SourceLocation Beginning =
       Lexer::GetBeginningOfToken(Loc, SM, Context->getLangOpts());
@@ -33,9 +33,9 @@ tok::TokenKind getTokenKind(SourceLocation Loc, const SourceManager &SM,
   return Tok.getKind();
 }
 
-SourceLocation forwardSkipWhitespaceAndComments(SourceLocation Loc,
-                                                const SourceManager &SM,
-                                                const ASTContext *Context) {
+static SourceLocation
+forwardSkipWhitespaceAndComments(SourceLocation Loc, const SourceManager &SM,
+                                 const ASTContext *Context) {
   assert(Loc.isValid());
   for (;;) {
     while (isWhitespace(*SM.getCharacterData(Loc)))
@@ -50,31 +50,15 @@ SourceLocation forwardSkipWhitespaceAndComments(SourceLocation Loc,
   }
 }
 
-SourceLocation findEndLocation(SourceLocation LastTokenLoc,
-                               const SourceManager &SM,
-                               const ASTContext *Context) {
+static SourceLocation findEndLocation(const Stmt &S, const SourceManager &SM,
+                                      const ASTContext *Context) {
   SourceLocation Loc =
-      Lexer::GetBeginningOfToken(LastTokenLoc, SM, Context->getLangOpts());
-  // Loc points to the beginning of the last (non-comment non-ws) token
-  // before end or ';'.
-  assert(Loc.isValid());
-  bool SkipEndWhitespaceAndComments = true;
-  tok::TokenKind TokKind = getTokenKind(Loc, SM, Context);
-  if (TokKind == tok::NUM_TOKENS || TokKind == tok::semi ||
-      TokKind == tok::r_brace) {
-    // If we are at ";" or "}", we found the last token. We could use as well
-    // `if (isa<NullStmt>(S))`, but it wouldn't work for nested statements.
-    SkipEndWhitespaceAndComments = false;
-  }
+      utils::lexer::getUnifiedEndLoc(S, SM, Context->getLangOpts());
+  if (!Loc.isValid())
+    return Loc;
 
-  Loc = Lexer::getLocForEndOfToken(Loc, 0, SM, Context->getLangOpts());
-  // Loc points past the last token before end or after ';'.
-  if (SkipEndWhitespaceAndComments) {
-    Loc = forwardSkipWhitespaceAndComments(Loc, SM, Context);
-    tok::TokenKind TokKind = getTokenKind(Loc, SM, Context);
-    if (TokKind == tok::semi)
-      Loc = Lexer::getLocForEndOfToken(Loc, 0, SM, Context->getLangOpts());
-  }
+  // Start searching right after S.
+  Loc = Loc.getLocWithOffset(1);
 
   for (;;) {
     assert(Loc.isValid());
@@ -109,8 +93,6 @@ SourceLocation findEndLocation(SourceLocation LastTokenLoc,
   return Loc;
 }
 
-} // namespace
-
 BracesAroundStatementsCheck::BracesAroundStatementsCheck(
     StringRef Name, ClangTidyContext *Context)
     : ClangTidyCheck(Name, Context),
@@ -224,13 +206,6 @@ bool BracesAroundStatementsCheck::checkStmt(
   const SourceManager &SM = *Result.SourceManager;
   const ASTContext *Context = Result.Context;
 
-  // Treat macros.
-  CharSourceRange FileRange = Lexer::makeFileCharRange(
-      CharSourceRange::getTokenRange(S->getSourceRange()), SM,
-      Context->getLangOpts());
-  if (FileRange.isInvalid())
-    return false;
-
   // Convert InitialLoc 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.
@@ -250,13 +225,12 @@ bool BracesAroundStatementsCheck::checkStmt(
     EndLoc = EndLocHint;
     ClosingInsertion = "} ";
   } else {
-    const auto FREnd = FileRange.getEnd().getLocWithOffset(-1);
-    EndLoc = findEndLocation(FREnd, SM, Context);
+    EndLoc = findEndLocation(*S, SM, Context);
     ClosingInsertion = "\n}";
   }
 
   assert(StartLoc.isValid());
-  assert(EndLoc.isValid());
+
   // Don't require braces for statements spanning less than certain number of
   // lines.
   if (ShortStatementLines && !ForceBracesStmts.erase(S)) {
@@ -267,6 +241,20 @@ bool BracesAroundStatementsCheck::checkStmt(
   }
 
   auto Diag = diag(StartLoc, "statement should be inside braces");
+
+  // 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, Context->getLangOpts())
+          .isInvalid())
+    return false;
+
   Diag << FixItHint::CreateInsertion(StartLoc, " {")
        << FixItHint::CreateInsertion(EndLoc, ClosingInsertion);
   return true;

diff  --git a/clang-tools-extra/clang-tidy/utils/LexerUtils.cpp b/clang-tools-extra/clang-tidy/utils/LexerUtils.cpp
index ca8b1b59f89e..88828f72e6bb 100644
--- a/clang-tools-extra/clang-tidy/utils/LexerUtils.cpp
+++ b/clang-tools-extra/clang-tidy/utils/LexerUtils.cpp
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "LexerUtils.h"
+#include "clang/AST/AST.h"
 #include "clang/Basic/SourceManager.h"
 
 namespace clang {
@@ -148,6 +149,70 @@ llvm::Optional<Token> getQualifyingToken(tok::TokenKind TK,
   return LastMatchAfterTemplate != None ? LastMatchAfterTemplate
                                         : LastMatchBeforeTemplate;
 }
+
+static bool breakAndReturnEnd(const Stmt &S) {
+  return isa<CompoundStmt, DeclStmt, NullStmt>(S);
+}
+
+static bool breakAndReturnEndPlus1Token(const Stmt &S) {
+  return isa<Expr, DoStmt, ReturnStmt, BreakStmt, ContinueStmt, GotoStmt, SEHLeaveStmt>(S);
+}
+
+// Given a Stmt which does not include it's semicolon this method returns the
+// SourceLocation of the semicolon.
+static SourceLocation getSemicolonAfterStmtEndLoc(const SourceLocation &EndLoc,
+                                                  const SourceManager &SM,
+                                                  const LangOptions &LangOpts) {
+
+  if (EndLoc.isMacroID()) {
+    // Assuming EndLoc points to a function call foo within macro F.
+    // This method is supposed to return location of the semicolon within
+    // those macro arguments:
+    //  F     (      foo()               ;   )
+    //  ^ EndLoc         ^ SpellingLoc   ^ next token of SpellingLoc
+    const SourceLocation SpellingLoc = SM.getSpellingLoc(EndLoc);
+    Optional<Token> NextTok =
+        findNextTokenSkippingComments(SpellingLoc, SM, LangOpts);
+
+    // Was the next token found successfully?
+    // All macro issues are simply resolved by ensuring it's a semicolon.
+    if (NextTok && NextTok->is(tok::TokenKind::semi)) {
+      // Ideally this would return `F` with spelling location `;` (NextTok)
+      // following the examle above. For now simply return NextTok location.
+      return NextTok->getLocation();
+    }
+
+    // Fallthrough to 'normal handling'.
+    //  F     (      foo()              ) ;
+    //  ^ EndLoc         ^ SpellingLoc  ) ^ next token of EndLoc
+  }
+
+  Optional<Token> NextTok = findNextTokenSkippingComments(EndLoc, SM, LangOpts);
+
+  // Testing for semicolon again avoids some issues with macros.
+  if (NextTok && NextTok->is(tok::TokenKind::semi))
+    return NextTok->getLocation();
+
+  return SourceLocation();
+}
+
+SourceLocation getUnifiedEndLoc(const Stmt &S, const SourceManager &SM,
+                                const LangOptions &LangOpts) {
+
+  const Stmt *LastChild = &S;
+  while (!LastChild->children().empty() && !breakAndReturnEnd(*LastChild) &&
+         !breakAndReturnEndPlus1Token(*LastChild)) {
+    for (const Stmt *Child : LastChild->children())
+      LastChild = Child;
+  }
+
+  if (!breakAndReturnEnd(*LastChild) &&
+      breakAndReturnEndPlus1Token(*LastChild))
+    return getSemicolonAfterStmtEndLoc(S.getEndLoc(), SM, LangOpts);
+
+  return S.getEndLoc();
+}
+
 } // namespace lexer
 } // namespace utils
 } // namespace tidy

diff  --git a/clang-tools-extra/clang-tidy/utils/LexerUtils.h b/clang-tools-extra/clang-tidy/utils/LexerUtils.h
index 8781b0571e7a..79ba16ded968 100644
--- a/clang-tools-extra/clang-tidy/utils/LexerUtils.h
+++ b/clang-tools-extra/clang-tidy/utils/LexerUtils.h
@@ -14,6 +14,9 @@
 #include "clang/Lex/Lexer.h"
 
 namespace clang {
+
+class Stmt;
+
 namespace tidy {
 namespace utils {
 namespace lexer {
@@ -104,6 +107,11 @@ llvm::Optional<Token> getQualifyingToken(tok::TokenKind TK,
                                          const ASTContext &Context,
                                          const SourceManager &SM);
 
+/// Stmt->getEndLoc does not always behave the same way depending on Token type.
+/// See implementation for exceptions.
+SourceLocation getUnifiedEndLoc(const Stmt &S, const SourceManager &SM,
+                                const LangOptions &LangOpts);
+
 } // namespace lexer
 } // namespace utils
 } // namespace tidy

diff  --git a/clang-tools-extra/test/clang-tidy/checkers/readability-braces-around-statements.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability-braces-around-statements.cpp
index 8dc5bf150fa3..494c2b780a7c 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/readability-braces-around-statements.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability-braces-around-statements.cpp
@@ -74,30 +74,41 @@ void test() {
     do_something("for");
   // CHECK-MESSAGES: :[[@LINE-2]]:11: warning: statement should be inside braces
   // CHECK-FIXES: for (;;) {
-  // CHECK-FIXES: }
+  // CHECK-FIXES-NEXT: do_something("for");
+  // CHECK-FIXES-NEXT: }
+
   for (;;) {
-    do_something("for");
+    do_something("for-ok");
   }
   for (;;)
     ;
   // CHECK-MESSAGES: :[[@LINE-2]]:11: warning: statement should be inside braces
   // CHECK-FIXES: for (;;) {
-  // CHECK-FIXES: }
+  // CHECK-FIXES-NEXT: ;
+  // CHECK-FIXES-NEXT: }
 
   int arr[4] = {1, 2, 3, 4};
   for (int a : arr)
     do_something("for-range");
   // CHECK-MESSAGES: :[[@LINE-2]]:20: warning: statement should be inside braces
   // CHECK-FIXES: for (int a : arr) {
-  // CHECK-FIXES: }
-  for (int a : arr) {
+  // CHECK-FIXES-NEXT: do_something("for-range");
+  // CHECK-FIXES-NEXT: }
+  for (int &assign : arr)
+    assign = 7;
+  // CHECK-MESSAGES: :[[@LINE-2]]:26: warning: statement should be inside braces
+  // CHECK-FIXES: for (int &assign : arr) {
+  // CHECK-FIXES-NEXT: assign = 7;
+  // CHECK-FIXES-NEXT: }
+  for (int ok : arr) {
     do_something("for-range");
   }
-  for (int a : arr)
+  for (int NullStmt : arr)
     ;
-  // CHECK-MESSAGES: :[[@LINE-2]]:20: warning: statement should be inside braces
-  // CHECK-FIXES: for (int a : arr) {
-  // CHECK-FIXES: }
+  // CHECK-MESSAGES: :[[@LINE-2]]:27: warning: statement should be inside braces
+  // CHECK-FIXES: for (int NullStmt : arr) {
+  // CHECK-FIXES-NEXT: ;
+  // CHECK-FIXES-NEXT: }
 
   while (cond("while1"))
     do_something("while");
@@ -143,14 +154,19 @@ void test() {
   // CHECK-FIXES-NEXT: }
 
   if (cond("ifif3"))
-    // comment
+    // comment1
     if (cond("ifif4")) {
-      // comment
-      /*comment*/; // comment
+      // comment2
+      /*comment3*/; // comment4
     }
   // CHECK-MESSAGES: :[[@LINE-6]]:21: warning: statement should be inside braces
   // CHECK-FIXES: if (cond("ifif3")) {
-  // CHECK-FIXES: }
+  // CHECK-FIXES-NEXT: // comment1
+  // CHECK-FIXES-NEXT: if (cond("ifif4")) {
+  // CHECK-FIXES-NEXT: // comment2
+  // CHECK-FIXES-NEXT: /*comment3*/; // comment4
+  // CHECK-FIXES-NEXT: }
+  // CHECK-FIXES-NEXT: }
 
   if (cond("ifif5"))
     ; /* multi-line
@@ -170,6 +186,161 @@ void test() {
   // CHECK-FIXES-NEXT: }
   // CHECK-FIXES-NEXT: }
   // CHECK-FIXES-NEXT: }
+
+  int S;
+  if (cond("assign with brackets"))
+    S = {5};
+  // CHECK-MESSAGES: :[[@LINE-2]]:36: warning: statement should be inside braces
+  // CHECK-FIXES: if (cond("assign with brackets")) {
+  // CHECK-FIXES-NEXT: S = {5};
+  // CHECK-FIXES-NEXT: }
+
+  if (cond("assign with brackets 2"))
+    S = {  5  } /* comment1 */ ; /* comment2 */
+  // CHECK-MESSAGES: :[[@LINE-2]]:38: warning: statement should be inside braces
+  // CHECK-FIXES: if (cond("assign with brackets 2")) {
+  // CHECK-FIXES-NEXT: S = {  5  } /* comment1 */ ; /* comment2 */
+  // CHECK-FIXES-NEXT: }
+
+  if (cond("return"))
+    return;
+  // CHECK-MESSAGES: :[[@LINE-2]]:22: warning: statement should be inside braces
+  // CHECK-FIXES: if (cond("return")) {
+  // CHECK-FIXES-NEXT: return;
+  // CHECK-FIXES-NEXT: }
+
+  while (cond("break and continue")) {
+    // CHECK-FIXES: while (cond("break and continue")) {
+    if (true)
+      break;
+    // CHECK-MESSAGES: :[[@LINE-2]]:14: warning: statement should be inside braces
+    // CHECK-FIXES: {{^}}    if (true) {{{$}}
+    // CHECK-FIXES-NEXT: {{^}}      break;{{$}}
+    // CHECK-FIXES-NEXT: {{^ *}}}{{$}}
+    if (false)
+      continue;
+    // CHECK-MESSAGES: :[[@LINE-2]]:15: warning: statement should be inside braces
+    // CHECK-FIXES: {{^}}    if (false) {{{$}}
+    // CHECK-FIXES-NEXT: {{^}}      continue;{{$}}
+    // CHECK-FIXES-NEXT: {{^ *}}}{{$}}
+  } //end
+  // CHECK-FIXES: } //end
+
+  if (cond("decl 1"))
+    int s;
+  else
+    int t;
+  // CHECK-MESSAGES: :[[@LINE-4]]:22: warning: statement should be inside braces
+  // CHECK-MESSAGES: :[[@LINE-3]]:7: warning: statement should be inside braces
+  // CHECK-FIXES: if (cond("decl 1")) {
+  // CHECK-FIXES-NEXT: int s;
+  // CHECK-FIXES-NEXT: } else {
+  // CHECK-FIXES-NEXT: int t;
+  // CHECK-FIXES-NEXT: }
+
+  if (cond("decl 2"))
+    int s = (5);
+  else
+    int t = (5);
+  // CHECK-MESSAGES: :[[@LINE-4]]:22: warning: statement should be inside braces
+  // CHECK-MESSAGES: :[[@LINE-3]]:7: warning: statement should be inside braces
+  // CHECK-FIXES: if (cond("decl 2")) {
+  // CHECK-FIXES-NEXT: int s = (5);
+  // CHECK-FIXES-NEXT: } else {
+  // CHECK-FIXES-NEXT: int t = (5);
+  // CHECK-FIXES-NEXT: }
+
+  if (cond("decl 3"))
+    int s = {6};
+  else
+    int t = {6};
+  // CHECK-MESSAGES: :[[@LINE-4]]:22: warning: statement should be inside braces
+  // CHECK-MESSAGES: :[[@LINE-3]]:7: warning: statement should be inside braces
+  // CHECK-FIXES: if (cond("decl 3")) {
+  // CHECK-FIXES-NEXT: int s = {6};
+  // CHECK-FIXES-NEXT: } else {
+  // CHECK-FIXES-NEXT: int t = {6};
+  // CHECK-FIXES-NEXT: }
+}
+
+void test_whitespace() {
+  while(cond("preserve empty lines"))
+    if(cond("using continue within if"))
+      continue;
+
+
+  test();
+
+  // CHECK-MESSAGES: :[[@LINE-7]]:{{[0-9]+}}: warning: statement should be inside braces
+  // CHECK-MESSAGES: :[[@LINE-7]]:{{[0-9]+}}: warning: statement should be inside braces
+  // CHECK-FIXES: {{^}}  while(cond("preserve empty lines")) {{{$}}
+  // CHECK-FIXES-NEXT: {{^}}    if(cond("using continue within if")) {{{$}}
+  // CHECK-FIXES-NEXT: {{^      continue;$}}
+  // The closing brace is added at beginning of line, clang-format can be
+  // applied afterwards.
+  // CHECK-FIXES-NEXT: {{^}$}}
+  // CHECK-FIXES-NEXT: {{^}$}}
+  // Following whitespace is assumed to not to belong to the else branch.
+  // However the check is not possible with CHECK-FIXES-NEXT.
+  // CHECK-FIXES: {{^}}  test();{{$}}
+
+  if (cond("preserve empty lines"))
+ 
+  
+    int s;
+   
+    
+  else
+ 
+  
+    int t;
+   
+    
+  test();
+
+  // CHECK-MESSAGES: :[[@LINE-14]]:{{[0-9]+}}: warning: statement should be inside braces
+  // CHECK-MESSAGES: :[[@LINE-9]]:{{[0-9]+}}: warning: statement should be inside braces
+  // CHECK-FIXES: {{^}}  if (cond("preserve empty lines")) {{{$}}
+  // CHECK-FIXES-NEXT: {{^ $}}
+  // CHECK-FIXES-NEXT: {{^  $}}
+  // CHECK-FIXES-NEXT: {{^    int s;$}}
+  // CHECK-FIXES-NEXT: {{^   $}}
+  // CHECK-FIXES-NEXT: {{^    $}}
+  // CHECK-FIXES-NEXT: {{^  } else {$}}
+  // CHECK-FIXES-NEXT: {{^ $}}
+  // CHECK-FIXES-NEXT: {{^  $}}
+  // CHECK-FIXES-NEXT: {{^    int t;$}}
+  // The closing brace is added at beginning of line, clang-format can be
+  // applied afterwards.
+  // CHECK-FIXES-NEXT: {{^}$}}
+  // Following whitespace is assumed to not to belong to the else branch.
+  // CHECK-FIXES-NEXT: {{^   $}}
+  // CHECK-FIXES-NEXT: {{^    $}}
+  // CHECK-FIXES-NEXT: {{^}}  test();{{$}}
+}
+
+int test_return_int() {
+  if (cond("return5"))
+    return 5;
+  // CHECK-MESSAGES: :[[@LINE-2]]:23: warning: statement should be inside braces
+  // CHECK-FIXES: if (cond("return5")) {
+  // CHECK-FIXES-NEXT: return 5;
+  // CHECK-FIXES-NEXT: }
+
+  if (cond("return{6}"))
+    return {6};
+  // CHECK-MESSAGES: :[[@LINE-2]]:25: warning: statement should be inside braces
+  // CHECK-FIXES: if (cond("return{6}")) {
+  // CHECK-FIXES-NEXT: return {6};
+  // CHECK-FIXES-NEXT: }
+
+  // From https://bugs.llvm.org/show_bug.cgi?id=25970
+  if (cond("25970")) return {25970};
+  return {!25970};
+  // CHECK-MESSAGES: :[[@LINE-2]]:21: warning: statement should be inside braces
+  // CHECK-FIXES: if (cond("25970")) { return {25970};
+  // CHECK-FIXES-NEXT: }
+  // CHECK-FIXES-NEXT: return {!25970};
 }
 
 void f(const char *p) {
@@ -203,4 +374,86 @@ int test_macros(bool b) {
   // CHECK-FIXES: {{^}}    for (;;) {{{$}}
   // CHECK-FIXES-NEXT: {{^      ;$}}
   // CHECK-FIXES-NEXT: {{^}$}}
+
+
+  #define WRAP(X) { X; }
+  // This is to ensure no other CHECK-FIXES matches the macro definition:
+  // CHECK-FIXES: WRAP
+
+  // Use-case: LLVM_DEBUG({ for(...) do_something(); });
+  WRAP({
+    for (;;)
+      do_something("for in wrapping macro 1");
+    });
+  // CHECK-MESSAGES: :[[@LINE-3]]:13: warning: statement should be inside braces
+  // CHECK-FIXES: for (;;) {
+  // CHECK-FIXES-NEXT: do_something("for in wrapping macro 1");
+  // CHECK-FIXES-NEXT: }
+
+  // Use-case: LLVM_DEBUG( for(...) do_something(); );
+  WRAP(
+    for (;;)
+      do_something("for in wrapping macro 2");
+    );
+  // CHECK-MESSAGES: :[[@LINE-3]]:13: warning: statement should be inside braces
+  // CHECK-FIXES: for (;;) {
+  // CHECK-FIXES-NEXT: do_something("for in wrapping macro 2");
+  // CHECK-FIXES-NEXT: }
+
+  // Use-case: LLVM_DEBUG( for(...) do_something() );
+  // This is not supported and this test ensure it's correctly not changed.
+  // We don't want to add the `}` into the Macro and there is no other way
+  // to add it except for introduction of a NullStmt.
+  WRAP(
+    for (;;)
+        do_something("for in wrapping macro 3")
+    );
+  // CHECK-MESSAGES: :[[@LINE-3]]:13: warning: statement should be inside braces
+  // CHECK-FIXES: WRAP(
+  // CHECK-FIXES-NEXT: for (;;)
+  // CHECK-FIXES-NEXT: do_something("for in wrapping macro 3")
+  // CHECK-FIXES-NEXT: );
+
+  // Taken from https://bugs.llvm.org/show_bug.cgi?id=22785
+  int i;
+  #define MACRO_1 i++
+  #define MACRO_2
+  if( i % 3) i--;
+  else if( i % 2) MACRO_1;
+  else MACRO_2;
+  // CHECK-MESSAGES: :[[@LINE-3]]:13: warning: statement should be inside braces
+  // CHECK-MESSAGES: :[[@LINE-3]]:18: warning: statement should be inside braces
+  // CHECK-MESSAGES: :[[@LINE-3]]:7: warning: statement should be inside braces
+  // CHECK-FIXES: if( i % 3) { i--;
+  // CHECK-FIXES-NEXT: } else if( i % 2) { MACRO_1;
+  // CHECK-FIXES-NEXT: } else { MACRO_2;
+  // CHECK-FIXES-NEXT: }
+
+  // Taken from https://bugs.llvm.org/show_bug.cgi?id=22785
+  #define M(x) x
+
+  if (b)
+    return 1;
+  else
+    return 2;
+  // CHECK-MESSAGES: :[[@LINE-4]]:9: warning: statement should be inside braces
+  // CHECK-MESSAGES: :[[@LINE-3]]:7: warning: statement should be inside braces
+  // CHECK-FIXES: if (b) {
+  // CHECK-FIXES-NEXT: return 1;
+  // CHECK-FIXES-NEXT: } else {
+  // CHECK-FIXES-NEXT: return 2;
+  // CHECK-FIXES-NEXT: }
+
+  if (b)
+    return 1;
+  else
+    M(return 2);
+  // CHECK-MESSAGES: :[[@LINE-4]]:9: warning: statement should be inside braces
+  // CHECK-MESSAGES: :[[@LINE-3]]:7: warning: statement should be inside braces
+  // CHECK-FIXES: if (b) {
+  // CHECK-FIXES-NEXT: return 1;
+  // CHECK-FIXES-NEXT: } else {
+  // CHECK-FIXES-NEXT: M(return 2);
+  // CHECK-FIXES-NEXT: }
+
 }


        


More information about the cfe-commits mailing list