diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -2478,7 +2478,8 @@ private: FormatStyleSet StyleSet; - friend std::error_code parseConfiguration(StringRef Text, FormatStyle *Style); + friend std::error_code parseConfiguration(StringRef Text, FormatStyle *Style, + bool IgnoreUnknownOptions); }; /// Returns a format style complying with the LLVM coding standards: @@ -2533,7 +2534,11 @@ /// /// When ``BasedOnStyle`` is not present, options not present in the YAML /// document, are retained in \p Style. -std::error_code parseConfiguration(StringRef Text, FormatStyle *Style); +/// +/// If IgnoreUnknownOptions is true, no errors are emitted if unknown +/// format options are occured. +std::error_code parseConfiguration(StringRef Text, FormatStyle *Style, + bool IgnoreUnknownOptions = false); /// Gets configuration in a YAML string. std::string configurationAsText(const FormatStyle &Style); @@ -2670,6 +2675,8 @@ /// language if the filename isn't sufficient. /// \param[in] FS The underlying file system, in which the file resides. By /// default, the file system is the real file system. +/// \param[in] IgnoreUnknownOptions If true, unknown format options are ignored. +/// If false, errors are emitted on unknown format options. /// /// \returns FormatStyle as specified by ``StyleName``. If ``StyleName`` is /// "file" and no file is found, returns ``FallbackStyle``. If no style could be @@ -2677,7 +2684,8 @@ llvm::Expected getStyle(StringRef StyleName, StringRef FileName, StringRef FallbackStyle, StringRef Code = "", - llvm::vfs::FileSystem *FS = nullptr); + llvm::vfs::FileSystem *FS = nullptr, + bool IgnoreUnknownOptions = false); // Guesses the language from the ``FileName`` and ``Code`` to be formatted. // Defaults to FormatStyle::LK_Cpp. diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -1288,7 +1288,8 @@ return true; } -std::error_code parseConfiguration(StringRef Text, FormatStyle *Style) { +std::error_code parseConfiguration(StringRef Text, FormatStyle *Style, + bool IgnoreUnknownOptions) { assert(Style); FormatStyle::LanguageKind Language = Style->Language; assert(Language != FormatStyle::LK_None); @@ -1302,6 +1303,7 @@ // Mapping also uses the context to get the language to find the correct // base style. Input.setContext(Style); + Input.setIgnoreUnknown(IgnoreUnknownOptions); Input >> Styles; if (Input.error()) return Input.error(); @@ -2800,8 +2802,8 @@ llvm::Expected getStyle(StringRef StyleName, StringRef FileName, StringRef FallbackStyleName, - StringRef Code, - llvm::vfs::FileSystem *FS) { + StringRef Code, llvm::vfs::FileSystem *FS, + bool IgnoreUnknownOptions) { if (!FS) { FS = llvm::vfs::getRealFileSystem().get(); } @@ -2813,7 +2815,8 @@ if (StyleName.startswith("{")) { // Parse YAML/JSON style from the command line. - if (std::error_code ec = parseConfiguration(StyleName, &Style)) + if (std::error_code ec = + parseConfiguration(StyleName, &Style, IgnoreUnknownOptions)) return make_string_error("Error parsing -style: " + ec.message()); return Style; } @@ -2857,8 +2860,8 @@ FS->getBufferForFile(ConfigFile.str()); if (std::error_code EC = Text.getError()) return make_string_error(EC.message()); - if (std::error_code ec = - parseConfiguration(Text.get()->getBuffer(), &Style)) { + if (std::error_code ec = parseConfiguration( + Text.get()->getBuffer(), &Style, IgnoreUnknownOptions)) { if (ec == ParseError::Unsuitable) { if (!UnsuitableConfigFiles.empty()) UnsuitableConfigFiles.append(", "); diff --git a/clang/tools/clang-format/ClangFormat.cpp b/clang/tools/clang-format/ClangFormat.cpp --- a/clang/tools/clang-format/ClangFormat.cpp +++ b/clang/tools/clang-format/ClangFormat.cpp @@ -104,6 +104,11 @@ "SortIncludes style flag"), cl::cat(ClangFormatCategory)); +static cl::opt + IgnoreUnkownOptions("ignore-unknown-options", + cl::desc("If set, unknown format options are ignored."), + cl::init(false), cl::cat(ClangFormatCategory)); + static cl::opt Verbose("verbose", cl::desc("If set, shows the list of processed files"), cl::cat(ClangFormatCategory)); @@ -378,7 +383,8 @@ } llvm::Expected FormatStyle = - getStyle(Style, AssumedFileName, FallbackStyle, Code->getBuffer()); + getStyle(Style, AssumedFileName, FallbackStyle, Code->getBuffer(), + nullptr, IgnoreUnkownOptions.getValue()); if (!FormatStyle) { llvm::errs() << llvm::toString(FormatStyle.takeError()) << "\n"; return true; diff --git a/llvm/include/llvm/Support/YAMLTraits.h b/llvm/include/llvm/Support/YAMLTraits.h --- a/llvm/include/llvm/Support/YAMLTraits.h +++ b/llvm/include/llvm/Support/YAMLTraits.h @@ -789,6 +789,7 @@ virtual NodeKind getNodeKind() = 0; virtual void setError(const Twine &) = 0; + virtual void setIgnoreUnknown(bool) = 0; template void enumCase(T &Val, const char* Str, const T ConstVal) { @@ -1504,6 +1505,8 @@ /// Returns the current node that's being parsed by the YAML Parser. const Node *getCurrentNode() const; + void setIgnoreUnknown(bool) override; + private: SourceMgr SrcMgr; // must be before Strm std::unique_ptr Strm; @@ -1514,6 +1517,7 @@ std::vector BitValuesUsed; HNode *CurrentNode = nullptr; bool ScalarMatchFound = false; + bool IgnoreUnkown = false; }; /// @@ -1561,6 +1565,7 @@ void scalarTag(std::string &) override; NodeKind getNodeKind() override; void setError(const Twine &message) override; + void setIgnoreUnknown(bool) override; bool canElideEmptySequence() override; // These are only used by operator<<. They could be private diff --git a/llvm/lib/Support/YAMLTraits.cpp b/llvm/lib/Support/YAMLTraits.cpp --- a/llvm/lib/Support/YAMLTraits.cpp +++ b/llvm/lib/Support/YAMLTraits.cpp @@ -195,6 +195,8 @@ MapHNode *MN = dyn_cast_or_null(CurrentNode); if (!MN) return; + if (IgnoreUnkown) + return; for (const auto &NN : MN->Mapping) { if (!is_contained(MN->ValidKeys, NN.first())) { setError(NN.second.get(), Twine("unknown key '") + NN.first() + "'"); @@ -428,6 +430,8 @@ setError(CurrentNode, Message); } +void Input::setIgnoreUnknown(bool Value) { IgnoreUnkown = Value; } + bool Input::canElideEmptySequence() { return false; } @@ -735,6 +739,8 @@ void Output::setError(const Twine &message) { } +void Output::setIgnoreUnknown(bool Value) {} + bool Output::canElideEmptySequence() { // Normally, with an optional key/value where the value is an empty sequence, // the whole key/value can be not written. But, that produces wrong yaml