diff options
-rw-r--r-- | include/clang/Frontend/FixItRewriter.h | 24 | ||||
-rw-r--r-- | lib/Frontend/FixItRewriter.cpp | 39 | ||||
-rw-r--r-- | test/FixIt/fixit-at.c | 5 | ||||
-rw-r--r-- | tools/clang-cc/clang-cc.cpp | 121 |
4 files changed, 187 insertions, 2 deletions
diff --git a/include/clang/Frontend/FixItRewriter.h b/include/clang/Frontend/FixItRewriter.h index 752b00836c..363b9ad9a3 100644 --- a/include/clang/Frontend/FixItRewriter.h +++ b/include/clang/Frontend/FixItRewriter.h @@ -17,10 +17,23 @@ #include "clang/Basic/Diagnostic.h" #include "clang/Rewrite/Rewriter.h" +#include "llvm/ADT/SmallVector.h" namespace clang { class SourceManager; +class FileEntry; + +/// \brief Stores a source location in the form that it shows up on +/// the Clang command line, e.g., file:line:column. +/// +/// FIXME: Would prefer to use real SourceLocations, but I don't see a +/// good way to resolve them during parsing. +struct RequestedSourceLocation { + const FileEntry *File; + unsigned Line; + unsigned Column; +}; class FixItRewriter : public DiagnosticClient { /// \brief The diagnostics machinery. @@ -37,6 +50,11 @@ class FixItRewriter : public DiagnosticClient { /// \brief The number of rewriter failures. unsigned NumFailures; + /// \brief Locations at which we should perform fix-its. + /// + /// When empty, perform fix-it modifications everywhere. + llvm::SmallVector<RequestedSourceLocation, 4> FixItLocations; + public: /// \brief Initialize a new fix-it rewriter. FixItRewriter(Diagnostic &Diags, SourceManager &SourceMgr); @@ -44,6 +62,12 @@ public: /// \brief Destroy the fix-it rewriter. ~FixItRewriter(); + /// \brief Add a location where fix-it modifications should be + /// performed. + void addFixItLocation(RequestedSourceLocation Loc) { + FixItLocations.push_back(Loc); + } + /// \brief Write the modified source file. /// /// \returns true if there was an error, false otherwise. diff --git a/lib/Frontend/FixItRewriter.cpp b/lib/Frontend/FixItRewriter.cpp index 8883b91ab9..6fde5dae75 100644 --- a/lib/Frontend/FixItRewriter.cpp +++ b/lib/Frontend/FixItRewriter.cpp @@ -81,8 +81,43 @@ bool FixItRewriter::IncludeInDiagnosticCounts() const { void FixItRewriter::HandleDiagnostic(Diagnostic::Level DiagLevel, const DiagnosticInfo &Info) { - if (Client) - Client->HandleDiagnostic(DiagLevel, Info); + Client->HandleDiagnostic(DiagLevel, Info); + + // Skip over any diagnostics that are ignored. + if (DiagLevel == Diagnostic::Ignored) + return; + + if (!FixItLocations.empty()) { + // The user has specified the locations where we should perform + // the various fix-it modifications. + + // If this diagnostic does not have any code modifications, + // completely ignore it, even if it's an error: fix-it locations + // are meant to perform specific fix-ups even in the presence of + // other errors. + if (Info.getNumCodeModificationHints() == 0) + return; + + // See if the location of the error is one that matches what the + // user requested. + bool AcceptableLocation = false; + const FileEntry *File + = Rewrite.getSourceMgr().getFileEntryForID( + Info.getLocation().getFileID()); + unsigned Line = Info.getLocation().getSpellingLineNumber(); + unsigned Column = Info.getLocation().getSpellingColumnNumber(); + for (llvm::SmallVector<RequestedSourceLocation, 4>::iterator + Loc = FixItLocations.begin(), LocEnd = FixItLocations.end(); + Loc != LocEnd; ++Loc) { + if (Loc->File == File && Loc->Line == Line && Loc->Column == Column) { + AcceptableLocation = true; + break; + } + } + + if (!AcceptableLocation) + return; + } // Make sure that we can perform all of the modifications we // in this diagnostic. diff --git a/test/FixIt/fixit-at.c b/test/FixIt/fixit-at.c new file mode 100644 index 0000000000..eb33855365 --- /dev/null +++ b/test/FixIt/fixit-at.c @@ -0,0 +1,5 @@ +// RUN: clang-cc -fsyntax-only -fixit-at=fixit-at.c:3:1 %s -o - | clang-cc -verify -x c - + +_Complex cd; + +int i0 = { 17 }; // expected-warning{{braces}} diff --git a/tools/clang-cc/clang-cc.cpp b/tools/clang-cc/clang-cc.cpp index 19f42b442c..a71847af12 100644 --- a/tools/clang-cc/clang-cc.cpp +++ b/tools/clang-cc/clang-cc.cpp @@ -58,9 +58,94 @@ #include "llvm/System/Host.h" #include "llvm/System/Path.h" #include "llvm/System/Signals.h" +#include <cstdlib> + using namespace clang; //===----------------------------------------------------------------------===// +// Source Location Parser +//===----------------------------------------------------------------------===// + +/// \brief A source location that has been parsed on the command line. +struct ParsedSourceLocation { + std::string FileName; + unsigned Line; + unsigned Column; + + /// \brief Try to resolve the file name of a parsed source location. + /// + /// \returns true if there was an error, false otherwise. + bool ResolveLocation(FileManager &FileMgr, RequestedSourceLocation &Result); +}; + +bool +ParsedSourceLocation::ResolveLocation(FileManager &FileMgr, + RequestedSourceLocation &Result) { + const FileEntry *File = FileMgr.getFile(FileName); + if (!File) + return true; + + Result.File = File; + Result.Line = Line; + Result.Column = Column; + return false; +} + +namespace llvm { + namespace cl { + /// \brief Command-line option parser that parses source locations. + /// + /// Source locations are of the form filename:line:column. + template<> + class parser<ParsedSourceLocation> + : public basic_parser<ParsedSourceLocation> { + public: + bool parse(Option &O, const char *ArgName, + const std::string &ArgValue, + ParsedSourceLocation &Val); + }; + + bool + parser<ParsedSourceLocation>:: + parse(Option &O, const char *ArgName, const std::string &ArgValue, + ParsedSourceLocation &Val) { + using namespace clang; + + const char *ExpectedFormat + = "source location must be of the form filename:line:column"; + std::string::size_type SecondColon = ArgValue.rfind(':'); + if (SecondColon == std::string::npos) { + std::fprintf(stderr, "%s\n", ExpectedFormat); + return true; + } + char *EndPtr; + long Column + = std::strtol(ArgValue.c_str() + SecondColon + 1, &EndPtr, 10); + if (EndPtr != ArgValue.c_str() + ArgValue.size()) { + std::fprintf(stderr, "%s\n", ExpectedFormat); + return true; + } + + std::string::size_type FirstColon = ArgValue.rfind(':', SecondColon-1); + if (SecondColon == std::string::npos) { + std::fprintf(stderr, "%s\n", ExpectedFormat); + return true; + } + long Line = std::strtol(ArgValue.c_str() + FirstColon + 1, &EndPtr, 10); + if (EndPtr != ArgValue.c_str() + SecondColon) { + std::fprintf(stderr, "%s\n", ExpectedFormat); + return true; + } + + Val.FileName = ArgValue.substr(0, FirstColon); + Val.Line = Line; + Val.Column = Column; + return false; + } + } +} + +//===----------------------------------------------------------------------===// // Global options. //===----------------------------------------------------------------------===// @@ -1262,6 +1347,13 @@ static void InitializeCompileOptions(CompileOptions &Opts, } //===----------------------------------------------------------------------===// +// Fix-It Options +//===----------------------------------------------------------------------===// +static llvm::cl::list<ParsedSourceLocation> +FixItAtLocations("fixit-at", llvm::cl::value_desc("source-location"), + llvm::cl::desc("Perform Fix-It modifications at the given source location")); + +//===----------------------------------------------------------------------===// // Main driver //===----------------------------------------------------------------------===// @@ -1454,6 +1546,35 @@ static void ProcessInputFile(Preprocessor &PP, PreprocessorFactory &PPF, if (Consumer) { llvm::OwningPtr<ASTContext> ContextOwner; + if (FixItAtLocations.size() > 0) { + // Even without the "-fixit" flag, with may have some specific + // locations where the user has requested fixes. Process those + // locations now. + if (!FixItRewrite) + FixItRewrite = new FixItRewriter(PP.getDiagnostics(), + PP.getSourceManager()); + + bool AddedFixitLocation = false; + for (unsigned Idx = 0, Last = FixItAtLocations.size(); + Idx != Last; ++Idx) { + RequestedSourceLocation Requested; + if (FixItAtLocations[Idx].ResolveLocation(PP.getFileManager(), + Requested)) { + fprintf(stderr, "FIX-IT could not find file \"%s\"\n", + FixItAtLocations[Idx].FileName.c_str()); + } else { + FixItRewrite->addFixItLocation(Requested); + AddedFixitLocation = true; + } + } + + if (!AddedFixitLocation) { + // All of the fix-it locations were bad. Don't fix anything. + delete FixItRewrite; + FixItRewrite = 0; + } + } + ContextOwner.reset(new ASTContext(PP.getLangOptions(), PP.getSourceManager(), PP.getTargetInfo(), |