[llvm] f64903f - Add -Wno-error=unknown flag to clang-format.

Joachim Meyer via llvm-commits llvm-commits at lists.llvm.org
Sat Sep 19 01:18:28 PDT 2020


Author: Joachim Meyer
Date: 2020-09-19T10:17:57+02:00
New Revision: f64903fd81764f1fde7aeb00eea5e1d488458f63

URL: https://github.com/llvm/llvm-project/commit/f64903fd81764f1fde7aeb00eea5e1d488458f63
DIFF: https://github.com/llvm/llvm-project/commit/f64903fd81764f1fde7aeb00eea5e1d488458f63.diff

LOG: Add -Wno-error=unknown flag to clang-format.

Currently newer clang-format options cannot be included in .clang-format files, if not all users can be forced to use an updated version.
This patch tries to solve this by adding an option to clang-format, enabling to ignore unknown (newer) options.

Differential Revision: https://reviews.llvm.org/D86137

Added: 
    

Modified: 
    clang/docs/ClangFormat.rst
    clang/include/clang/Format/Format.h
    clang/lib/Format/Format.cpp
    clang/tools/clang-format/ClangFormat.cpp
    clang/unittests/Format/FormatTest.cpp
    llvm/include/llvm/Support/YAMLParser.h
    llvm/include/llvm/Support/YAMLTraits.h
    llvm/lib/Support/YAMLParser.cpp
    llvm/lib/Support/YAMLTraits.cpp
    llvm/unittests/ObjectYAML/YAMLTest.cpp

Removed: 
    


################################################################################
diff  --git a/clang/docs/ClangFormat.rst b/clang/docs/ClangFormat.rst
index b09f48b0027b..d396667ce10c 100644
--- a/clang/docs/ClangFormat.rst
+++ b/clang/docs/ClangFormat.rst
@@ -31,6 +31,12 @@ to format C/C++/Java/JavaScript/Objective-C/Protobuf/C# code.
   Clang-format options:
 
     --Werror                   - If set, changes formatting warnings to errors
+    --Wno-error=unknown        - If set, unknown format options are only warned about.
+                                 This can be used to enable formatting, even if the
+                                 configuration contains unknown (newer) options.
+                                 Use with caution, as this might lead to dramatically
+                                 
diff ering format depending on an option being
+                                 supported or not.
     --assume-filename=<string> - Override filename used to determine the language.
                                  When reading from stdin, clang-format assumes this
                                  filename to determine the language.

diff  --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h
index 2840a6846018..40d5184f4d0d 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -2523,7 +2523,8 @@ struct FormatStyle {
 private:
   FormatStyleSet StyleSet;
 
-  friend std::error_code parseConfiguration(StringRef Text, FormatStyle *Style);
+  friend std::error_code parseConfiguration(StringRef Text, FormatStyle *Style,
+                                            bool AllowUnknownOptions);
 };
 
 /// Returns a format style complying with the LLVM coding standards:
