r317343 - [refactor][extract] insert semicolons into extracted/inserted code

Alex Lorenz via cfe-commits cfe-commits at lists.llvm.org
Fri Nov 3 11:11:23 PDT 2017


Author: arphaman
Date: Fri Nov  3 11:11:22 2017
New Revision: 317343

URL: http://llvm.org/viewvc/llvm-project?rev=317343&view=rev
Log:
[refactor][extract] insert semicolons into extracted/inserted code
when needed

This commit implements the semicolon insertion logic into the extract
refactoring. The following rules are used:

- extracting expression: add terminating ';' to the extracted function.
- extracting statements that don't require terminating ';' (e.g. switch): add
  terminating ';' to the callee.
- extracting statements with ';':  move (if possible) the original ';' from the
  callee and add terminating ';'.
- otherwise, add ';' to both places.

Differential Revision: https://reviews.llvm.org/D39441

Added:
    cfe/trunk/lib/Tooling/Refactoring/Extract/
    cfe/trunk/lib/Tooling/Refactoring/Extract/SourceExtraction.cpp
    cfe/trunk/lib/Tooling/Refactoring/Extract/SourceExtraction.h
    cfe/trunk/test/Refactor/Extract/ExtractionSemicolonPolicy.cpp
    cfe/trunk/test/Refactor/Extract/ExtractionSemicolonPolicy.m
Modified:
    cfe/trunk/include/clang/Lex/Lexer.h
    cfe/trunk/lib/Lex/Lexer.cpp
    cfe/trunk/lib/Tooling/Refactoring/CMakeLists.txt
    cfe/trunk/lib/Tooling/Refactoring/Extract.cpp
    cfe/trunk/test/Refactor/Extract/ExtractExprIntoFunction.cpp

Modified: cfe/trunk/include/clang/Lex/Lexer.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Lex/Lexer.h?rev=317343&r1=317342&r2=317343&view=diff
==============================================================================
--- cfe/trunk/include/clang/Lex/Lexer.h (original)
+++ cfe/trunk/include/clang/Lex/Lexer.h Fri Nov  3 11:11:22 2017
@@ -466,6 +466,13 @@ public:
                                         const LangOptions &LangOpts,
                                         unsigned MaxLines = 0);
 
+  /// Finds the token that comes right after the given location.
+  ///
+  /// Returns the next token, or none if the location is inside a macro.
+  static Optional<Token> findNextToken(SourceLocation Loc,
+                                       const SourceManager &SM,
+                                       const LangOptions &LangOpts);
+
   /// \brief Checks that the given token is the first token that occurs after
   /// the given location (this excludes comments and whitespace). Returns the
   /// location immediately after the specified token. If the token is not found

Modified: cfe/trunk/lib/Lex/Lexer.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Lex/Lexer.cpp?rev=317343&r1=317342&r2=317343&view=diff
==============================================================================
--- cfe/trunk/lib/Lex/Lexer.cpp (original)
+++ cfe/trunk/lib/Lex/Lexer.cpp Fri Nov  3 11:11:22 2017
@@ -1212,18 +1212,12 @@ const char *Lexer::SkipEscapedNewLines(c
   }
 }
 
