From 84f2271acd818e50c598fef7ce9586e7ee0ac553 Mon Sep 17 00:00:00 2001 From: Yitzhak Mandelbaum Date: Fri, 5 Apr 2019 14:05:03 +0000 Subject: [PATCH] [LibTooling] Add "SourceCode" library for functions relating to source-code manipulation. Summary: Introduces a utility library in Refactoring/ to collect routines related to source-code manipulation. In this change, we move "extended-range" functions from the FixIt library (in clangTooling) to this new library. We need to use this functionality in Refactoring/ and cannot access it if it resides in Tooling/, because that would cause clangToolingRefactor to depend on clangTooling, which would be a circular dependency. Reviewers: ilya-biryukov, ioeric Reviewed By: ilya-biryukov Subscribers: mgorny, cfe-commits Tags: #clang Differential Revision: https://reviews.llvm.org/D60269 llvm-svn: 357764 --- clang/include/clang/Tooling/FixIt.h | 44 ---------- .../include/clang/Tooling/Refactoring/SourceCode.h | 77 +++++++++++++++++ clang/lib/Tooling/FixIt.cpp | 9 -- clang/lib/Tooling/Refactoring/CMakeLists.txt | 1 + clang/lib/Tooling/Refactoring/SourceCode.cpp | 31 +++++++ clang/unittests/Tooling/CMakeLists.txt | 1 + clang/unittests/Tooling/FixItTest.cpp | 29 ------- clang/unittests/Tooling/SourceCodeTest.cpp | 97 ++++++++++++++++++++++ 8 files changed, 207 insertions(+), 82 deletions(-) create mode 100644 clang/include/clang/Tooling/Refactoring/SourceCode.h create mode 100644 clang/lib/Tooling/Refactoring/SourceCode.cpp create mode 100644 clang/unittests/Tooling/SourceCodeTest.cpp diff --git a/clang/include/clang/Tooling/FixIt.h b/clang/include/clang/Tooling/FixIt.h index a1cf8be..5fce71f 100644 --- a/clang/include/clang/Tooling/FixIt.h +++ b/clang/include/clang/Tooling/FixIt.h @@ -20,7 +20,6 @@ #define LLVM_CLANG_TOOLING_FIXIT_H #include "clang/AST/ASTContext.h" -#include "clang/Basic/TokenKinds.h" namespace clang { namespace tooling { @@ -44,11 +43,6 @@ inline CharSourceRange getSourceRange(const SourceLocation &Loc) { template CharSourceRange getSourceRange(const T &Node) { return CharSourceRange::getTokenRange(Node.getSourceRange()); } - -/// Extends \p Range to include the token \p Next, if it immediately follows the -/// end of the range. Otherwise, returns \p Range unchanged. -CharSourceRange maybeExtendRange(CharSourceRange Range, tok::TokenKind Next, - ASTContext &Context); } // end namespace internal /// Returns a textual representation of \p Node. @@ -57,44 +51,6 @@ StringRef getText(const T &Node, const ASTContext &Context) { return internal::getText(internal::getSourceRange(Node), Context); } -/// Returns the source range spanning the node, extended to include \p Next, if -/// it immediately follows \p Node. Otherwise, returns the normal range of \p -/// Node. See comments on `getExtendedText()` for examples. -template -CharSourceRange getExtendedRange(const T &Node, tok::TokenKind Next, - ASTContext &Context) { - return internal::maybeExtendRange(internal::getSourceRange(Node), Next, - Context); -} - -/// Returns the source text of the node, extended to include \p Next, if it -/// immediately follows the node. Otherwise, returns the text of just \p Node. -/// -/// For example, given statements S1 and S2 below: -/// \code -/// { -/// // S1: -/// if (!x) return foo(); -/// // S2: -/// if (!x) { return 3; } -/// } -/// \endcode -/// then -/// \code -/// getText(S1, Context) = "if (!x) return foo()" -/// getExtendedText(S1, tok::TokenKind::semi, Context) -/// = "if (!x) return foo();" -/// getExtendedText(*S1.getThen(), tok::TokenKind::semi, Context) -/// = "return foo();" -/// getExtendedText(*S2.getThen(), tok::TokenKind::semi, Context) -/// = getText(S2, Context) = "{ return 3; }" -/// \endcode -template -StringRef getExtendedText(const T &Node, tok::TokenKind Next, - ASTContext &Context) { - return internal::getText(getExtendedRange(Node, Next, Context), Context); -} - // Returns a FixItHint to remove \p Node. // TODO: Add support for related syntactical elements (i.e. comments, ...). template FixItHint createRemoval(const T &Node) { diff --git a/clang/include/clang/Tooling/Refactoring/SourceCode.h b/clang/include/clang/Tooling/Refactoring/SourceCode.h new file mode 100644 index 0000000..498dbea --- /dev/null +++ b/clang/include/clang/Tooling/Refactoring/SourceCode.h @@ -0,0 +1,77 @@ +//===--- SourceCode.h - Source code manipulation routines -------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file provides functions that simplify extraction of source code. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLING_REFACTOR_SOURCE_CODE_H +#define LLVM_CLANG_TOOLING_REFACTOR_SOURCE_CODE_H + +#include "clang/AST/ASTContext.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/TokenKinds.h" + +namespace clang { +namespace tooling { + +/// Extends \p Range to include the token \p Next, if it immediately follows the +/// end of the range. Otherwise, returns \p Range unchanged. +CharSourceRange maybeExtendRange(CharSourceRange Range, tok::TokenKind Next, + ASTContext &Context); + +/// Returns the source range spanning the node, extended to include \p Next, if +/// it immediately follows \p Node. Otherwise, returns the normal range of \p +/// Node. See comments on `getExtendedText()` for examples. +template +CharSourceRange getExtendedRange(const T &Node, tok::TokenKind Next, + ASTContext &Context) { + return maybeExtendRange(CharSourceRange::getTokenRange(Node.getSourceRange()), + Next, Context); +} + +/// Returns the source-code text in the specified range. +StringRef getText(CharSourceRange Range, const ASTContext &Context); + +/// Returns the source-code text corresponding to \p Node. +template +StringRef getText(const T &Node, const ASTContext &Context) { + return getText(CharSourceRange::getTokenRange(Node.getSourceRange()), + Context); +} + +/// Returns the source text of the node, extended to include \p Next, if it +/// immediately follows the node. Otherwise, returns the text of just \p Node. +/// +/// For example, given statements S1 and S2 below: +/// \code +/// { +/// // S1: +/// if (!x) return foo(); +/// // S2: +/// if (!x) { return 3; } +/// } +/// \endcode +/// then +/// \code +/// getText(S1, Context) = "if (!x) return foo()" +/// getExtendedText(S1, tok::TokenKind::semi, Context) +/// = "if (!x) return foo();" +/// getExtendedText(*S1.getThen(), tok::TokenKind::semi, Context) +/// = "return foo();" +/// getExtendedText(*S2.getThen(), tok::TokenKind::semi, Context) +/// = getText(S2, Context) = "{ return 3; }" +/// \endcode +template +StringRef getExtendedText(const T &Node, tok::TokenKind Next, + ASTContext &Context) { + return getText(getExtendedRange(Node, Next, Context), Context); +} +} // namespace tooling +} // namespace clang +#endif // LLVM_CLANG_TOOLING_REFACTOR_SOURCE_CODE_H diff --git a/clang/lib/Tooling/FixIt.cpp b/clang/lib/Tooling/FixIt.cpp index ca12a56..76c92c5 100644 --- a/clang/lib/Tooling/FixIt.cpp +++ b/clang/lib/Tooling/FixIt.cpp @@ -22,15 +22,6 @@ StringRef getText(CharSourceRange Range, const ASTContext &Context) { return Lexer::getSourceText(Range, Context.getSourceManager(), Context.getLangOpts()); } - -CharSourceRange maybeExtendRange(CharSourceRange Range, tok::TokenKind Next, - ASTContext &Context) { - Optional Tok = Lexer::findNextToken( - Range.getEnd(), Context.getSourceManager(), Context.getLangOpts()); - if (!Tok || !Tok->is(Next)) - return Range; - return CharSourceRange::getTokenRange(Range.getBegin(), Tok->getLocation()); -} } // namespace internal } // end namespace fixit diff --git a/clang/lib/Tooling/Refactoring/CMakeLists.txt b/clang/lib/Tooling/Refactoring/CMakeLists.txt index 402b5d3..43508f8 100644 --- a/clang/lib/Tooling/Refactoring/CMakeLists.txt +++ b/clang/lib/Tooling/Refactoring/CMakeLists.txt @@ -12,6 +12,7 @@ add_clang_library(clangToolingRefactor Rename/USRFinder.cpp Rename/USRFindingAction.cpp Rename/USRLocFinder.cpp + SourceCode.cpp LINK_LIBS clangAST diff --git a/clang/lib/Tooling/Refactoring/SourceCode.cpp b/clang/lib/Tooling/Refactoring/SourceCode.cpp new file mode 100644 index 0000000..3a97e17 --- /dev/null +++ b/clang/lib/Tooling/Refactoring/SourceCode.cpp @@ -0,0 +1,31 @@ +//===--- SourceCode.cpp - Source code manipulation routines -----*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file provides functions that simplify extraction of source code. +// +//===----------------------------------------------------------------------===// +#include "clang/Tooling/Refactoring/SourceCode.h" +#include "clang/Lex/Lexer.h" + +using namespace clang; + +StringRef clang::tooling::getText(CharSourceRange Range, + const ASTContext &Context) { + return Lexer::getSourceText(Range, Context.getSourceManager(), + Context.getLangOpts()); +} + +CharSourceRange clang::tooling::maybeExtendRange(CharSourceRange Range, + tok::TokenKind Next, + ASTContext &Context) { + Optional Tok = Lexer::findNextToken( + Range.getEnd(), Context.getSourceManager(), Context.getLangOpts()); + if (!Tok || !Tok->is(Next)) + return Range; + return CharSourceRange::getTokenRange(Range.getBegin(), Tok->getLocation()); +} diff --git a/clang/unittests/Tooling/CMakeLists.txt b/clang/unittests/Tooling/CMakeLists.txt index 7619c7f..b72af93 100644 --- a/clang/unittests/Tooling/CMakeLists.txt +++ b/clang/unittests/Tooling/CMakeLists.txt @@ -49,6 +49,7 @@ add_clang_unittest(ToolingTests RefactoringTest.cpp ReplacementsYamlTest.cpp RewriterTest.cpp + SourceCodeTest.cpp ToolingTest.cpp ) diff --git a/clang/unittests/Tooling/FixItTest.cpp b/clang/unittests/Tooling/FixItTest.cpp index bb3711e..ec9801d 100644 --- a/clang/unittests/Tooling/FixItTest.cpp +++ b/clang/unittests/Tooling/FixItTest.cpp @@ -13,7 +13,6 @@ using namespace clang; using tooling::fixit::getText; -using tooling::fixit::getExtendedText; using tooling::fixit::createRemoval; using tooling::fixit::createReplacement; @@ -78,34 +77,6 @@ TEST(FixItTest, getTextWithMacro) { "void foo(int x, int y) { FOO(x,y) }"); } -TEST(FixItTest, getExtendedText) { - CallsVisitor Visitor; - - Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { - EXPECT_EQ("foo(x, y);", - getExtendedText(*CE, tok::TokenKind::semi, *Context)); - - Expr *P0 = CE->getArg(0); - Expr *P1 = CE->getArg(1); - EXPECT_EQ("x", getExtendedText(*P0, tok::TokenKind::semi, *Context)); - EXPECT_EQ("x,", getExtendedText(*P0, tok::TokenKind::comma, *Context)); - EXPECT_EQ("y", getExtendedText(*P1, tok::TokenKind::semi, *Context)); - }; - Visitor.runOver("void foo(int x, int y) { foo(x, y); }"); - Visitor.runOver("void foo(int x, int y) { if (true) foo(x, y); }"); - Visitor.runOver("int foo(int x, int y) { if (true) return 3 + foo(x, y); }"); - Visitor.runOver("void foo(int x, int y) { for (foo(x, y);;) ++x; }"); - Visitor.runOver( - "bool foo(int x, int y) { for (;foo(x, y);) x = 1; return true; }"); - - Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { - EXPECT_EQ("foo()", getExtendedText(*CE, tok::TokenKind::semi, *Context)); - }; - Visitor.runOver("bool foo() { if (foo()) return true; return false; }"); - Visitor.runOver("void foo() { int x; for (;; foo()) ++x; }"); - Visitor.runOver("int foo() { return foo() + 3; }"); -} - TEST(FixItTest, createRemoval) { CallsVisitor Visitor; diff --git a/clang/unittests/Tooling/SourceCodeTest.cpp b/clang/unittests/Tooling/SourceCodeTest.cpp new file mode 100644 index 0000000..258947a --- /dev/null +++ b/clang/unittests/Tooling/SourceCodeTest.cpp @@ -0,0 +1,97 @@ +//===- unittest/Tooling/SourceCodeTest.cpp --------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "TestVisitor.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Tooling/Refactoring/SourceCode.h" + +using namespace clang; + +using tooling::getText; +using tooling::getExtendedText; + +namespace { + +struct CallsVisitor : TestVisitor { + bool VisitCallExpr(CallExpr *Expr) { + OnCall(Expr, Context); + return true; + } + + std::function OnCall; +}; + +TEST(SourceCodeTest, getText) { + CallsVisitor Visitor; + + Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { + EXPECT_EQ("foo(x, y)", getText(*CE, *Context)); + }; + Visitor.runOver("void foo(int x, int y) { foo(x, y); }"); + + Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { + EXPECT_EQ("APPLY(foo, x, y)", getText(*CE, *Context)); + }; + Visitor.runOver("#define APPLY(f, x, y) f(x, y)\n" + "void foo(int x, int y) { APPLY(foo, x, y); }"); +} + +TEST(SourceCodeTest, getTextWithMacro) { + CallsVisitor Visitor; + + Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { + EXPECT_EQ("F OO", getText(*CE, *Context)); + Expr *P0 = CE->getArg(0); + Expr *P1 = CE->getArg(1); + EXPECT_EQ("", getText(*P0, *Context)); + EXPECT_EQ("", getText(*P1, *Context)); + }; + Visitor.runOver("#define F foo(\n" + "#define OO x, y)\n" + "void foo(int x, int y) { F OO ; }"); + + Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { + EXPECT_EQ("", getText(*CE, *Context)); + Expr *P0 = CE->getArg(0); + Expr *P1 = CE->getArg(1); + EXPECT_EQ("x", getText(*P0, *Context)); + EXPECT_EQ("y", getText(*P1, *Context)); + }; + Visitor.runOver("#define FOO(x, y) (void)x; (void)y; foo(x, y);\n" + "void foo(int x, int y) { FOO(x,y) }"); +} + +TEST(SourceCodeTest, getExtendedText) { + CallsVisitor Visitor; + + Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { + EXPECT_EQ("foo(x, y);", + getExtendedText(*CE, tok::TokenKind::semi, *Context)); + + Expr *P0 = CE->getArg(0); + Expr *P1 = CE->getArg(1); + EXPECT_EQ("x", getExtendedText(*P0, tok::TokenKind::semi, *Context)); + EXPECT_EQ("x,", getExtendedText(*P0, tok::TokenKind::comma, *Context)); + EXPECT_EQ("y", getExtendedText(*P1, tok::TokenKind::semi, *Context)); + }; + Visitor.runOver("void foo(int x, int y) { foo(x, y); }"); + Visitor.runOver("void foo(int x, int y) { if (true) foo(x, y); }"); + Visitor.runOver("int foo(int x, int y) { if (true) return 3 + foo(x, y); }"); + Visitor.runOver("void foo(int x, int y) { for (foo(x, y);;) ++x; }"); + Visitor.runOver( + "bool foo(int x, int y) { for (;foo(x, y);) x = 1; return true; }"); + + Visitor.OnCall = [](CallExpr *CE, ASTContext *Context) { + EXPECT_EQ("foo()", getExtendedText(*CE, tok::TokenKind::semi, *Context)); + }; + Visitor.runOver("bool foo() { if (foo()) return true; return false; }"); + Visitor.runOver("void foo() { int x; for (;; foo()) ++x; }"); + Visitor.runOver("int foo() { return foo() + 3; }"); +} + +} // end anonymous namespace -- 2.7.4