Index: clang-tools-extra/clang-tidy/ClangTidyOptions.h =================================================================== --- clang-tools-extra/clang-tidy/ClangTidyOptions.h +++ clang-tools-extra/clang-tidy/ClangTidyOptions.h @@ -64,6 +64,9 @@ /// Checks filter. llvm::Optional Checks; + /// Clang-tidy-config + llvm::Optional ClangTidyConfig; + /// WarningsAsErrors filter. llvm::Optional WarningsAsErrors; @@ -227,6 +230,8 @@ /// Try to read configuration files from \p Directory using registered /// \c ConfigHandlers. llvm::Optional tryReadConfigFile(llvm::StringRef Directory); + llvm::Optional tryReadConfigFile(llvm::StringRef Path, + bool IsFile); llvm::StringMap CachedOptions; ClangTidyOptions OverrideOptions; Index: clang-tools-extra/clang-tidy/ClangTidyOptions.cpp =================================================================== --- clang-tools-extra/clang-tidy/ClangTidyOptions.cpp +++ clang-tools-extra/clang-tidy/ClangTidyOptions.cpp @@ -109,6 +109,7 @@ ClangTidyOptions ClangTidyOptions::getDefaults() { ClangTidyOptions Options; Options.Checks = ""; + Options.ClangTidyConfig = ""; Options.WarningsAsErrors = ""; Options.HeaderFilterRegex = ""; Options.SystemHeaders = false; @@ -306,7 +307,41 @@ << "...\n"); assert(FS && "FS must be set."); - llvm::SmallString<128> AbsoluteFilePath(FileName); + llvm::SmallString<1024> AbsoluteFilePath(FileName); + if (!OverrideOptions.ClangTidyConfig.getValue().empty()) { + AbsoluteFilePath.assign(OverrideOptions.ClangTidyConfig.getValue()); + if (FS->makeAbsolute(AbsoluteFilePath)) { + std::string Msg; + llvm::raw_string_ostream ErrStream(Msg); + ErrStream << " reading configuration from <" << AbsoluteFilePath + << "> can't make absolute path.\n"; + llvm::report_fatal_error(ErrStream.str()); + } + bool IsFile = false; + bool IsLink = false; + llvm::sys::fs::is_regular_file(Twine(AbsoluteFilePath), IsFile); + llvm::sys::fs::is_symlink_file(Twine(AbsoluteFilePath), IsLink); + if (!(IsFile || IsLink)) { + std::string Msg; + llvm::raw_string_ostream ErrStream(Msg); + ErrStream << " reading configuration from <" << AbsoluteFilePath + << "> file doesn't exist or not regular/symlink file.\n"; + llvm::report_fatal_error(ErrStream.str()); + } + + std::vector RawOptions = + DefaultOptionsProvider::getRawOptions(AbsoluteFilePath.str()); + OptionsSource CommandLineOptions(OverrideOptions, + OptionsSourceTypeCheckCommandLineOption); + + llvm::Optional Result; + Result = tryReadConfigFile(AbsoluteFilePath, true); + if (Result) { + RawOptions.push_back(*Result); + } + RawOptions.push_back(CommandLineOptions); + return RawOptions; + } if (FS->makeAbsolute(AbsoluteFilePath)) return {}; @@ -368,6 +403,47 @@ return llvm::None; } +llvm::Optional +FileOptionsProvider::tryReadConfigFile(StringRef Path, bool IsFile) { + // llvm::outs() << "tryReadConfigFile IsFile<" << + // OverrideOptions.ClangTidyConfig << ">\n"; + assert(!Path.empty()); + + if (!IsFile) { + tryReadConfigFile(Path); + } + + llvm::ErrorOr> Text = + llvm::MemoryBuffer::getFile(Path); + if (std::error_code EC = Text.getError()) { + std::string Msg; + llvm::raw_string_ostream ErrStream(Msg); + ErrStream << " Can't read <" << Path << ">: " << EC.message() << "\n"; + llvm::report_fatal_error(ErrStream.str()); + } + + // Skip empty files, e.g. files opened for writing via shell output + // redirection. + if ((*Text)->getBuffer().empty()) { + std::string Msg; + llvm::raw_string_ostream ErrStream(Msg); + ErrStream << " empty configuration file <" << Path << ">.\n"; + llvm::report_fatal_error(ErrStream.str()); + } + llvm::ErrorOr ParsedOptions = + parseConfiguration((*Text)->getBuffer()); + if (!ParsedOptions) { + if (ParsedOptions.getError()) { + std::string Msg; + llvm::raw_string_ostream ErrStream(Msg); + ErrStream << " parsing <" << Path + << ">: " << ParsedOptions.getError().message() << "\n"; + llvm::report_fatal_error(ErrStream.str()); + } + } + return OptionsSource(*ParsedOptions, Path); +} + /// Parses -line-filter option and stores it to the \c Options. std::error_code parseLineFilter(StringRef LineFilter, clang::tidy::ClangTidyGlobalOptions &Options) { Index: clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp =================================================================== --- clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp +++ clang-tools-extra/clang-tidy/tool/ClangTidyMain.cpp @@ -73,6 +73,13 @@ )"), cl::init(""), cl::cat(ClangTidyCategory)); +static cl::opt ClangTidyConfig("clang-tidy-config", cl::desc(R"( +Specify full path of .clang-tidy config file. +For example, --clang-tidy-config=/some/path/myTidyConfig +)"), + cl::init(""), + cl::cat(ClangTidyCategory)); + static cl::opt WarningsAsErrors("warnings-as-errors", cl::desc(R"( Upgrades warnings to errors. Same format as '-checks'. @@ -279,6 +286,7 @@ ClangTidyOptions DefaultOptions; DefaultOptions.Checks = DefaultChecks; + DefaultOptions.ClangTidyConfig = ""; DefaultOptions.WarningsAsErrors = ""; DefaultOptions.HeaderFilterRegex = HeaderFilter; DefaultOptions.SystemHeaders = SystemHeaders; @@ -291,6 +299,8 @@ ClangTidyOptions OverrideOptions; if (Checks.getNumOccurrences() > 0) OverrideOptions.Checks = Checks; + if (ClangTidyConfig.getNumOccurrences() > 0) + OverrideOptions.ClangTidyConfig = ClangTidyConfig; if (WarningsAsErrors.getNumOccurrences() > 0) OverrideOptions.WarningsAsErrors = WarningsAsErrors; if (HeaderFilter.getNumOccurrences() > 0)