[llvm-branch-commits] [cfe-branch] r149126 - in /cfe/branches/tooling: include/clang/Tooling/Refactoring.h lib/Tooling/Refactoring.cpp lib/Tooling/Tooling.cpp tools/CMakeLists.txt tools/fix-llvm-style/ tools/fix-llvm-style/CMakeLists.txt tools/fix-llvm-style/FixLLVMStyle.cpp tools/fix-llvm-style/Makefile unittests/Tooling/RefactoringTest.cpp
Manuel Klimek
klimek at google.com
Fri Jan 27 01:34:04 PST 2012
Author: klimek
Date: Fri Jan 27 03:34:04 2012
New Revision: 149126
URL: http://llvm.org/viewvc/llvm-project?rev=149126&view=rev
Log:
Implements a first draft of an LLVM style correction tool.
Includes some fixes (and regression tests) to the refactoring
infrastructure.
Added:
cfe/branches/tooling/tools/fix-llvm-style/
cfe/branches/tooling/tools/fix-llvm-style/CMakeLists.txt (with props)
cfe/branches/tooling/tools/fix-llvm-style/FixLLVMStyle.cpp (with props)
cfe/branches/tooling/tools/fix-llvm-style/Makefile (with props)
Modified:
cfe/branches/tooling/include/clang/Tooling/Refactoring.h
cfe/branches/tooling/lib/Tooling/Refactoring.cpp
cfe/branches/tooling/lib/Tooling/Tooling.cpp
cfe/branches/tooling/tools/CMakeLists.txt
cfe/branches/tooling/unittests/Tooling/RefactoringTest.cpp
Modified: cfe/branches/tooling/include/clang/Tooling/Refactoring.h
URL: http://llvm.org/viewvc/llvm-project/cfe/branches/tooling/include/clang/Tooling/Refactoring.h?rev=149126&r1=149125&r2=149126&view=diff
==============================================================================
--- cfe/branches/tooling/include/clang/Tooling/Refactoring.h (original)
+++ cfe/branches/tooling/include/clang/Tooling/Refactoring.h Fri Jan 27 03:34:04 2012
@@ -35,6 +35,9 @@
/// specific file.
class Replacement {
public:
+ /// \brief Creates an invalid (not applicable) replacement.
+ Replacement();
+
/// \brief Creates a replacement of the range [Offset, Offset+Length) in
/// FilePath with ReplacementText.
///
@@ -58,6 +61,18 @@
Replacement(SourceManager &Sources, const Node &NodeToReplace,
llvm::StringRef ReplacementText);
+ /// \brief Returns whether this replacement can be applied to a file.
+ ///
+ /// Only replacements that are in a valid file can be applied.
+ bool IsApplicable() const;
+
+ /// \brief Accessors.
+ /// @{
+ std::string GetFilePath() const { return FilePath; }
+ unsigned GetOffset() const { return Offset; }
+ unsigned GetLength() const { return Length; }
+ /// @}
+
/// \brief Applies the replacement on the Rewriter.
bool Apply(Rewriter &Rewrite) const;
Modified: cfe/branches/tooling/lib/Tooling/Refactoring.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/branches/tooling/lib/Tooling/Refactoring.cpp?rev=149126&r1=149125&r2=149126&view=diff
==============================================================================
--- cfe/branches/tooling/lib/Tooling/Refactoring.cpp (original)
+++ cfe/branches/tooling/lib/Tooling/Refactoring.cpp Fri Jan 27 03:34:04 2012
@@ -23,10 +23,15 @@
namespace clang {
namespace tooling {
+static const char * const InvalidLocation = "invalid-location";
+
+Replacement::Replacement()
+ : FilePath(InvalidLocation), Offset(0), Length(0) {}
+
Replacement::Replacement(llvm::StringRef FilePath, unsigned Offset,
unsigned Length, llvm::StringRef ReplacementText)
- : FilePath(FilePath), Offset(Offset),
- Length(Length), ReplacementText(ReplacementText) {}
+ : FilePath(FilePath), Offset(Offset),
+ Length(Length), ReplacementText(ReplacementText) {}
Replacement::Replacement(SourceManager &Sources, SourceLocation Start,
unsigned Length, llvm::StringRef ReplacementText) {
@@ -38,6 +43,10 @@
SetFromSourceRange(Sources, Range, ReplacementText);
}
+bool Replacement::IsApplicable() const {
+ return FilePath != InvalidLocation;
+}
+
bool Replacement::Apply(Rewriter &Rewrite) const {
SourceManager &SM = Rewrite.getSourceMgr();
const FileEntry *Entry = SM.getFileManager().getFile(FilePath);
@@ -76,7 +85,7 @@
const std::pair<FileID, unsigned> DecomposedLocation =
Sources.getDecomposedLoc(Start);
const FileEntry *Entry = Sources.getFileEntryForID(DecomposedLocation.first);
- this->FilePath = Entry != NULL ? Entry->getName() : "invalid-location";
+ this->FilePath = Entry != NULL ? Entry->getName() : InvalidLocation;
this->Offset = DecomposedLocation.second;
this->Length = Length;
this->ReplacementText = ReplacementText;
@@ -85,7 +94,7 @@
void Replacement::SetFromSourceRange(SourceManager &Sources,
const CharSourceRange &Range,
llvm::StringRef ReplacementText) {
- SetFromSourceLocation(Sources, Range.getBegin(),
+ SetFromSourceLocation(Sources, Sources.getSpellingLoc(Range.getBegin()),
getRangeSize(Sources, Range), ReplacementText);
}
@@ -94,7 +103,11 @@
for (Replacements::const_iterator I = Replaces.begin(),
E = Replaces.end();
I != E; ++I) {
- Result = I->Apply(Rewrite) && Result;
+ if (I->IsApplicable()) {
+ Result = I->Apply(Rewrite) && Result;
+ } else {
+ Result = false;
+ }
}
return Result;
}
@@ -120,14 +133,14 @@
}
int getRangeSize(SourceManager &Sources, const CharSourceRange &Range) {
- std::pair<FileID, unsigned> Start =
- Sources.getDecomposedLoc(Range.getBegin());
- std::pair<FileID, unsigned> End =
- Sources.getDecomposedLoc(Range.getEnd());
+ SourceLocation SpellingBegin = Sources.getSpellingLoc(Range.getBegin());
+ SourceLocation SpellingEnd = Sources.getSpellingLoc(Range.getEnd());
+ std::pair<FileID, unsigned> Start = Sources.getDecomposedLoc(SpellingBegin);
+ std::pair<FileID, unsigned> End = Sources.getDecomposedLoc(SpellingEnd);
if (Start.first != End.first) return -1;
if (Range.isTokenRange())
// FIXME: Bring in the correct LangOptions.
- End.second += Lexer::MeasureTokenLength(Range.getEnd(), Sources,
+ End.second += Lexer::MeasureTokenLength(SpellingEnd, Sources,
LangOptions());
return End.second - Start.second;
}
@@ -148,8 +161,7 @@
SourceManager Sources(Diagnostics, Tool.GetFiles());
Rewriter Rewrite(Sources, DefaultLangOptions);
if (!ApplyAllReplacements(Replace, Rewrite)) {
- llvm::errs() << "Could not apply replacements.\n";
- return 1;
+ llvm::errs() << "Skipped some replacements.\n";
}
if (!SaveRewrittenFiles(Rewrite)) {
llvm::errs() << "Could not save rewritten files.\n";
Modified: cfe/branches/tooling/lib/Tooling/Tooling.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/branches/tooling/lib/Tooling/Tooling.cpp?rev=149126&r1=149125&r2=149126&view=diff
==============================================================================
--- cfe/branches/tooling/lib/Tooling/Tooling.cpp (original)
+++ cfe/branches/tooling/lib/Tooling/Tooling.cpp Fri Jan 27 03:34:04 2012
@@ -436,7 +436,8 @@
if (!ErrorMessage.empty()) {
llvm::outs() << "Error while parsing JSON database: " << ErrorMessage
<< "\n";
- exit(1);
+ llvm::outs() << "Skipping " << File << "\n";
+ continue;
}
if (!LookupResult.CommandLine.empty()) {
if (!LookupResult.Directory.empty()) {
Modified: cfe/branches/tooling/tools/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/branches/tooling/tools/CMakeLists.txt?rev=149126&r1=149125&r2=149126&view=diff
==============================================================================
--- cfe/branches/tooling/tools/CMakeLists.txt (original)
+++ cfe/branches/tooling/tools/CMakeLists.txt Fri Jan 27 03:34:04 2012
@@ -6,3 +6,4 @@
add_subdirectory(driver)
add_subdirectory(clang-check)
add_subdirectory(remove-cstr-calls)
+add_subdirectory(fix-llvm-style)
Added: cfe/branches/tooling/tools/fix-llvm-style/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/branches/tooling/tools/fix-llvm-style/CMakeLists.txt?rev=149126&view=auto
==============================================================================
--- cfe/branches/tooling/tools/fix-llvm-style/CMakeLists.txt (added)
+++ cfe/branches/tooling/tools/fix-llvm-style/CMakeLists.txt Fri Jan 27 03:34:04 2012
@@ -0,0 +1,5 @@
+set(LLVM_USED_LIBS clangTooling clangBasic clangAST)
+
+add_clang_executable(fix-llvm-style
+ FixLLVMStyle.cpp
+ )
Propchange: cfe/branches/tooling/tools/fix-llvm-style/CMakeLists.txt
------------------------------------------------------------------------------
svn:eol-style = LF
Added: cfe/branches/tooling/tools/fix-llvm-style/FixLLVMStyle.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/branches/tooling/tools/fix-llvm-style/FixLLVMStyle.cpp?rev=149126&view=auto
==============================================================================
--- cfe/branches/tooling/tools/fix-llvm-style/FixLLVMStyle.cpp (added)
+++ cfe/branches/tooling/tools/fix-llvm-style/FixLLVMStyle.cpp Fri Jan 27 03:34:04 2012
@@ -0,0 +1,165 @@
+//=- tools/fix-llvm-style/FixLLVMStyle.cpp - Automatic LLVM style correction =//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// FIXME: This is an early first draft that needs clean-up.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Tooling/Refactoring.h"
+#include "clang/Tooling/Tooling.h"
+#include "llvm/ADT/OwningPtr.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Regex.h"
+#include "llvm/Support/raw_ostream.h"
+#include "llvm/Support/system_error.h"
+
+using namespace clang;
+using namespace clang::ast_matchers;
+using clang::tooling::NewFrontendActionFactory;
+using clang::tooling::Replacement;
+
+// FIXME: Pull out helper methods in here into more fitting places.
+
+template <typename T>
+std::string GetFile(const clang::SourceManager& source_manager, const T& node) {
+ clang::SourceLocation start_spelling_location =
+ source_manager.getSpellingLoc(node.getLocStart());
+ if (!start_spelling_location.isValid()) return std::string();
+ clang::FileID file_id = source_manager.getFileID(start_spelling_location);
+ const clang::FileEntry* file_entry =
+ source_manager.getFileEntryForID(file_id);
+ if (file_entry == NULL) return std::string();
+ return file_entry->getName();
+}
+
+// Returns the text that makes up 'node' in the source.
+// Returns an empty string if the text cannot be found.
+static std::string GetText(const SourceManager &SourceManager,
+ SourceLocation LocStart, SourceLocation LocEnd) {
+ SourceLocation StartSpellingLocatino =
+ SourceManager.getSpellingLoc(LocStart);
+ SourceLocation EndSpellingLocation =
+ SourceManager.getSpellingLoc(LocEnd);
+ if (!StartSpellingLocatino.isValid() || !EndSpellingLocation.isValid()) {
+ return std::string();
+ }
+ bool Invalid = true;
+ const char *Text =
+ SourceManager.getCharacterData(StartSpellingLocatino, &Invalid);
+ if (Invalid) {
+ return std::string();
+ }
+ std::pair<FileID, unsigned> Start =
+ SourceManager.getDecomposedLoc(StartSpellingLocatino);
+ std::pair<FileID, unsigned> End =
+ SourceManager.getDecomposedLoc(Lexer::getLocForEndOfToken(
+ EndSpellingLocation, 0, SourceManager, LangOptions()));
+ if (Start.first != End.first) {
+ // Start and end are in different files.
+ return std::string();
+ }
+ if (End.second < Start.second) {
+ // Shuffling text with macros may cause this.
+ return std::string();
+ }
+ return std::string(Text, End.second - Start.second);
+}
+
+template <typename T>
+static std::string GetText(const SourceManager &SourceManager, const T &Node) {
+ return GetText(SourceManager, Node.getLocStart(), Node.getLocEnd());
+}
+
+namespace {
+class FixLLVMStyle: public ast_matchers::MatchFinder::MatchCallback {
+ public:
+ FixLLVMStyle(tooling::Replacements *Replace)
+ : Replace(Replace), EditFilesExpression(".*Tooling/.*") {}
+
+ virtual void Run(const ast_matchers::MatchFinder::MatchResult &Result) {
+ if (const CallExpr *Call = Result.Nodes.GetStmtAs<CallExpr>("call")) {
+ llvm::errs() << "Skipping: "
+ << GetText(*Result.SourceManager, *Call) << "\n";
+ return;
+ }
+ Replacement ReplaceText;
+ std::string Name;
+ std::string OldName;
+ std::string SedCommand;
+ if (const FunctionDecl *Declaration =
+ Result.Nodes.GetDeclAs<FunctionDecl>("declaration")) {
+ Name = Declaration->getNameAsString();
+ OldName = Name;
+ if (const CXXMethodDecl *Method =
+ llvm::dyn_cast<CXXMethodDecl>(Declaration)) {
+ if (Method->size_overridden_methods() > 0) {
+ llvm::errs() << "Skipping: " << OldName << "\n";
+ return;
+ }
+ }
+ if (isupper(Name[0])) {
+ Name[0] = tolower(Name[0]);
+ if (Name == "new") Name = "create";
+
+ if (const Expr *Callee = Result.Nodes.GetStmtAs<Expr>("callee")) {
+ if (!EditFilesExpression.match(GetFile(*Result.SourceManager,
+ *Declaration))) {
+ llvm::errs() << "Skipping: " << OldName << "\n";
+ return;
+ }
+ std::string CalleeText = GetText(*Result.SourceManager, *Callee);
+ std::string ReplacementText =
+ CalleeText.substr(0, CalleeText.size() - OldName.size()) + Name;
+ ReplaceText = Replacement(*Result.SourceManager, Callee,
+ ReplacementText);
+ } else {
+ DeclarationNameInfo NameInfo = Declaration->getNameInfo();
+ ReplaceText = Replacement(*Result.SourceManager, &NameInfo, Name);
+ }
+ }
+ }
+ if (EditFilesExpression.match(ReplaceText.GetFilePath())) {
+ llvm::outs() << "s/" << OldName << "/" << Name << "/g;\n";
+ Replace->insert(ReplaceText);
+ }
+ }
+
+ private:
+ tooling::Replacements *Replace;
+ llvm::Regex EditFilesExpression;
+};
+} // end namespace
+
+int main(int argc, char **argv) {
+ tooling::RefactoringTool Tool(argc, argv);
+ ast_matchers::MatchFinder Finder;
+ Finder.AddMatcher(
+ StatementMatcher(AnyOf(
+ // Match method or function calls.
+ Call(Callee(Id("declaration", Function())),
+ Callee(Id("callee", Expression()))),
+ // Match any calls to catch cases we can't handle yet.
+ Id("call", Call()))),
+ new FixLLVMStyle(&Tool.GetReplacements()));
+
+ Finder.AddMatcher(
+ DeclarationMatcher(AllOf(
+ Id("declaration", Function()),
+ Not(Constructor()))),
+ new FixLLVMStyle(&Tool.GetReplacements()));
+ return Tool.Run(NewFrontendActionFactory(&Finder));
+}
+
Propchange: cfe/branches/tooling/tools/fix-llvm-style/FixLLVMStyle.cpp
------------------------------------------------------------------------------
svn:eol-style = LF
Added: cfe/branches/tooling/tools/fix-llvm-style/Makefile
URL: http://llvm.org/viewvc/llvm-project/cfe/branches/tooling/tools/fix-llvm-style/Makefile?rev=149126&view=auto
==============================================================================
--- cfe/branches/tooling/tools/fix-llvm-style/Makefile (added)
+++ cfe/branches/tooling/tools/fix-llvm-style/Makefile Fri Jan 27 03:34:04 2012
@@ -0,0 +1,23 @@
+##===- tools/remove-cstr-calls/Makefile --------------------*- Makefile -*-===##
+#
+# The LLVM Compiler Infrastructure
+#
+# This file is distributed under the University of Illinois Open Source
+# License. See LICENSE.TXT for details.
+#
+##===----------------------------------------------------------------------===##
+
+CLANG_LEVEL := ../..
+
+TOOLNAME = fix-llvm-style
+NO_INSTALL = 1
+
+# No plugins, optimize startup time.
+TOOL_NO_EXPORTS = 1
+
+LINK_COMPONENTS := support mc
+USEDLIBS = clangTooling.a clangFrontend.a clangSerialization.a clangDriver.a \
+ clangRewrite.a clangParse.a clangSema.a clangAnalysis.a \
+ clangAST.a clangASTMatchers.a clangLex.a clangBasic.a
+
+include $(CLANG_LEVEL)/Makefile
Propchange: cfe/branches/tooling/tools/fix-llvm-style/Makefile
------------------------------------------------------------------------------
svn:eol-style = LF
Modified: cfe/branches/tooling/unittests/Tooling/RefactoringTest.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/branches/tooling/unittests/Tooling/RefactoringTest.cpp?rev=149126&r1=149125&r2=149126&view=diff
==============================================================================
--- cfe/branches/tooling/unittests/Tooling/RefactoringTest.cpp (original)
+++ cfe/branches/tooling/unittests/Tooling/RefactoringTest.cpp Fri Jan 27 03:34:04 2012
@@ -7,14 +7,20 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/AST/ASTConsumer.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclGroup.h"
#include "clang/Tooling/Refactoring.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceManager.h"
+#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/DiagnosticOptions.h"
+#include "clang/Frontend/FrontendAction.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Rewrite/Rewriter.h"
+#include "clang/Tooling/Tooling.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/raw_os_ostream.h"
@@ -102,7 +108,7 @@
TEST_F(ReplacementTest, CanReplaceTextAtPosition) {
FileID ID = Context.CreateInMemoryFile("input.cpp",
- "line1\nline2\nline3\nline4");
+ "line1\nline2\nline3\nline4");
SourceLocation Location = Context.GetLocation(ID, 2, 3);
Replacement Replace(Context.CreateReplacement(Location, 12, "x"));
EXPECT_TRUE(Replace.Apply(Context.Rewrite));
@@ -130,6 +136,19 @@
EXPECT_FALSE(Replace.Apply(Context.Rewrite));
}
+TEST_F(ReplacementTest, CanRetrivePath) {
+ Replacement Replace("/path/to/file.cpp", 0, 1, "");
+ EXPECT_EQ("/path/to/file.cpp", Replace.GetFilePath());
+}
+
+TEST_F(ReplacementTest, ReturnsInvalidPath) {
+ Replacement Replace1(Context.Sources, SourceLocation(), 0, "");
+ EXPECT_EQ("invalid-location", Replace1.GetFilePath());
+
+ Replacement Replace2;
+ EXPECT_EQ("invalid-location", Replace2.GetFilePath());
+}
+
TEST_F(ReplacementTest, CanApplyReplacements) {
FileID ID = Context.CreateInMemoryFile("input.cpp",
"line1\nline2\nline3\nline4");
@@ -227,5 +246,70 @@
GetFileContentFromDisk("input.cpp"));
}
+// FIXME: Copied from ToolingTest.cpp - put into a common header.
+namespace {
+class FindClassDeclXConsumer : public clang::ASTConsumer {
+ public:
+ FindClassDeclXConsumer(bool *FoundClassDeclX, StringRef ExpectedFile,
+ unsigned ExpectedOffset, unsigned ExpectedLength)
+ : SM(NULL), FoundClassDeclX(FoundClassDeclX), ExpectedFile(ExpectedFile),
+ ExpectedOffset(ExpectedOffset), ExpectedLength(ExpectedLength) {}
+ virtual bool HandleTopLevelDecl(clang::DeclGroupRef GroupRef) {
+ if (CXXRecordDecl* Record = llvm::dyn_cast<clang::CXXRecordDecl>(
+ *GroupRef.begin())) {
+ if (Record->getName() == "X") {
+ Replacement Replace(*SM, Record, "");
+ EXPECT_EQ(ExpectedFile, Replace.GetFilePath());
+ EXPECT_EQ(ExpectedOffset, Replace.GetOffset());
+ EXPECT_EQ(ExpectedLength, Replace.GetLength());
+ *FoundClassDeclX = true;
+ }
+ }
+ return true;
+ }
+ clang::SourceManager *SM;
+ private:
+ bool *FoundClassDeclX;
+ std::string ExpectedFile;
+ unsigned ExpectedOffset;
+ unsigned ExpectedLength;
+};
+
+class TestAction : public clang::ASTFrontendAction {
+ public:
+ explicit TestAction(FindClassDeclXConsumer *TestConsumer)
+ : TestConsumer(TestConsumer) {}
+
+ protected:
+ virtual clang::ASTConsumer* CreateASTConsumer(
+ clang::CompilerInstance& compiler, llvm::StringRef dummy) {
+ TestConsumer->SM = &compiler.getSourceManager();
+ /// TestConsumer will be deleted by the framework calling us.
+ return TestConsumer;
+ }
+
+ private:
+ FindClassDeclXConsumer * const TestConsumer;
+};
+} // end namespace
+
+TEST(Replacement, CanBeConstructedFromNode) {
+ bool FoundClassDeclX = false;
+ EXPECT_TRUE(RunSyntaxOnlyToolOnCode(
+ new TestAction(
+ new FindClassDeclXConsumer(&FoundClassDeclX, "input.cc", 5, 7)),
+ " class X;"));
+ EXPECT_TRUE(FoundClassDeclX);
+}
+
+TEST(Replacement, ReplacesAtSpellingLocation) {
+ bool FoundClassDeclX = false;
+ EXPECT_TRUE(RunSyntaxOnlyToolOnCode(
+ new TestAction(
+ new FindClassDeclXConsumer(&FoundClassDeclX, "input.cc", 17, 7)),
+ "#define A(Y) Y\nA(class X);"));
+ EXPECT_TRUE(FoundClassDeclX);
+}
+
} // end namespace tooling
} // end namespace clang
More information about the llvm-branch-commits
mailing list