-/// \brief Checks that the given token is the first token that occurs after the
-/// given location (this excludes comments and whitespace). Returns the location
-/// immediately after the specified token. If the token is not found or the
-/// location is inside a macro, the returned source location will be invalid.
-SourceLocation Lexer::findLocationAfterToken(SourceLocation Loc,
-                                        tok::TokenKind TKind,
-                                        const SourceManager &SM,
-                                        const LangOptions &LangOpts,
-                                        bool SkipTrailingWhitespaceAndNewLine) {
+Optional<Token> Lexer::findNextToken(SourceLocation Loc,
+                                     const SourceManager &SM,
+                                     const LangOptions &LangOpts) {
   if (Loc.isMacroID()) {
     if (!Lexer::isAtEndOfMacroExpansion(Loc, SM, LangOpts, &Loc))
-      return SourceLocation();
+      return None;
   }
   Loc = Lexer::getLocForEndOfToken(Loc, 0, SM, LangOpts);
 
@@ -1234,7 +1228,7 @@ SourceLocation Lexer::findLocationAfterT
   bool InvalidTemp = false;
   StringRef File = SM.getBufferData(LocInfo.first, &InvalidTemp);
   if (InvalidTemp)
-    return SourceLocation();
+    return None;
 
   const char *TokenBegin = File.data() + LocInfo.second;
 
@@ -1244,15 +1238,25 @@ SourceLocation Lexer::findLocationAfterT
   // Find the token.
   Token Tok;
   lexer.LexFromRawLexer(Tok);
-  if (Tok.isNot(TKind))
+  return Tok;
+}
+
+/// \brief Checks that the given token is the first token that occurs after the
+/// given location (this excludes comments and whitespace). Returns the location
+/// immediately after the specified token. If the token is not found or the
+/// location is inside a macro, the returned source location will be invalid.
+SourceLocation Lexer::findLocationAfterToken(
+    SourceLocation Loc, tok::TokenKind TKind, const SourceManager &SM,
+    const LangOptions &LangOpts, bool SkipTrailingWhitespaceAndNewLine) {
+  Optional<Token> Tok = findNextToken(Loc, SM, LangOpts);
+  if (!Tok || Tok->isNot(TKind))
     return SourceLocation();
-  SourceLocation TokenLoc = Tok.getLocation();
+  SourceLocation TokenLoc = Tok->getLocation();
 
   // Calculate how much whitespace needs to be skipped if any.
   unsigned NumWhitespaceChars = 0;
   if (SkipTrailingWhitespaceAndNewLine) {
-    const char *TokenEnd = SM.getCharacterData(TokenLoc) +
-                           Tok.getLength();
+    const char *TokenEnd = SM.getCharacterData(TokenLoc) + Tok->getLength();
     unsigned char C = *TokenEnd;
     while (isHorizontalWhitespace(C)) {
       C = *(++TokenEnd);
@@ -1269,7 +1273,7 @@ SourceLocation Lexer::findLocationAfterT
     }
   }
 
-  return TokenLoc.getLocWithOffset(Tok.getLength() + NumWhitespaceChars);
+  return TokenLoc.getLocWithOffset(Tok->getLength() + NumWhitespaceChars);
 }
 
 /// getCharAndSizeSlow - Peek a single 'character' from the specified buffer,

Modified: cfe/trunk/lib/Tooling/Refactoring/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Tooling/Refactoring/CMakeLists.txt?rev=317343&r1=317342&r2=317343&view=diff
==============================================================================
--- cfe/trunk/lib/Tooling/Refactoring/CMakeLists.txt (original)
+++ cfe/trunk/lib/Tooling/Refactoring/CMakeLists.txt Fri Nov  3 11:11:22 2017
@@ -4,7 +4,8 @@ add_clang_library(clangToolingRefactor
   ASTSelection.cpp
   ASTSelectionRequirements.cpp
   AtomicChange.cpp
-  Extract.cpp
+  Extract/Extract.cpp
+  Extract/SourceExtraction.cpp
   RefactoringActions.cpp
   Rename/RenamingAction.cpp
   Rename/SymbolOccurrences.cpp

Modified: cfe/trunk/lib/Tooling/Refactoring/Extract.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Tooling/Refactoring/Extract.cpp?rev=317343&r1=317342&r2=317343&view=diff
==============================================================================
--- cfe/trunk/lib/Tooling/Refactoring/Extract.cpp (original)
+++ cfe/trunk/lib/Tooling/Refactoring/Extract.cpp Fri Nov  3 11:11:22 2017
@@ -14,6 +14,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/Tooling/Refactoring/Extract/Extract.h"
+#include "SourceExtraction.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/Expr.h"
@@ -145,6 +146,8 @@ ExtractFunction::createSourceReplacement
   PP.SuppressLifetimeQualifiers = true;
   PP.SuppressUnwrittenScope = true;
 
+  ExtractionSemicolonPolicy Semicolons = ExtractionSemicolonPolicy::compute(
+      Code[Code.size() - 1], ExtractedRange, SM, LangOpts);
   AtomicChange Change(SM, ExtractedDeclLocation);
   // Create the replacement for the extracted declaration.
   {
@@ -162,8 +165,8 @@ ExtractFunction::createSourceReplacement
     if (IsExpr && !ReturnType->isVoidType())
       OS << "return ";
     OS << ExtractedCodeRewriter.getRewrittenText(ExtractedRange);
-    // FIXME: Compute the correct semicolon policy.
-    OS << ';';
+    if (Semicolons.isNeededInExtractedFunction())
+      OS << ';';
     OS << "\n}\n\n";
     auto Err = Change.insert(SM, ExtractedDeclLocation, OS.str());
     if (Err)
@@ -178,7 +181,8 @@ ExtractFunction::createSourceReplacement
     OS << DeclName << '(';
     // FIXME: Forward arguments.
     OS << ')';
-    // FIXME: Add semicolon if needed.
+    if (Semicolons.isNeededInOriginalFunction())
+      OS << ';';
 
     auto Err = Change.replace(
         SM, CharSourceRange::getTokenRange(ExtractedRange), OS.str());

Added: cfe/trunk/lib/Tooling/Refactoring/Extract/SourceExtraction.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Tooling/Refactoring/Extract/SourceExtraction.cpp?rev=317343&view=auto
==============================================================================
--- cfe/trunk/lib/Tooling/Refactoring/Extract/SourceExtraction.cpp (added)
+++ cfe/trunk/lib/Tooling/Refactoring/Extract/SourceExtraction.cpp Fri Nov  3 11:11:22 2017
@@ -0,0 +1,112 @@
+//===--- SourceExtraction.cpp - Clang refactoring library -----------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "SourceExtraction.h"
+#include "clang/AST/Stmt.h"
+#include "clang/AST/StmtCXX.h"
+#include "clang/AST/StmtObjC.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang;
+
+namespace {
+
+/// Returns true if the token at the given location is a semicolon.
+bool isSemicolonAtLocation(SourceLocation TokenLoc, const SourceManager &SM,
+                           const LangOptions &LangOpts) {
+  return Lexer::getSourceText(
+             CharSourceRange::getTokenRange(TokenLoc, TokenLoc), SM,
+             LangOpts) == ";";
+}
+
+/// Returns true if there should be a semicolon after the given statement.
+bool isSemicolonRequiredAfter(const Stmt *S) {
+  if (isa<CompoundStmt>(S))
+    return false;
+  if (const auto *If = dyn_cast<IfStmt>(S))
+    return isSemicolonRequiredAfter(If->getElse() ? If->getElse()
+                                                  : If->getThen());
+  if (const auto *While = dyn_cast<WhileStmt>(S))
+    return isSemicolonRequiredAfter(While->getBody());
+  if (const auto *For = dyn_cast<ForStmt>(S))
+    return isSemicolonRequiredAfter(For->getBody());
+  if (const auto *CXXFor = dyn_cast<CXXForRangeStmt>(S))
+    return isSemicolonRequiredAfter(CXXFor->getBody());
+  if (const auto *ObjCFor = dyn_cast<ObjCForCollectionStmt>(S))
+    return isSemicolonRequiredAfter(ObjCFor->getBody());
+  switch (S->getStmtClass()) {
+  case Stmt::SwitchStmtClass:
+  case Stmt::CXXTryStmtClass:
+  case Stmt::ObjCAtSynchronizedStmtClass:
+  case Stmt::ObjCAutoreleasePoolStmtClass:
+  case Stmt::ObjCAtTryStmtClass:
+    return false;
+  default:
+    return true;
+  }
+}
+
+/// Returns true if the two source locations are on the same line.
+bool areOnSameLine(SourceLocation Loc1, SourceLocation Loc2,
+                   const SourceManager &SM) {
+  return !Loc1.isMacroID() && !Loc2.isMacroID() &&
+         SM.getSpellingLineNumber(Loc1) == SM.getSpellingLineNumber(Loc2);
+}
+
+} // end anonymous namespace
+
+namespace clang {
+namespace tooling {
+
+ExtractionSemicolonPolicy
+ExtractionSemicolonPolicy::compute(const Stmt *S, SourceRange &ExtractedRange,
+                                   const SourceManager &SM,
+                                   const LangOptions &LangOpts) {
+  auto neededInExtractedFunction = []() {
+    return ExtractionSemicolonPolicy(true, false);
+  };
+  auto neededInOriginalFunction = []() {
+    return ExtractionSemicolonPolicy(false, true);
+  };
+
+  /// The extracted expression should be terminated with a ';'. The call to
+  /// the extracted function will replace this expression, so it won't need
+  /// a terminating ';'.
+  if (isa<Expr>(S))
+    return neededInExtractedFunction();
+
+  /// Some statements don't need to be terminated with ';'. The call to the
+  /// extracted function will be a standalone statement, so it should be
+  /// terminated with a ';'.
+  bool NeedsSemi = isSemicolonRequiredAfter(S);
+  if (!NeedsSemi)
+    return neededInOriginalFunction();
+
+  /// Some statements might end at ';'. The extraction will move that ';', so
+  /// the call to the extracted function should be terminated with a ';'.
+  SourceLocation End = ExtractedRange.getEnd();
+  if (isSemicolonAtLocation(End, SM, LangOpts))
+    return neededInOriginalFunction();
+
+  /// Other statements should generally have a trailing ';'. We can try to find
+  /// it and move it together it with the extracted code.
+  Optional<Token> NextToken = Lexer::findNextToken(End, SM, LangOpts);
+  if (NextToken && NextToken->is(tok::semi) &&
+      areOnSameLine(NextToken->getLocation(), End, SM)) {
+    ExtractedRange.setEnd(NextToken->getLocation());
+    return neededInOriginalFunction();
+  }
+
+  /// Otherwise insert semicolons in both places.
+  return ExtractionSemicolonPolicy(true, true);
+}
+
+} // end namespace tooling
+} // end namespace clang

Added: cfe/trunk/lib/Tooling/Refactoring/Extract/SourceExtraction.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Tooling/Refactoring/Extract/SourceExtraction.h?rev=317343&view=auto
==============================================================================
--- cfe/trunk/lib/Tooling/Refactoring/Extract/SourceExtraction.h (added)
+++ cfe/trunk/lib/Tooling/Refactoring/Extract/SourceExtraction.h Fri Nov  3 11:11:22 2017
@@ -0,0 +1,52 @@
+//===--- SourceExtraction.cpp - Clang refactoring library -----------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_TOOLING_REFACTORING_EXTRACT_SOURCE_EXTRACTION_H
+#define LLVM_CLANG_LIB_TOOLING_REFACTORING_EXTRACT_SOURCE_EXTRACTION_H
+
+#include "clang/Basic/LLVM.h"
+
+namespace clang {
+
+class LangOptions;
+class SourceManager;
+class SourceRange;
+class Stmt;
+
+namespace tooling {
+
+/// Determines which semicolons should be inserted during extraction.
+class ExtractionSemicolonPolicy {
+public:
+  bool isNeededInExtractedFunction() const {
+    return IsNeededInExtractedFunction;
+  }
+
+  bool isNeededInOriginalFunction() const { return IsNeededInOriginalFunction; }
+
+  /// Returns the semicolon insertion policy that is needed for extraction of
+  /// the given statement from the given source range.
+  static ExtractionSemicolonPolicy compute(const Stmt *S,
+                                           SourceRange &ExtractedRange,
+                                           const SourceManager &SM,
+                                           const LangOptions &LangOpts);
+
+private:
+  ExtractionSemicolonPolicy(bool IsNeededInExtractedFunction,
+                            bool IsNeededInOriginalFunction)
+      : IsNeededInExtractedFunction(IsNeededInExtractedFunction),
+        IsNeededInOriginalFunction(IsNeededInOriginalFunction) {}
+  bool IsNeededInExtractedFunction;
+  bool IsNeededInOriginalFunction;
+};
+
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_LIB_TOOLING_REFACTORING_EXTRACT_SOURCE_EXTRACTION_H

Modified: cfe/trunk/test/Refactor/Extract/ExtractExprIntoFunction.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Refactor/Extract/ExtractExprIntoFunction.cpp?rev=317343&r1=317342&r2=317343&view=diff
==============================================================================
--- cfe/trunk/test/Refactor/Extract/ExtractExprIntoFunction.cpp (original)
+++ cfe/trunk/test/Refactor/Extract/ExtractExprIntoFunction.cpp Fri Nov  3 11:11:22 2017
@@ -20,10 +20,10 @@ void simpleExtractStmtNoCaptures() {
 // CHECK: 1 'astatement' results:
 // CHECK:      static void extracted() {
 // CHECK-NEXT: int a = 1;
-// CHECK-NEXT: int b = 2;;{{$}}
+// CHECK-NEXT: int b = 2;{{$}}
 // CHECK-NEXT: }{{[[:space:]].*}}
 // CHECK-NEXT: void simpleExtractStmtNoCaptures() {
-// CHECK-NEXT:   /*range astatement=->+1:13*/extracted(){{$}}
+// CHECK-NEXT:   /*range astatement=->+1:13*/extracted();{{$}}
 // CHECK-NEXT: }
 
 

Added: cfe/trunk/test/Refactor/Extract/ExtractionSemicolonPolicy.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Refactor/Extract/ExtractionSemicolonPolicy.cpp?rev=317343&view=auto
==============================================================================
--- cfe/trunk/test/Refactor/Extract/ExtractionSemicolonPolicy.cpp (added)
+++ cfe/trunk/test/Refactor/Extract/ExtractionSemicolonPolicy.cpp Fri Nov  3 11:11:22 2017
@@ -0,0 +1,192 @@
+// RUN: clang-refactor extract -selection=test:%s %s -- -std=c++11 2>&1 | grep -v CHECK | FileCheck %s
+
+struct Rectangle { int width, height; };
+
+void extractStatement(const Rectangle &r) {
+  /*range adeclstmt=->+0:59*/int area = r.width * r.height;
+}
+// CHECK: 1 'adeclstmt' results:
+// CHECK:      static void extracted() {
+// CHECK-NEXT: int area = r.width * r.height;{{$}}
+// CHECK-NEXT: }{{[[:space:]].*}}
+// CHECK-NEXT: void extractStatement(const Rectangle &r) {
+// CHECK-NEXT:   /*range adeclstmt=->+0:59*/extracted();{{$}}
+// CHECK-NEXT: }
+
+void extractStatementNoSemiIf(const Rectangle &r) {
+  /*range bextractif=->+2:4*/if (r.width) {
+    int x = r.height;
+  }
+}
+// CHECK: 1 'bextractif' results:
+// CHECK:      static void extracted() {
+// CHECK-NEXT: if (r.width) {
+// CHECK-NEXT: int x = r.height;
+// CHECK-NEXT: }{{$}}
+// CHECK-NEXT: }{{[[:space:]].*}}
+// CHECK-NEXT: void extractStatementNoSemiIf(const Rectangle &r) {
+// CHECK-NEXT:   /*range bextractif=->+2:4*/extracted();{{$}}
+// CHECK-NEXT: }
+
+void extractStatementDontExtraneousSemi(const Rectangle &r) {
+  /*range cextractif=->+2:4*/if (r.width) {
+    int x = r.height;
+  } ;
+} //^ This semicolon shouldn't be extracted.
+// CHECK: 1 'cextractif' results:
+// CHECK:      static void extracted() {
+// CHECK-NEXT: if (r.width) {
+// CHECK-NEXT: int x = r.height;
+// CHECK-NEXT: }{{$}}
+// CHECK-NEXT: }{{[[:space:]].*}}
+// CHECK-NEXT: void extractStatementDontExtraneousSemi(const Rectangle &r) {
+// CHECK-NEXT: extracted(); ;{{$}}
+// CHECK-NEXT: }
+
+void extractStatementNotSemiSwitch() {
+  /*range dextract=->+5:4*/switch (2) {
+  case 1:
+    break;
+  case 2:
+    break;
+  }
+}
+// CHECK: 1 'dextract' results:
+// CHECK:      static void extracted() {
+// CHECK-NEXT: switch (2) {
+// CHECK-NEXT: case 1:
+// CHECK-NEXT:   break;
+// CHECK-NEXT: case 2:
+// CHECK-NEXT:   break;
+// CHECK-NEXT: }{{$}}
+// CHECK-NEXT: }{{[[:space:]].*}}
+// CHECK-NEXT: void extractStatementNotSemiSwitch() {
+// CHECK-NEXT: extracted();{{$}}
+// CHECK-NEXT: }
+
+void extractStatementNotSemiWhile() {
+  /*range eextract=->+2:4*/while (true) {
+    int x = 0;
+  }
+}
+// CHECK: 1 'eextract' results:
+// CHECK:      static void extracted() {
+// CHECK-NEXT: while (true) {
+// CHECK-NEXT: int x = 0;
+// CHECK-NEXT: }{{$}}
+// CHECK-NEXT: }{{[[:space:]].*}}
+// CHECK-NEXT: void extractStatementNotSemiWhile() {
+// CHECK-NEXT: extracted();{{$}}
+// CHECK-NEXT: }
+
+void extractStatementNotSemiFor() {
+  /*range fextract=->+1:4*/for (int i = 0; i < 10; ++i) {
+  }
+}
+// CHECK: 1 'fextract' results:
+// CHECK:      static void extracted() {
+// CHECK-NEXT: for (int i = 0; i < 10; ++i) {
+// CHECK-NEXT: }{{$}}
+// CHECK-NEXT: }{{[[:space:]].*}}
+// CHECK-NEXT: void extractStatementNotSemiFor() {
+// CHECK-NEXT: extracted();{{$}}
+// CHECK-NEXT: }
+
+struct XS {
+  int *begin() { return 0; }
+  int *end() { return 0; }
+};
+
+void extractStatementNotSemiRangedFor(XS xs) {
+  /*range gextract=->+1:4*/for (int i : xs) {
+  }
+}
+// CHECK: 1 'gextract' results:
+// CHECK:      static void extracted() {
+// CHECK-NEXT: for (int i : xs) {
+// CHECK-NEXT: }{{$}}
+// CHECK-NEXT: }{{[[:space:]].*}}
+// CHECK-NEXT: void extractStatementNotSemiRangedFor(XS xs) {
+// CHECK-NEXT: extracted();{{$}}
+// CHECK-NEXT: }
+
+void extractStatementNotSemiRangedTryCatch() {
+  /*range hextract=->+3:4*/try { int x = 0; }
+  catch (const int &i) {
+    int y = i;
+  }
+}
+// CHECK: 1 'hextract' results:
+// CHECK:      static void extracted() {
+// CHECK-NEXT: try { int x = 0; }
+// CHECK-NEXT: catch (const int &i) {
+// CHECK-NEXT:   int y = i;
+// CHECK-NEXT: }{{$}}
+// CHECK-NEXT: }{{[[:space:]].*}}
+// CHECK-NEXT: void extractStatementNotSemiRangedTryCatch() {
+// CHECK-NEXT: extracted();{{$}}
+// CHECK-NEXT: }
+
+void extractCantFindSemicolon() {
+  /*range iextract=->+1:17*/do {
+  } while (true)
+  // Add a semicolon in both the extracted and original function as we don't
+  // want to extract the semicolon below.
+  ;
+}
+// CHECK: 1 'iextract' results:
+// CHECK:      static void extracted() {
+// CHECK-NEXT: do {
+// CHECK-NEXT: } while (true);{{$}}
+// CHECK-NEXT: }{{[[:space:]].*}}
+// CHECK-NEXT: void extractCantFindSemicolon() {
+// CHECK-NEXT: extracted();{{$}}
+// CHECK-NEXT: //
+// CHECK-NEXT: //
+// CHECK-NEXT: ;
+// CHECK-NEXT: }
+
+void extractFindSemicolon() {
+  /*range jextract=->+1:17*/do {
+  } while (true) /*grab*/ ;
+}
+// CHECK: 1 'jextract' results:
+// CHECK:      static void extracted() {
+// CHECK-NEXT: do {
+// CHECK-NEXT: } while (true) /*grab*/ ;{{$}}
+// CHECK-NEXT: }{{[[:space:]].*}}
+// CHECK-NEXT: void extractFindSemicolon() {
+// CHECK-NEXT: extracted();{{$}}
+// CHECK-NEXT: }
+
+void call();
+
+void careForNonCompoundSemicolons1() {
+  /*range kextract=->+1:11*/if (true)
+    call();
+}
+// CHECK: 1 'kextract' results:
+// CHECK:      static void extracted() {
+// CHECK-NEXT: if (true)
+// CHECK-NEXT: call();{{$}}
+// CHECK-NEXT: }{{[[:space:]].*}}
+// CHECK-NEXT: void careForNonCompoundSemicolons1() {
+// CHECK-NEXT: extracted();{{$}}
+// CHECK-NEXT: }
+
+void careForNonCompoundSemicolons2() {
+  /*range lextract=->+3:1*/for (int i = 0; i < 10; ++i)
+    while (i != 0)
+      ;
+  // end right here111!
+}
+// CHECK: 1 'lextract' results:
+// CHECK:      static void extracted() {
+// CHECK-NEXT: for (int i = 0; i < 10; ++i)
+// CHECK-NEXT: while (i != 0)
+// CHECK-NEXT:   ;{{$}}
+// CHECK-NEXT: }{{[[:space:]].*}}
+// CHECK-NEXT: void careForNonCompoundSemicolons2() {
+// CHECK-NEXT: extracted();{{$}}
+// CHECK-NEXT: //
+// CHECK-NEXT: }

Added: cfe/trunk/test/Refactor/Extract/ExtractionSemicolonPolicy.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Refactor/Extract/ExtractionSemicolonPolicy.m?rev=317343&view=auto
==============================================================================
--- cfe/trunk/test/Refactor/Extract/ExtractionSemicolonPolicy.m (added)
+++ cfe/trunk/test/Refactor/Extract/ExtractionSemicolonPolicy.m Fri Nov  3 11:11:22 2017
@@ -0,0 +1,56 @@
+// RUN: clang-refactor extract -selection=test:%s %s -- 2>&1 | grep -v CHECK | FileCheck %s
+
+ at interface NSArray
++ (id)arrayWithObjects:(const id [])objects count:(unsigned long)cnt;
+ at end
+
+void extractStatementNoSemiObjCFor(NSArray *array) {
+  /*range astmt=->+2:4*/for (id i in array) {
+    int x = 0;
+  }
+}
+// CHECK: 1 'astmt' results:
+// CHECK:      static void extracted() {
+// CHECK-NEXT: for (id i in array) {
+// CHECK-NEXT: int x = 0;
+// CHECK-NEXT: }{{$}}
+// CHECK-NEXT: }{{[[:space:]].*}}
+
+void extractStatementNoSemiSync() {
+  id lock;
+  /*range bstmt=->+2:4*/@synchronized(lock) {
+    int x = 0;
+  }
+}
+// CHECK: 1 'bstmt' results:
+// CHECK:      static void extracted() {
+// CHECK-NEXT: @synchronized(lock) {
+// CHECK-NEXT: int x = 0;
+// CHECK-NEXT: }{{$}}
+// CHECK-NEXT: }{{[[:space:]].*}}
+
+void extractStatementNoSemiAutorel() {
+  /*range cstmt=->+2:4*/@autoreleasepool {
+    int x = 0;
+  }
+}
+// CHECK: 1 'cstmt' results:
+// CHECK:      static void extracted() {
+// CHECK-NEXT: @autoreleasepool {
+// CHECK-NEXT: int x = 0;
+// CHECK-NEXT: }{{$}}
+// CHECK-NEXT: }{{[[:space:]].*}}
+
+void extractStatementNoSemiTryFinalllllly() {
+  /*range dstmt=->+3:4*/@try {
+    int x = 0;
+  } @finally {
+  }
+}
+// CHECK: 1 'dstmt' results:
+// CHECK:      static void extracted() {
+// CHECK-NEXT: @try {
+// CHECK-NEXT: int x = 0;
+// CHECK-NEXT: } @finally {
+// CHECK-NEXT: }{{$}}
+// CHECK-NEXT: }{{[[:space:]].*}}




More information about the cfe-commits mailing list