forked from OSchip/llvm-project
Introduce a "-fixit" mode to clang-cc that applies code-modification hints.
llvm-svn: 68268
This commit is contained in:
parent
40968598c7
commit
578dae57ca
|
@ -0,0 +1,65 @@
|
|||
//===--- FixItRewriter.h - Fix-It Rewriter Diagnostic Client ----*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This is a diagnostic client adaptor that performs rewrites as
|
||||
// suggested by code modification hints attached to diagnostics. It
|
||||
// then forwards any diagnostics to the adapted diagnostic client.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#ifndef LLVM_CLANG_FRONTEND_FIX_IT_REWRITER_H
|
||||
#define LLVM_CLANG_FRONTEND_FIX_IT_REWRITER_H
|
||||
|
||||
#include "clang/Basic/Diagnostic.h"
|
||||
|
||||
namespace clang {
|
||||
|
||||
class Rewriter;
|
||||
class SourceManager;
|
||||
|
||||
class FixItRewriter : public DiagnosticClient {
|
||||
/// \brief The adapted diagnostic client, to which we will forward
|
||||
/// any diagnostics.
|
||||
DiagnosticClient *Client;
|
||||
|
||||
/// \brief The rewriter used to perform the various code
|
||||
/// modifications.
|
||||
Rewriter *Rewrite;
|
||||
|
||||
/// \brief The number of rewriter failures.
|
||||
unsigned NumFailures;
|
||||
|
||||
public:
|
||||
/// \brief Initialize a new fix-it rewriter.
|
||||
FixItRewriter(DiagnosticClient *Client, SourceManager &SourceMgr);
|
||||
|
||||
/// \brief Destroy the fix-it rewriter.
|
||||
~FixItRewriter();
|
||||
|
||||
/// \brief Write the modified source file.
|
||||
///
|
||||
/// \returns true if there was an error, false otherwise.
|
||||
bool WriteFixedFile(const std::string &InFileName,
|
||||
const std::string &OutFileName = std::string());
|
||||
|
||||
/// IncludeInDiagnosticCounts - This method (whose default implementation
|
||||
/// returns true) indicates whether the diagnostics handled by this
|
||||
/// DiagnosticClient should be included in the number of diagnostics
|
||||
/// reported by Diagnostic.
|
||||
virtual bool IncludeInDiagnosticCounts() const;
|
||||
|
||||
/// HandleDiagnostic - Handle this diagnostic, reporting it to the user or
|
||||
/// capturing it to a log as needed.
|
||||
virtual void HandleDiagnostic(Diagnostic::Level DiagLevel,
|
||||
const DiagnosticInfo &Info);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // LLVM_CLANG_FRONTEND_FIX_IT_REWRITER_H
|
|
@ -0,0 +1,140 @@
|
|||
//===--- FixItRewriter.cpp - Fix-It Rewriter Diagnostic Client --*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This is a diagnostic client adaptor that performs rewrites as
|
||||
// suggested by code modification hints attached to diagnostics. It
|
||||
// then forwards any diagnostics to the adapted diagnostic client.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
#include "clang/Basic/SourceManager.h"
|
||||
#include "clang/Frontend/FixItRewriter.h"
|
||||
#include "clang/Rewrite/Rewriter.h"
|
||||
#include "llvm/ADT/OwningPtr.h"
|
||||
#include "llvm/Support/Streams.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include "llvm/System/Path.h"
|
||||
#include <cstdio>
|
||||
using namespace clang;
|
||||
|
||||
FixItRewriter::FixItRewriter(DiagnosticClient *Client,
|
||||
SourceManager &SourceMgr)
|
||||
: Client(Client), NumFailures(0) {
|
||||
Rewrite = new Rewriter(SourceMgr);
|
||||
}
|
||||
|
||||
FixItRewriter::~FixItRewriter() {
|
||||
delete Rewrite;
|
||||
}
|
||||
|
||||
bool FixItRewriter::WriteFixedFile(const std::string &InFileName,
|
||||
const std::string &OutFileName) {
|
||||
if (NumFailures > 0) {
|
||||
// FIXME: Use diagnostic machinery!
|
||||
std::fprintf(stderr,
|
||||
"%d fix-it failures detected; code will not be modified",
|
||||
NumFailures);
|
||||
return true;
|
||||
}
|
||||
|
||||
llvm::OwningPtr<llvm::raw_ostream> OwnedStream;
|
||||
llvm::raw_ostream *OutFile;
|
||||
if (OutFileName == "-") {
|
||||
OutFile = &llvm::outs();
|
||||
} else if (!OutFileName.empty()) {
|
||||
std::string Err;
|
||||
OutFile = new llvm::raw_fd_ostream(OutFileName.c_str(),
|
||||
// set binary mode (critical for Windoze)
|
||||
true,
|
||||
Err);
|
||||
OwnedStream.reset(OutFile);
|
||||
} else if (InFileName == "-") {
|
||||
OutFile = &llvm::outs();
|
||||
} else {
|
||||
llvm::sys::Path Path(InFileName);
|
||||
Path.eraseSuffix();
|
||||
Path.appendSuffix("cpp");
|
||||
std::string Err;
|
||||
OutFile = new llvm::raw_fd_ostream(Path.toString().c_str(),
|
||||
// set binary mode (critical for Windoze)
|
||||
true,
|
||||
Err);
|
||||
OwnedStream.reset(OutFile);
|
||||
}
|
||||
|
||||
FileID MainFileID = Rewrite->getSourceMgr().getMainFileID();
|
||||
if (const RewriteBuffer *RewriteBuf =
|
||||
Rewrite->getRewriteBufferFor(MainFileID)) {
|
||||
*OutFile << std::string(RewriteBuf->begin(), RewriteBuf->end());
|
||||
} else {
|
||||
std::fprintf(stderr, "Main file is unchanged\n");
|
||||
}
|
||||
OutFile->flush();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FixItRewriter::IncludeInDiagnosticCounts() const {
|
||||
return Client? Client->IncludeInDiagnosticCounts() : false;
|
||||
}
|
||||
|
||||
void FixItRewriter::HandleDiagnostic(Diagnostic::Level DiagLevel,
|
||||
const DiagnosticInfo &Info) {
|
||||
if (Client)
|
||||
Client->HandleDiagnostic(DiagLevel, Info);
|
||||
|
||||
// Make sure that we can perform all of the modifications we
|
||||
// in this diagnostic.
|
||||
bool CanRewrite = true;
|
||||
for (unsigned Idx = 0; Idx < Info.getNumCodeModificationHints(); ++Idx) {
|
||||
const CodeModificationHint &Hint = Info.getCodeModificationHint(Idx);
|
||||
if (Hint.RemoveRange.isValid() &&
|
||||
(!Rewrite->isRewritable(Hint.RemoveRange.getBegin()) ||
|
||||
!Rewrite->isRewritable(Hint.RemoveRange.getEnd()) ||
|
||||
Rewrite->getRangeSize(Hint.RemoveRange) == -1)) {
|
||||
CanRewrite = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (Hint.InsertionLoc.isValid() &&
|
||||
!Rewrite->isRewritable(Hint.InsertionLoc)) {
|
||||
CanRewrite = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!CanRewrite) // FIXME: warn the user that this rewrite couldn't be done
|
||||
return;
|
||||
|
||||
bool Failed = false;
|
||||
for (unsigned Idx = 0; Idx < Info.getNumCodeModificationHints(); ++Idx) {
|
||||
const CodeModificationHint &Hint = Info.getCodeModificationHint(Idx);
|
||||
if (Hint.RemoveRange.isValid()) {
|
||||
if (Hint.CodeToInsert.empty()) {
|
||||
// We're removing code.
|
||||
if (Rewrite->RemoveText(Hint.RemoveRange.getBegin(),
|
||||
Rewrite->getRangeSize(Hint.RemoveRange)))
|
||||
Failed = true;
|
||||
} else {
|
||||
// We're replacing code.
|
||||
if (Rewrite->ReplaceText(Hint.RemoveRange.getBegin(),
|
||||
Rewrite->getRangeSize(Hint.RemoveRange),
|
||||
Hint.CodeToInsert.c_str(),
|
||||
Hint.CodeToInsert.size()))
|
||||
Failed = true;
|
||||
}
|
||||
} else {
|
||||
// We're adding code.
|
||||
if (Rewrite->InsertStrBefore(Hint.InsertionLoc, Hint.CodeToInsert))
|
||||
Failed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (Failed)
|
||||
++NumFailures;
|
||||
}
|
|
@ -218,7 +218,7 @@ Parser::OwningExprResult Parser::ParseInitializerWithPotentialDesignator() {
|
|||
(Desig.getDesignator(0).isArrayDesignator() ||
|
||||
Desig.getDesignator(0).isArrayRangeDesignator())) {
|
||||
Diag(Tok, diag::ext_gnu_missing_equal_designator)
|
||||
<< CodeModificationHint::CreateInsertion(Tok.getLocation(), "=");
|
||||
<< CodeModificationHint::CreateInsertion(Tok.getLocation(), "= ");
|
||||
return Actions.ActOnDesignatedInitializer(Desig, Tok.getLocation(),
|
||||
true, ParseInitializer());
|
||||
}
|
||||
|
|
|
@ -1309,7 +1309,7 @@ bool Sema::CheckConstructor(CXXConstructorDecl *Constructor) {
|
|||
if (Context.getCanonicalType(ParamType).getUnqualifiedType() == ClassTy) {
|
||||
SourceLocation ParamLoc = Constructor->getParamDecl(0)->getLocation();
|
||||
Diag(ParamLoc, diag::err_constructor_byvalue_arg)
|
||||
<< CodeModificationHint::CreateInsertion(ParamLoc, "const &");
|
||||
<< CodeModificationHint::CreateInsertion(ParamLoc, " const &");
|
||||
Invalid = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* RUN: clang -fsyntax-only -std=c90 -pedantic %s
|
||||
/* RUN: clang -fsyntax-only -std=c90 -pedantic -fixit %s -o - | clang-cc -pedantic -x c -std=c90 -Werror -
|
||||
*/
|
||||
/* This is a test of the various code modification hints that are
|
||||
provided as part of warning or extension diagnostics. Eventually,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// RUN: clang-cc -fsyntax-only -pedantic -verify %s
|
||||
// RUN: clang-cc -fsyntax-only -pedantic -fixit %s -o - | clang-cc -pedantic -Werror -x c -
|
||||
|
||||
/* This is a test of the various code modification hints that are
|
||||
provided as part of warning or extension diagnostics. Eventually,
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// RUN: clang -fsyntax-only -pedantic %s
|
||||
// RUN: clang -fsyntax-only -pedantic -fixit %s -o - | clang-cc -pedantic -Werror -x c -
|
||||
|
||||
/* This is a test of the various code modification hints that are
|
||||
provided as part of warning or extension diagnostics. Eventually,
|
||||
|
@ -25,5 +25,6 @@ int i0 = { 17 };
|
|||
|
||||
int f2(const char *my_string) {
|
||||
// FIXME: terminal output isn't so good when "my_string" is shorter
|
||||
return my_string == "foo";
|
||||
// FIXME: Needs an #include hint, too!
|
||||
// return my_string == "foo";
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// RUN: clang-cc -fsyntax-only -pedantic -verify %s
|
||||
// RUN: clang-cc -fsyntax-only -pedantic -fixit %s -o - | clang-cc -fsyntax-only -pedantic -Werror -x c++ -
|
||||
|
||||
/* This is a test of the various code modification hints that are
|
||||
provided as part of warning or extension diagnostics. Eventually,
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "clang-cc.h"
|
||||
#include "ASTConsumers.h"
|
||||
#include "clang/Frontend/CompileOptions.h"
|
||||
#include "clang/Frontend/FixItRewriter.h"
|
||||
#include "clang/Frontend/FrontendDiagnostic.h"
|
||||
#include "clang/Frontend/InitHeaderSearch.h"
|
||||
#include "clang/Frontend/PathDiagnosticClients.h"
|
||||
|
@ -85,6 +86,7 @@ enum ProgActions {
|
|||
RewriteBlocks, // ObjC->C Rewriter for Blocks.
|
||||
RewriteMacros, // Expand macros but not #includes.
|
||||
RewriteTest, // Rewriter playground
|
||||
FixIt, // Fix-It Rewriter
|
||||
HTMLTest, // HTML displayer testing stuff.
|
||||
EmitAssembly, // Emit a .s file.
|
||||
EmitLLVM, // Emit a .ll file.
|
||||
|
@ -161,6 +163,8 @@ ProgAction(llvm::cl::desc("Choose output type:"), llvm::cl::ZeroOrMore,
|
|||
"Expand macros without full preprocessing"),
|
||||
clEnumValN(RewriteBlocks, "rewrite-blocks",
|
||||
"Rewrite Blocks to C"),
|
||||
clEnumValN(FixIt, "fixit",
|
||||
"Apply fix-it advice to the input source"),
|
||||
clEnumValEnd));
|
||||
|
||||
|
||||
|
@ -1342,7 +1346,8 @@ static void ProcessInputFile(Preprocessor &PP, PreprocessorFactory &PPF,
|
|||
const std::string &InFile, ProgActions PA) {
|
||||
llvm::OwningPtr<ASTConsumer> Consumer;
|
||||
bool ClearSourceMgr = false;
|
||||
|
||||
FixItRewriter *FixItRewrite = 0;
|
||||
|
||||
switch (PA) {
|
||||
default:
|
||||
Consumer.reset(CreateASTConsumer(InFile, PP.getDiagnostics(),
|
||||
|
@ -1443,6 +1448,14 @@ static void ProcessInputFile(Preprocessor &PP, PreprocessorFactory &PPF,
|
|||
ClearSourceMgr = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case FixIt:
|
||||
llvm::TimeRegion Timer(ClangFrontendTimer);
|
||||
Consumer.reset(new ASTConsumer());
|
||||
FixItRewrite = new FixItRewriter(PP.getDiagnostics().getClient(),
|
||||
PP.getSourceManager());
|
||||
PP.getDiagnostics().setClient(FixItRewrite);
|
||||
break;
|
||||
}
|
||||
|
||||
if (Consumer) {
|
||||
|
@ -1458,6 +1471,9 @@ static void ProcessInputFile(Preprocessor &PP, PreprocessorFactory &PPF,
|
|||
|
||||
ParseAST(PP, Consumer.get(), *ContextOwner.get(), Stats);
|
||||
|
||||
if (FixItRewrite)
|
||||
FixItRewrite->WriteFixedFile(InFile, OutputFile);
|
||||
|
||||
// If in -disable-free mode, don't deallocate these when they go out of
|
||||
// scope.
|
||||
if (DisableFree)
|
||||
|
|
Loading…
Reference in New Issue