Index: clang/tools/clang-format/CMakeLists.txt =================================================================== --- clang/tools/clang-format/CMakeLists.txt +++ clang/tools/clang-format/CMakeLists.txt @@ -7,6 +7,7 @@ set(CLANG_FORMAT_LIB_DEPS clangBasic clangFormat + clangFrontend clangRewrite clangToolingCore ) Index: clang/tools/clang-format/ClangFormat.cpp =================================================================== --- clang/tools/clang-format/ClangFormat.cpp +++ clang/tools/clang-format/ClangFormat.cpp @@ -18,6 +18,7 @@ #include "clang/Basic/SourceManager.h" #include "clang/Basic/Version.h" #include "clang/Format/Format.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" #include "clang/Rewrite/Core/Rewriter.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" @@ -107,6 +108,54 @@ Verbose("verbose", cl::desc("If set, shows the list of processed files"), cl::cat(ClangFormatCategory)); +// Use --dry-run to match other LLVM tools when you mean do it but don't +// actually do it +static cl::opt + DryRun("dry-run", + cl::desc("If set, do not actually make the formatting changes"), + cl::cat(ClangFormatCategory)); + +// Use -n as a common command as an alias for --dry-run. (git and make use -n) +static cl::alias DryRunShort("n", cl::desc("Alias for --dry-run"), + cl::cat(ClangFormatCategory), cl::aliasopt(DryRun), + cl::NotHidden); + +// Emulate being able to turn on/off the warning. +static cl::opt + WarnFormat("Wclang-format-violations", + cl::desc("Warnings about individual formatting changes needed. " + "Used only with --dry-run or -n"), + cl::init(true), cl::cat(ClangFormatCategory), cl::Hidden); + +static cl::opt + NoWarnFormat("Wno-clang-format-violations", + cl::desc("Do not warn about individual formatting changes " + "needed. Used only with --dry-run or -n"), + cl::init(false), cl::cat(ClangFormatCategory), cl::Hidden); + +static cl::opt ErrorLimit( + "ferror-limit", + cl::desc("Set the maximum number of clang-format errors to emit before " + "stopping (0 = no limit). Used only with --dry-run or -n"), + cl::init(0), cl::cat(ClangFormatCategory)); + +static cl::opt + WarningsAsErrors("Werror", + cl::desc("If set, changes formatting warnings to errors"), + cl::cat(ClangFormatCategory)); + +static cl::opt + ShowColors("fcolor-diagnostics", + cl::desc("If set, and on a color-capable terminal controls " + "whether or not to print diagnostics in color"), + cl::init(true), cl::cat(ClangFormatCategory), cl::Hidden); + +static cl::opt + NoShowColors("fno-color-diagnostics", + cl::desc("If set, and on a color-capable terminal controls " + "whether or not to print diagnostics in color"), + cl::init(false), cl::cat(ClangFormatCategory), cl::Hidden); + static cl::list FileNames(cl::Positional, cl::desc("[ ...]"), cl::cat(ClangFormatCategory)); @@ -313,23 +362,65 @@ // Get new affected ranges after sorting `#includes`. Ranges = tooling::calculateRangesAfterReplacements(Replaces, Ranges); FormattingAttemptStatus Status; - Replacements FormatChanges = reformat(*FormatStyle, *ChangedCode, Ranges, - AssumedFileName, &Status); + Replacements FormatChanges = + reformat(*FormatStyle, *ChangedCode, Ranges, AssumedFileName, &Status); Replaces = Replaces.merge(FormatChanges); - if (OutputXML) { - outs() << "\n\n"; - if (Cursor.getNumOccurrences() != 0) - outs() << "" - << FormatChanges.getShiftedCodePosition(CursorPosition) - << "\n"; - - outputReplacementsXML(Replaces); - outs() << "\n"; + if (OutputXML || DryRun) { + if (DryRun) { + if (!Replaces.empty()) { + IntrusiveRefCntPtr DiagOpts = + new DiagnosticOptions(); + DiagOpts->ShowColors = (ShowColors && !NoShowColors); + + TextDiagnosticPrinter *DiagsBuffer = + new TextDiagnosticPrinter(llvm::errs(), &*DiagOpts, false); + + IntrusiveRefCntPtr DiagID(new DiagnosticIDs()); + IntrusiveRefCntPtr Diags( + new DiagnosticsEngine(DiagID, &*DiagOpts, DiagsBuffer)); + + IntrusiveRefCntPtr InMemoryFileSystem( + new llvm::vfs::InMemoryFileSystem); + FileManager Files(FileSystemOptions(), InMemoryFileSystem); + SourceManager Sources(*Diags, Files); + FileID FileID = createInMemoryFile(AssumedFileName, Code.get(), Sources, + Files, InMemoryFileSystem.get()); + + const unsigned ID = Diags->getCustomDiagID( + WarningsAsErrors ? clang::DiagnosticsEngine::Error + : clang::DiagnosticsEngine::Warning, + "code should be clang-formatted [-Wclang-format-violations]"); + + unsigned Errors = 0; + DiagsBuffer->BeginSourceFile(LangOptions(), nullptr); + if (WarnFormat && !NoWarnFormat) { + for (const auto &R : Replaces) { + Diags->Report(Sources.getLocForStartOfFile(FileID).getLocWithOffset( + R.getOffset()), + ID); + Errors++; + if (ErrorLimit && Errors >= ErrorLimit) + break; + } + } + DiagsBuffer->EndSourceFile(); + } + return true; + } else { + outs() << "\n\n"; + if (Cursor.getNumOccurrences() != 0) + outs() << "" + << FormatChanges.getShiftedCodePosition(CursorPosition) + << "\n"; + + outputReplacementsXML(Replaces); + outs() << "\n"; + } } else { IntrusiveRefCntPtr InMemoryFileSystem( new llvm::vfs::InMemoryFileSystem);