Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[LibTooling] Add "SourceCode" library for functions relating to sourc…
…e-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
- Loading branch information
Showing
8 changed files
with
207 additions
and
82 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <typename T> | ||
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 <typename T> | ||
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 <typename T> | ||
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<Token> Tok = Lexer::findNextToken( | ||
Range.getEnd(), Context.getSourceManager(), Context.getLangOpts()); | ||
if (!Tok || !Tok->is(Next)) | ||
return Range; | ||
return CharSourceRange::getTokenRange(Range.getBegin(), Tok->getLocation()); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<CallsVisitor> { | ||
bool VisitCallExpr(CallExpr *Expr) { | ||
OnCall(Expr, Context); | ||
return true; | ||
} | ||
|
||
std::function<void(CallExpr *, ASTContext *Context)> 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 |