diff --git a/clang-tools-extra/clang-modernize/Core/IncludeExcludeInfo.cpp b/clang-tools-extra/clang-modernize/Core/IncludeExcludeInfo.cpp index e3f07c5f0d3e..358522f95895 100644 --- a/clang-tools-extra/clang-modernize/Core/IncludeExcludeInfo.cpp +++ b/clang-tools-extra/clang-modernize/Core/IncludeExcludeInfo.cpp @@ -157,13 +157,17 @@ bool IncludeExcludeInfo::isFileIncluded(StringRef FilePath) const { if (!InIncludeList) return false; + // If the file is in the included list but not is not explicitly excluded, + // then it is safe to transform. + return !isFileExplicitlyExcluded(FilePath); +} + +bool IncludeExcludeInfo::isFileExplicitlyExcluded(StringRef FilePath) const { for (std::vector::const_iterator I = ExcludeList.begin(), E = ExcludeList.end(); - I != E; ++I) + I != E; ++I) if (fileHasPathPrefix(FilePath, *I)) - return false; + return true; - // If the file is in the included list but not in the excluded list, then - // it is safe to transform. - return true; + return false; } diff --git a/clang-tools-extra/clang-modernize/Core/IncludeExcludeInfo.h b/clang-tools-extra/clang-modernize/Core/IncludeExcludeInfo.h index 3f31e1ee2c89..9e919920f9a9 100644 --- a/clang-tools-extra/clang-modernize/Core/IncludeExcludeInfo.h +++ b/clang-tools-extra/clang-modernize/Core/IncludeExcludeInfo.h @@ -48,6 +48,12 @@ public: /// operators were removed. bool isFileIncluded(llvm::StringRef FilePath) const; + /// \brief Determine if a file path was explicitly excluded. + bool isFileExplicitlyExcluded(llvm::StringRef FilePath) const; + + /// \brief Determine if a list of include paths was provided. + bool isIncludeListEmpty() const { return IncludeList.empty(); } + private: std::vector IncludeList; std::vector ExcludeList; diff --git a/clang-tools-extra/clang-modernize/Core/Transform.cpp b/clang-tools-extra/clang-modernize/Core/Transform.cpp index 7b92d14dc77c..a61f101ddc20 100644 --- a/clang-tools-extra/clang-modernize/Core/Transform.cpp +++ b/clang-tools-extra/clang-modernize/Core/Transform.cpp @@ -91,7 +91,7 @@ bool Transform::isFileModifiable(const SourceManager &SM, if (!FE) return false; - return GlobalOptions.ModifiableHeaders.isFileIncluded(FE->getName()); + return GlobalOptions.ModifiableFiles.isFileIncluded(FE->getName()); } bool Transform::handleBeginSource(CompilerInstance &CI, StringRef Filename) { diff --git a/clang-tools-extra/clang-modernize/Core/Transform.h b/clang-tools-extra/clang-modernize/Core/Transform.h index 281262a7503d..e74c77f73e60 100644 --- a/clang-tools-extra/clang-modernize/Core/Transform.h +++ b/clang-tools-extra/clang-modernize/Core/Transform.h @@ -66,9 +66,9 @@ struct TransformOptions { /// \brief Enable the use of performance timers. bool EnableTiming; - /// \brief Contains information on which headers are safe to transform and + /// \brief Contains information on which files are safe to transform and /// which aren't. - IncludeExcludeInfo ModifiableHeaders; + IncludeExcludeInfo ModifiableFiles; /// \brief Maximum allowed level of risk. RiskLevel MaxRiskLevel; diff --git a/clang-tools-extra/clang-modernize/tool/ClangModernize.cpp b/clang-tools-extra/clang-modernize/tool/ClangModernize.cpp index f2007e9ad652..7ada0399ef2d 100644 --- a/clang-tools-extra/clang-modernize/tool/ClangModernize.cpp +++ b/clang-tools-extra/clang-modernize/tool/ClangModernize.cpp @@ -44,7 +44,7 @@ static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage); static cl::opt BuildPath( "p", cl::desc("Build Path"), cl::Optional); static cl::list SourcePaths( - cl::Positional, cl::desc(" [... ]"), cl::OneOrMore); + cl::Positional, cl::desc("[...]"), cl::ZeroOrMore); static cl::extrahelp MoreHelp( "EXAMPLES:\n\n" "Apply all transforms on a given file, no compilation database:\n\n" @@ -281,6 +281,42 @@ bool serializeReplacements(const replace::TUReplacements &Replacements) { return !Errors; } +CompilationDatabase *autoDetectCompilations(std::string &ErrorMessage) { + // Auto-detect a compilation database from BuildPath. + if (BuildPath.getNumOccurrences() > 0) + return CompilationDatabase::autoDetectFromDirectory(BuildPath, + ErrorMessage); + // Try to auto-detect a compilation database from the first source. + if (!SourcePaths.empty()) { + if (CompilationDatabase *Compilations = + CompilationDatabase::autoDetectFromSource(SourcePaths[0], + ErrorMessage)) + return Compilations; + // If no compilation database can be detected from source then we create a + // fixed compilation database with c++11 support. + std::string CommandLine[] = { "-std=c++11" }; + return new FixedCompilationDatabase(".", CommandLine); + } + + ErrorMessage = "Could not determine sources to transform"; + return 0; +} + +// Predicate definition for determining whether a file is not included. +static bool isFileNotIncludedPredicate(llvm::StringRef FilePath) { + return !GlobalOptions.ModifiableFiles.isFileIncluded(FilePath); +} + +// Predicate definition for determining if a file was explicitly excluded. +static bool isFileExplicitlyExcludedPredicate(llvm::StringRef FilePath) { + if (GlobalOptions.ModifiableFiles.isFileExplicitlyExcluded(FilePath)) { + llvm::errs() << "Warning \"" << FilePath << "\" will not be transformed " + << "because it's in the excluded list.\n"; + return true; + } + return false; +} + int main(int argc, const char **argv) { llvm::sys::PrintStackTraceOnErrorSignal(); Transforms TransformManager; @@ -292,23 +328,45 @@ int main(int argc, const char **argv) { FixedCompilationDatabase::loadFromCommandLine(argc, argv)); cl::ParseCommandLineOptions(argc, argv); + // Populate the ModifiableFiles structure. + GlobalOptions.ModifiableFiles.readListFromString(IncludePaths, ExcludePaths); + GlobalOptions.ModifiableFiles.readListFromFile(IncludeFromFile, + ExcludeFromFile); + if (!Compilations) { std::string ErrorMessage; - if (BuildPath.getNumOccurrences() > 0) { - Compilations.reset(CompilationDatabase::autoDetectFromDirectory( - BuildPath, ErrorMessage)); - } else { - Compilations.reset(CompilationDatabase::autoDetectFromSource( - SourcePaths[0], ErrorMessage)); - // If no compilation database can be detected from source then we create - // a new FixedCompilationDatabase with c++11 support. - if (!Compilations) { - std::string CommandLine[] = {"-std=c++11"}; - Compilations.reset(new FixedCompilationDatabase(".", CommandLine)); - } + Compilations.reset(autoDetectCompilations(ErrorMessage)); + if (!Compilations) { + llvm::errs() << llvm::sys::path::filename(argv[0]) << ": " << ErrorMessage + << "\n"; + return 1; } - if (!Compilations) - llvm::report_fatal_error(ErrorMessage); + } + + // Populate source files. + std::vector Sources; + if (!SourcePaths.empty()) { + // Use only files that are not explicitly excluded. + std::remove_copy_if(SourcePaths.begin(), SourcePaths.end(), + std::back_inserter(Sources), + isFileExplicitlyExcludedPredicate); + } else { + if (GlobalOptions.ModifiableFiles.isIncludeListEmpty()) { + llvm::errs() << llvm::sys::path::filename(argv[0]) + << ": Use -include to indicate which files of " + << "the compilatiion database to transform.\n"; + return 1; + } + // Use source paths from the compilation database. + // We only transform files that are explicitly included. + Sources = Compilations->getAllFiles(); + std::remove_if(Sources.begin(), Sources.end(), isFileNotIncludedPredicate); + } + + if (Sources.empty()) { + llvm::errs() << llvm::sys::path::filename(argv[0]) + << ": Could not determine sources to transform.\n"; + return 1; } // Since ExecutionTimeDirectoryName could be an empty string we compare @@ -325,12 +383,6 @@ int main(int argc, const char **argv) { if (CmdSwitchError) return 1; - // Populate the ModifiableHeaders structure. - GlobalOptions.ModifiableHeaders - .readListFromString(IncludePaths, ExcludePaths); - GlobalOptions.ModifiableHeaders - .readListFromFile(IncludeFromFile, ExcludeFromFile); - TransformManager.createSelectedTransforms(GlobalOptions, RequiredVersions); llvm::IntrusiveRefCntPtr DiagOpts( @@ -344,9 +396,10 @@ int main(int argc, const char **argv) { if (TransformManager.begin() == TransformManager.end()) { if (SupportedCompilers.empty()) - llvm::errs() << argv[0] << ": no selected transforms\n"; + llvm::errs() << llvm::sys::path::filename(argv[0]) + << ": no selected transforms\n"; else - llvm::errs() << argv[0] + llvm::errs() << llvm::sys::path::filename(argv[0]) << ": no transforms available for specified compilers\n"; return 1; } @@ -372,7 +425,7 @@ int main(int argc, const char **argv) { I != E; ++I) { Transform *T = *I; - if (T->apply(FileStates, *Compilations, SourcePaths) != 0) { + if (T->apply(FileStates, *Compilations, Sources) != 0) { // FIXME: Improve ClangTool to not abort if just one file fails. return 1; } @@ -441,12 +494,12 @@ int main(int argc, const char **argv) { // replacements. Otherwise reformat changes if reformatting is enabled. if (!SerializeReplacements) { if (ChangesReformatter) - reformat(*ChangesReformatter, FileStates, Diagnostics); + reformat(*ChangesReformatter, FileStates, Diagnostics); FileStates.writeToDisk(Diagnostics); } if (FinalSyntaxCheck) - if (!doSyntaxCheck(*Compilations, SourcePaths, FileStates)) + if (!doSyntaxCheck(*Compilations, Sources, FileStates)) return 1; // Report execution times. diff --git a/clang-tools-extra/docs/ModernizerUsage.rst b/clang-tools-extra/docs/ModernizerUsage.rst index d2c7b55f392d..bc09eec0af76 100644 --- a/clang-tools-extra/docs/ModernizerUsage.rst +++ b/clang-tools-extra/docs/ModernizerUsage.rst @@ -2,7 +2,7 @@ clang-modernize Usage ===================== -``clang-modernize [options] [... ] [-- [args]]`` +``clang-modernize [options] [...] [-- [args]]`` ```` specifies the path to the source to migrate. This path may be relative to the current directory. @@ -40,6 +40,16 @@ General Command Line Options This option is ignored if ``--`` is present. + Files in the compilation database that can be transformed if no sources are + provided and file paths are explicitly included using ``-include`` or + ``-include-from``. + In order to transform all files in a compilation database the following + command line can be used: + + ``clang-modernize -p= -include=`` + + Use ``-exclude`` or ``-exclude-from`` to limit the scope of ``-include``. + .. option:: -- [args] Another way to provide compiler arguments is to specify all arguments on the diff --git a/clang-tools-extra/unittests/clang-modernize/TransformTest.cpp b/clang-tools-extra/unittests/clang-modernize/TransformTest.cpp index 654658846ab1..b69d27c70120 100644 --- a/clang-tools-extra/unittests/clang-modernize/TransformTest.cpp +++ b/clang-tools-extra/unittests/clang-modernize/TransformTest.cpp @@ -264,7 +264,7 @@ TEST(Transform, isFileModifiable) { StringRef ExcludeDir = llvm::sys::path::parent_path(HeaderBFile); IncludeExcludeInfo IncInfo; - Options.ModifiableHeaders.readListFromString(CurrentDir, ExcludeDir); + Options.ModifiableFiles.readListFromString(CurrentDir, ExcludeDir); tooling::FixedCompilationDatabase Compilations(CurrentDir.str(), std::vector());