@@ -2578,7 +2579,11 @@ bool getPredefinedStyle(StringRef Name, FormatStyle::LanguageKind Language,
 ///
 /// 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 AllowUnknownOptions is true, no errors are emitted if unknown
+/// format options are occured.
+std::error_code parseConfiguration(StringRef Text, FormatStyle *Style,
+                                   bool AllowUnknownOptions = false);
 
 /// Gets configuration in a YAML string.
 std::string configurationAsText(const FormatStyle &Style);
@@ -2715,6 +2720,9 @@ extern const char *DefaultFallbackStyle;
 /// 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] AllowUnknownOptions If true, unknown format options only
+///             emit a warning. 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
@@ -2722,7 +2730,8 @@ extern const char *DefaultFallbackStyle;
 llvm::Expected<FormatStyle> getStyle(StringRef StyleName, StringRef FileName,
                                      StringRef FallbackStyle,
                                      StringRef Code = "",
-                                     llvm::vfs::FileSystem *FS = nullptr);
+                                     llvm::vfs::FileSystem *FS = nullptr,
+                                     bool AllowUnknownOptions = 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
index 03188b46099d..8e4f65ace3f0 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -1301,7 +1301,8 @@ bool getPredefinedStyle(StringRef Name, FormatStyle::LanguageKind Language,
   return true;
 }
 
-std::error_code parseConfiguration(StringRef Text, FormatStyle *Style) {
+std::error_code parseConfiguration(StringRef Text, FormatStyle *Style,
+                                   bool AllowUnknownOptions) {
   assert(Style);
   FormatStyle::LanguageKind Language = Style->Language;
   assert(Language != FormatStyle::LK_None);
@@ -1315,6 +1316,7 @@ std::error_code parseConfiguration(StringRef Text, FormatStyle *Style) {
   // Mapping also uses the context to get the language to find the correct
   // base style.
   Input.setContext(Style);
+  Input.setAllowUnknownKeys(AllowUnknownOptions);
   Input >> Styles;
   if (Input.error())
     return Input.error();
@@ -2818,8 +2820,8 @@ const char *DefaultFallbackStyle = "LLVM";
 
 llvm::Expected<FormatStyle> getStyle(StringRef StyleName, StringRef FileName,
                                      StringRef FallbackStyleName,
-                                     StringRef Code,
-                                     llvm::vfs::FileSystem *FS) {
+                                     StringRef Code, llvm::vfs::FileSystem *FS,
+                                     bool AllowUnknownOptions) {
   if (!FS) {
     FS = llvm::vfs::getRealFileSystem().get();
   }
@@ -2831,7 +2833,8 @@ llvm::Expected<FormatStyle> getStyle(StringRef StyleName, StringRef FileName,
 
   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, AllowUnknownOptions))
       return make_string_error("Error parsing -style: " + ec.message());
     return Style;
   }
@@ -2875,8 +2878,8 @@ llvm::Expected<FormatStyle> getStyle(StringRef StyleName, StringRef FileName,
             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, AllowUnknownOptions)) {
           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
index aa40bab52df5..8dd55d99e2a0 100644
--- a/clang/tools/clang-format/ClangFormat.cpp
+++ b/clang/tools/clang-format/ClangFormat.cpp
@@ -104,6 +104,18 @@ static cl::opt<bool> SortIncludes(
              "SortIncludes style flag"),
     cl::cat(ClangFormatCategory));
 
+// using the full param name as Wno-error probably won't be a common use case in
+// clang-format
+static cl::opt<bool> AllowUnknownOptions(
+    "Wno-error=unknown",
+    cl::desc("If set, unknown format options are only warned about.\n"
+             "This can be used to enable formatting, even if the\n"
+             "configuration contains unknown (newer) options.\n"
+             "Use with caution, as this might lead to dramatically\n"
+             "
diff ering format depending on an option being\n"
+             "supported or not."),
+    cl::init(false), cl::cat(ClangFormatCategory));
+
 static cl::opt<bool>
     Verbose("verbose", cl::desc("If set, shows the list of processed files"),
             cl::cat(ClangFormatCategory));
@@ -378,7 +390,8 @@ static bool format(StringRef FileName) {
   }
 
   llvm::Expected<FormatStyle> FormatStyle =
-      getStyle(Style, AssumedFileName, FallbackStyle, Code->getBuffer());
+      getStyle(Style, AssumedFileName, FallbackStyle, Code->getBuffer(),
+               nullptr, AllowUnknownOptions.getValue());
   if (!FormatStyle) {
     llvm::errs() << llvm::toString(FormatStyle.takeError()) << "\n";
     return true;

diff  --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index eb72a01d3d27..27e76d40d125 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -16315,9 +16315,12 @@ TEST(FormatStyle, GetStyleOfFile) {
                                                   "InvalidKey: InvalidValue")));
   ASSERT_TRUE(
       FS.addFile("/d/test.cpp", 0, llvm::MemoryBuffer::getMemBuffer("int i;")));
-  auto Style7 = getStyle("file", "/d/.clang-format", "LLVM", "", &FS);
-  ASSERT_FALSE((bool)Style7);
-  llvm::consumeError(Style7.takeError());
+  auto Style7a = getStyle("file", "/d/.clang-format", "LLVM", "", &FS);
+  ASSERT_FALSE((bool)Style7a);
+  llvm::consumeError(Style7a.takeError());
+
+  auto Style7b = getStyle("file", "/d/.clang-format", "LLVM", "", &FS, true);
+  ASSERT_TRUE((bool)Style7b);
 
   // Test 8: inferred per-language defaults apply.
   auto StyleTd = getStyle("file", "x.td", "llvm", "", &FS);

diff  --git a/llvm/include/llvm/Support/YAMLParser.h b/llvm/include/llvm/Support/YAMLParser.h
index 44daf7850904..7baf69eebac9 100644
--- a/llvm/include/llvm/Support/YAMLParser.h
+++ b/llvm/include/llvm/Support/YAMLParser.h
@@ -40,6 +40,7 @@
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Allocator.h"
 #include "llvm/Support/SMLoc.h"
+#include "llvm/Support/SourceMgr.h"
 #include <cassert>
 #include <cstddef>
 #include <iterator>
@@ -51,7 +52,6 @@
 namespace llvm {
 
 class MemoryBufferRef;
-class SourceMgr;
 class raw_ostream;
 class Twine;
 
@@ -100,7 +100,8 @@ class Stream {
     return !failed();
   }
 
-  void printError(Node *N, const Twine &Msg);
+  void printError(Node *N, const Twine &Msg,
+                  SourceMgr::DiagKind Kind = SourceMgr::DK_Error);
 
 private:
   friend class Document;

diff  --git a/llvm/include/llvm/Support/YAMLTraits.h b/llvm/include/llvm/Support/YAMLTraits.h
index acb1d61cf569..f32a6ca92d29 100644
--- a/llvm/include/llvm/Support/YAMLTraits.h
+++ b/llvm/include/llvm/Support/YAMLTraits.h
@@ -789,6 +789,7 @@ class IO {
   virtual NodeKind getNodeKind() = 0;
 
   virtual void setError(const Twine &) = 0;
+  virtual void setAllowUnknownKeys(bool Allow);
 
   template <typename T>
   void enumCase(T &Val, const char* Str, const T ConstVal) {
@@ -1495,6 +1496,9 @@ class Input : public IO {
   void setError(HNode *hnode, const Twine &message);
   void setError(Node *node, const Twine &message);
 
+  void reportWarning(HNode *hnode, const Twine &message);
+  void reportWarning(Node *hnode, const Twine &message);
+
 public:
   // These are only used by operator>>. They could be private
   // if those templated things could be made friends.
@@ -1504,6 +1508,8 @@ class Input : public IO {
   /// Returns the current node that's being parsed by the YAML Parser.
   const Node *getCurrentNode() const;
 
+  void setAllowUnknownKeys(bool Allow) override;
+
 private:
   SourceMgr                           SrcMgr; // must be before Strm
   std::unique_ptr<llvm::yaml::Stream> Strm;
@@ -1514,6 +1520,7 @@ class Input : public IO {
   std::vector<bool>                   BitValuesUsed;
   HNode *CurrentNode = nullptr;
   bool                                ScalarMatchFound = false;
+  bool AllowUnknownKeys = false;
 };
 
 ///

diff  --git a/llvm/lib/Support/YAMLParser.cpp b/llvm/lib/Support/YAMLParser.cpp
index ca8ffdc47afa..b9bbdc33a433 100644
--- a/llvm/lib/Support/YAMLParser.cpp
+++ b/llvm/lib/Support/YAMLParser.cpp
@@ -1775,12 +1775,9 @@ Stream::~Stream() = default;
 
 bool Stream::failed() { return scanner->failed(); }
 
-void Stream::printError(Node *N, const Twine &Msg) {
+void Stream::printError(Node *N, const Twine &Msg, SourceMgr::DiagKind Kind) {
   SMRange Range = N ? N->getSourceRange() : SMRange();
-  scanner->printError( Range.Start
-                     , SourceMgr::DK_Error
-                     , Msg
-                     , Range);
+  scanner->printError(Range.Start, Kind, Msg, Range);
 }
 
 document_iterator Stream::begin() {

diff  --git a/llvm/lib/Support/YAMLTraits.cpp b/llvm/lib/Support/YAMLTraits.cpp
index 9ac7c65e19f7..8a5bb653d7ac 100644
--- a/llvm/lib/Support/YAMLTraits.cpp
+++ b/llvm/lib/Support/YAMLTraits.cpp
@@ -48,6 +48,10 @@ void IO::setContext(void *Context) {
   Ctxt = Context;
 }
 
+void IO::setAllowUnknownKeys(bool Allow) {
+  llvm_unreachable("Only supported for Input");
+}
+
 //===----------------------------------------------------------------------===//
 //  Input
 //===----------------------------------------------------------------------===//
@@ -197,8 +201,12 @@ void Input::endMapping() {
     return;
   for (const auto &NN : MN->Mapping) {
     if (!is_contained(MN->ValidKeys, NN.first())) {
-      setError(NN.second.get(), Twine("unknown key '") + NN.first() + "'");
-      break;
+      HNode *ReportNode = NN.second.get();
+      if (!AllowUnknownKeys) {
+        setError(ReportNode, Twine("unknown key '") + NN.first() + "'");
+        break;
+      } else
+        reportWarning(ReportNode, Twine("unknown key '") + NN.first() + "'");
     }
   }
 }
@@ -370,6 +378,11 @@ void Input::setError(Node *node, const Twine &message) {
   EC = make_error_code(errc::invalid_argument);
 }
 
+void Input::reportWarning(HNode *hnode, const Twine &message) {
+  assert(hnode && "HNode must not be NULL");
+  Strm->printError(hnode->_node, message, SourceMgr::DK_Warning);
+}
+
 std::unique_ptr<Input::HNode> Input::createHNodes(Node *N) {
   SmallString<128> StringStorage;
   if (ScalarNode *SN = dyn_cast<ScalarNode>(N)) {
@@ -428,6 +441,8 @@ void Input::setError(const Twine &Message) {
   setError(CurrentNode, Message);
 }
 
+void Input::setAllowUnknownKeys(bool Allow) { AllowUnknownKeys = Allow; }
+
 bool Input::canElideEmptySequence() {
   return false;
 }

diff  --git a/llvm/unittests/ObjectYAML/YAMLTest.cpp b/llvm/unittests/ObjectYAML/YAMLTest.cpp
index 3d283a03b225..53582a987215 100644
--- a/llvm/unittests/ObjectYAML/YAMLTest.cpp
+++ b/llvm/unittests/ObjectYAML/YAMLTest.cpp
@@ -35,3 +35,21 @@ TEST(ObjectYAML, BinaryRef) {
   YOut << BH;
   EXPECT_NE(OS.str().find("''"), StringRef::npos);
 }
+
+TEST(ObjectYAML, UnknownOption) {
+  StringRef InputYAML = "InvalidKey: InvalidValue\n"
+                        "Binary: AAAA\n";
+  BinaryHolder BH;
+  yaml::Input Input(InputYAML);
+  // test 1: default in trying to parse invalid key is an error case.
+  Input >> BH;
+  EXPECT_EQ(Input.error().value(), 22);
+
+  // test 2: only warn about invalid key if actively set.
+  yaml::Input Input2(InputYAML);
+  BinaryHolder BH2;
+  Input2.setAllowUnknownKeys(true);
+  Input2 >> BH2;
+  EXPECT_EQ(BH2.Binary, yaml::BinaryRef("AAAA"));
+  EXPECT_EQ(Input2.error().value(), 0);
+}


        


More information about the llvm-commits mailing list