]> granicus.if.org Git - clang/commitdiff
Comment parsing: in the generated XML file, mark HTML that is safe to pass
authorDmitri Gribenko <gribozavr@gmail.com>
Tue, 22 Apr 2014 10:59:13 +0000 (10:59 +0000)
committerDmitri Gribenko <gribozavr@gmail.com>
Tue, 22 Apr 2014 10:59:13 +0000 (10:59 +0000)
through to the output even if the input comment comes from an untrusted source

Attribute filtering is currently based on a blacklist, which right now includes
all event handler attributes (they contain JavaScipt code).  It should be
switched to a whitelist, but going over all of the HTML5 spec requires a
significant amount of time.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@206882 91177308-0d34-0410-b5e6-96231b3b80d8

bindings/xml/comment-xml-schema.rng
include/clang/AST/Comment.h
include/clang/AST/CommentHTMLTags.td
include/clang/Basic/DiagnosticCommentKinds.td
lib/AST/CommentSema.cpp
lib/Index/CommentToXML.cpp
test/Index/Inputs/CommentXML/valid-function-02.xml
test/Index/comment-to-html-xml-conversion.cpp
test/Sema/warn-documentation.cpp
utils/TableGen/ClangCommentHTMLTagsEmitter.cpp

index a8913a360b79d4fcb8bf46a55f012f4e7b2cf283..29a91bf674f65a1105845e3c07da0c34fb6d60db 100644 (file)
         </data>
       </element>
       <element name="rawHTML">
+        <optional>
+          <!-- If not specified, the default value is 'false'. -->
+          <attribute name="isSafeToPassThrough">
+            <data type="boolean" />
+          </attribute>
+        </optional>
         <!-- Non-empty text content. -->
         <data type="string">
           <param name="pattern">.*\S.*</param>
index 50e9196b1a9d62adec9d4d57fa2f23d834fba06d..90dfb2570a5f91f96b3d2a6849d64256a8f17ae6 100644 (file)
@@ -100,16 +100,27 @@ protected:
   };
   enum { NumInlineCommandCommentBits = NumInlineContentCommentBits + 10 };
 
+  class HTMLTagCommentBitfields {
+    friend class HTMLTagComment;
+
+    unsigned : NumInlineContentCommentBits;
+
+    /// True if this tag is safe to pass through to HTML output even if the
+    /// comment comes from an untrusted source.
+    unsigned IsSafeToPassThrough : 1;
+  };
+  enum { NumHTMLTagCommentBits = NumInlineContentCommentBits + 1 };
+
   class HTMLStartTagCommentBitfields {
     friend class HTMLStartTagComment;
 
-    unsigned : NumInlineContentCommentBits;
+    unsigned : NumHTMLTagCommentBits;
 
     /// True if this tag is self-closing (e. g., <br />).  This is based on tag
     /// spelling in comment (plain <br> would not set this flag).
     unsigned IsSelfClosing : 1;
   };
-  enum { NumHTMLStartTagCommentBits = NumInlineContentCommentBits + 1 };
+  enum { NumHTMLStartTagCommentBits = NumHTMLTagCommentBits + 1 };
 
   class ParagraphCommentBitfields {
     friend class ParagraphComment;
@@ -155,6 +166,7 @@ protected:
     InlineContentCommentBitfields InlineContentCommentBits;
     TextCommentBitfields TextCommentBits;
     InlineCommandCommentBitfields InlineCommandCommentBits;
+    HTMLTagCommentBitfields HTMLTagCommentBits;
     HTMLStartTagCommentBitfields HTMLStartTagCommentBits;
     ParagraphCommentBitfields ParagraphCommentBits;
     BlockCommandCommentBitfields BlockCommandCommentBits;
@@ -360,8 +372,7 @@ public:
 };
 
 /// Abstract class for opening and closing HTML tags.  HTML tags are always
-/// treated as inline content (regardless HTML semantics); opening and closing
-/// tags are not matched.
+/// treated as inline content (regardless HTML semantics).
 class HTMLTagComment : public InlineContentComment {
 protected:
   StringRef TagName;
@@ -377,6 +388,7 @@ protected:
       TagName(TagName),
       TagNameRange(TagNameBegin, TagNameEnd) {
     setLocation(TagNameBegin);
+    HTMLTagCommentBits.IsSafeToPassThrough = 1;
   }
 
 public:
@@ -392,6 +404,14 @@ public:
     return SourceRange(L.getLocWithOffset(1),
                        L.getLocWithOffset(1 + TagName.size()));
   }
+
+  bool isSafeToPassThrough() const {
+    return HTMLTagCommentBits.IsSafeToPassThrough;
+  }
+
+  void setUnsafeToPassThrough() {
+    HTMLTagCommentBits.IsSafeToPassThrough = 0;
+  }
 };
 
 /// An opening HTML tag with attributes.
index f98e32ddca4f830ca97b09435822762d13c99983..79951b80ee5ea0230f210bef250fabf9c0ba982a 100644 (file)
@@ -52,3 +52,83 @@ def Tr      : Tag<"tr"> { let EndTagOptional = 1; }
 def Th      : Tag<"th"> { let EndTagOptional = 1; }
 def Td      : Tag<"td"> { let EndTagOptional = 1; }
 
+// Define a blacklist of attributes that are not safe to pass through to HTML
+// output if the input is untrusted.
+//
+// FIXME: this should be a whitelist.  When changing this to a whitelist, don't
+// forget to change the default in the TableGen backend.
+class Attribute<string spelling> {
+  string Spelling = spelling;
+  bit IsSafeToPassThrough = 1;
+}
+class EventHandlerContentAttribute<string spelling> : Attribute<spelling> {
+  let IsSafeToPassThrough = 0;
+}
+
+// This list is based on HTML5 draft as of 04 February 2014.
+//
+// The list is intentionally organized as one item per line to make it easier
+// to compare with the HTML spec.
+foreach AttrName = [
+    "onabort",
+    "onblur",
+    "oncancel",
+    "oncanplay",
+    "oncanplaythrough",
+    "onchange",
+    "onclick",
+    "onclose",
+    "oncuechange",
+    "ondblclick",
+    "ondrag",
+    "ondragend",
+    "ondragenter",
+    "ondragexit",
+    "ondragleave",
+    "ondragover",
+    "ondragstart",
+    "ondrop",
+    "ondurationchange",
+    "onemptied",
+    "onended",
+    "onerror",
+    "onfocus",
+    "oninput",
+    "oninvalid",
+    "onkeydown",
+    "onkeypress",
+    "onkeyup",
+    "onload",
+    "onloadeddata",
+    "onloadedmetadata",
+    "onloadstart",
+    "onmousedown",
+    "onmouseenter",
+    "onmouseleave",
+    "onmousemove",
+    "onmouseout",
+    "onmouseover",
+    "onmouseup",
+    "onmousewheel",
+    "onpause",
+    "onplay",
+    "onplaying",
+    "onprogress",
+    "onratechange",
+    "onreset",
+    "onresize",
+    "onscroll",
+    "onseeked",
+    "onseeking",
+    "onselect",
+    "onshow",
+    "onstalled",
+    "onsubmit",
+    "onsuspend",
+    "ontimeupdate",
+    "ontoggle",
+    "onvolumechange",
+    "onwaiting"
+  ] in {
+  def Attr#AttrName : EventHandlerContentAttribute<AttrName>;
+}
index 49781fec9a9cf882b96aba4ac05194ce364289eb..49296f9b5e1ea63d47d964e808f66984dab8c745 100644 (file)
@@ -41,6 +41,10 @@ def warn_doc_html_start_end_mismatch : Warning<
 def note_doc_html_end_tag : Note<
   "end tag">;
 
+def warn_doc_html_missing_end_tag : Warning<
+  "HTML tag '%0' requires an end tag">,
+  InGroup<DocumentationHTML>, DefaultIgnore;
+
 // Commands
 
 def warn_doc_block_command_empty_paragraph : Warning<
index e51f8781c1b0ca67d6b7daf2c142c9dc6a7af5e7..5a0bf37739cc0a36c311482bbbb4bb9c98b736cc 100644 (file)
@@ -467,6 +467,11 @@ void Sema::actOnHTMLStartTagFinish(
                               SourceLocation GreaterLoc,
                               bool IsSelfClosing) {
   Tag->setAttrs(Attrs);
+  for (const auto &Attr : Attrs) {
+    if (!isHTMLAttributeSafeToPassThrough(Attr.Name))
+      Tag->setUnsafeToPassThrough();
+  }
+
   Tag->setGreaterLoc(GreaterLoc);
   if (IsSelfClosing)
     Tag->setSelfClosing();
@@ -482,6 +487,7 @@ HTMLEndTagComment *Sema::actOnHTMLEndTag(SourceLocation LocBegin,
   if (isHTMLEndTagForbidden(TagName)) {
     Diag(HET->getLocation(), diag::warn_doc_html_end_forbidden)
       << TagName << HET->getSourceRange();
+    HET->setUnsafeToPassThrough();
     return HET;
   }
 
@@ -497,14 +503,19 @@ HTMLEndTagComment *Sema::actOnHTMLEndTag(SourceLocation LocBegin,
   if (!FoundOpen) {
     Diag(HET->getLocation(), diag::warn_doc_html_end_unbalanced)
       << HET->getSourceRange();
+    HET->setUnsafeToPassThrough();
     return HET;
   }
 
   while (!HTMLOpenTags.empty()) {
-    const HTMLStartTagComment *HST = HTMLOpenTags.pop_back_val();
+    HTMLStartTagComment *HST = HTMLOpenTags.pop_back_val();
     StringRef LastNotClosedTagName = HST->getTagName();
-    if (LastNotClosedTagName == TagName)
+    if (LastNotClosedTagName == TagName) {
+      // If the start tag is unsafe, end tag is unsafe as well.
+      if (!HST->isSafeToPassThrough())
+        HET->setUnsafeToPassThrough();
       break;
+    }
 
     if (isHTMLEndTagOptional(LastNotClosedTagName))
       continue;
@@ -518,16 +529,18 @@ HTMLEndTagComment *Sema::actOnHTMLEndTag(SourceLocation LocBegin,
                                                 HET->getLocation(),
                                                 &CloseLineInvalid);
 
-    if (OpenLineInvalid || CloseLineInvalid || OpenLine == CloseLine)
+    if (OpenLineInvalid || CloseLineInvalid || OpenLine == CloseLine) {
       Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
         << HST->getTagName() << HET->getTagName()
         << HST->getSourceRange() << HET->getSourceRange();
-    else {
+      HST->setUnsafeToPassThrough();
+    } else {
       Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
         << HST->getTagName() << HET->getTagName()
         << HST->getSourceRange();
       Diag(HET->getLocation(), diag::note_doc_html_end_tag)
         << HET->getSourceRange();
+      HST->setUnsafeToPassThrough();
     }
   }
 
@@ -538,6 +551,18 @@ FullComment *Sema::actOnFullComment(
                               ArrayRef<BlockContentComment *> Blocks) {
   FullComment *FC = new (Allocator) FullComment(Blocks, ThisDeclInfo);
   resolveParamCommandIndexes(FC);
+
+  // Complain about HTML tags that are not closed.
+  while (!HTMLOpenTags.empty()) {
+    HTMLStartTagComment *HST = HTMLOpenTags.pop_back_val();
+    if (isHTMLEndTagOptional(HST->getTagName()))
+      continue;
+
+    Diag(HST->getLocation(), diag::warn_doc_html_missing_end_tag)
+      << HST->getTagName() << HST->getSourceRange();
+    HST->setUnsafeToPassThrough();
+  }
+
   return FC;
 }
 
index 43c423274daac70ba18b70d623dc4495a8401882..377440f81d58ca61cbf37e217752998f5a94e35c 100644 (file)
@@ -667,14 +667,20 @@ void CommentASTToXMLConverter::visitInlineCommandComment(
 
 void CommentASTToXMLConverter::visitHTMLStartTagComment(
     const HTMLStartTagComment *C) {
-  Result << "<rawHTML><![CDATA[";
+  Result << "<rawHTML";
+  if (C->isSafeToPassThrough())
+    Result << " isSafeToPassThrough=\"1\"";
+  Result << "><![CDATA[";
   printHTMLStartTagComment(C, Result);
   Result << "]]></rawHTML>";
 }
 
 void
 CommentASTToXMLConverter::visitHTMLEndTagComment(const HTMLEndTagComment *C) {
-  Result << "<rawHTML>&lt;/" << C->getTagName() << "&gt;</rawHTML>";
+  Result << "<rawHTML";
+  if (C->isSafeToPassThrough())
+    Result << " isSafeToPassThrough=\"1\"";
+  Result << ">&lt;/" << C->getTagName() << "&gt;</rawHTML>";
 }
 
 void
index 989d6a7c143d2845c9ef7a6325a226252c7b022b..98e4fd1c1edbe673a3f7c40ed22ea249c941b5b6 100644 (file)
@@ -1,5 +1,14 @@
 <?xml version="1.0" encoding="utf-8"?>
 <Function>
 <Name>aaa</Name>
-<Abstract><Para>Aaa <bold>bbb</bold> <monospaced>ccc</monospaced> <emphasized>ddd</emphasized>.</Para></Abstract>
+<Abstract>
+  <Para>Aaa
+    <bold>bbb</bold>
+    <monospaced>ccc</monospaced>
+    <emphasized>ddd</emphasized>
+    <rawHTML>&lt;eee&gt;</rawHTML>
+    <rawHTML isSafeToPassThrough="0">&lt;fff&gt;</rawHTML>
+    <rawHTML isSafeToPassThrough="1">&lt;ggg&gt;</rawHTML>.
+  </Para>
+</Abstract>
 </Function>
index 327fa6448327e489142a8ee629f8af29f5640f9d..590e18719741546a6197732f5cf687d580e71149 100644 (file)
@@ -472,7 +472,7 @@ void test_full_comment_1(int x1, int x2);
 /// <br><a href="http://example.com/">Aaa</a>
 void comment_to_html_conversion_24();
 
-// CHECK: comment-to-html-xml-conversion.cpp:[[@LINE-2]]:6: FunctionDecl=comment_to_html_conversion_24:{{.*}} FullCommentAsHTML=[<p class="para-brief"> <br><a href="http://example.com/">Aaa</a></p>] FullCommentAsXML=[<Function file="{{[^"]+}}comment-to-html-xml-conversion.cpp" line="[[@LINE-2]]" column="6"><Name>comment_to_html_conversion_24</Name><USR>c:@F@comment_to_html_conversion_24#</USR><Declaration>void comment_to_html_conversion_24()</Declaration><Abstract><Para> <rawHTML><![CDATA[<br>]]></rawHTML><rawHTML><![CDATA[<a href="http://example.com/">]]></rawHTML>Aaa<rawHTML>&lt;/a&gt;</rawHTML></Para></Abstract></Function>]
+// CHECK: comment-to-html-xml-conversion.cpp:[[@LINE-2]]:6: FunctionDecl=comment_to_html_conversion_24:{{.*}} FullCommentAsHTML=[<p class="para-brief"> <br><a href="http://example.com/">Aaa</a></p>] FullCommentAsXML=[<Function file="{{[^"]+}}comment-to-html-xml-conversion.cpp" line="[[@LINE-2]]" column="6"><Name>comment_to_html_conversion_24</Name><USR>c:@F@comment_to_html_conversion_24#</USR><Declaration>void comment_to_html_conversion_24()</Declaration><Abstract><Para> <rawHTML isSafeToPassThrough="1"><![CDATA[<br>]]></rawHTML><rawHTML isSafeToPassThrough="1"><![CDATA[<a href="http://example.com/">]]></rawHTML>Aaa<rawHTML isSafeToPassThrough="1">&lt;/a&gt;</rawHTML></Para></Abstract></Function>]
 // CHECK-NEXT:  CommentAST=[
 // CHECK-NEXT:    (CXComment_FullComment
 // CHECK-NEXT:       (CXComment_Paragraph
@@ -678,7 +678,7 @@ void comment_to_html_conversion_33();
 /// <em>0&lt;i</em>
 void comment_to_html_conversion_34();
 
-// CHECK: comment-to-html-xml-conversion.cpp:[[@LINE-2]]:6: FunctionDecl=comment_to_html_conversion_34:{{.*}} FullCommentAsHTML=[<p class="para-brief"> <em>0&lt;i</em></p>] FullCommentAsXML=[<Function file="{{[^"]+}}comment-to-html-xml-conversion.cpp" line="[[@LINE-2]]" column="6"><Name>comment_to_html_conversion_34</Name><USR>c:@F@comment_to_html_conversion_34#</USR><Declaration>void comment_to_html_conversion_34()</Declaration><Abstract><Para> <rawHTML><![CDATA[<em>]]></rawHTML>0&lt;i<rawHTML>&lt;/em&gt;</rawHTML></Para></Abstract></Function>]
+// CHECK: comment-to-html-xml-conversion.cpp:[[@LINE-2]]:6: FunctionDecl=comment_to_html_conversion_34:{{.*}} FullCommentAsHTML=[<p class="para-brief"> <em>0&lt;i</em></p>] FullCommentAsXML=[<Function file="{{[^"]+}}comment-to-html-xml-conversion.cpp" line="[[@LINE-2]]" column="6"><Name>comment_to_html_conversion_34</Name><USR>c:@F@comment_to_html_conversion_34#</USR><Declaration>void comment_to_html_conversion_34()</Declaration><Abstract><Para> <rawHTML isSafeToPassThrough="1"><![CDATA[<em>]]></rawHTML>0&lt;i<rawHTML isSafeToPassThrough="1">&lt;/em&gt;</rawHTML></Para></Abstract></Function>]
 // CHECK-NEXT:  CommentAST=[
 // CHECK-NEXT:    (CXComment_FullComment
 // CHECK-NEXT:       (CXComment_Paragraph
@@ -853,6 +853,34 @@ enum class comment_to_xml_conversion_17 {
 // CHECK: comment-to-html-xml-conversion.cpp:[[@LINE-1]]:3: EnumConstantDecl=comment_to_xml_conversion_18:{{.*}} FullCommentAsXML=[<Variable file="{{[^"]+}}comment-to-html-xml-conversion.cpp" line="[[@LINE-1]]" column="3"><Name>comment_to_xml_conversion_18</Name><USR>c:@E@comment_to_xml_conversion_17@comment_to_xml_conversion_18</USR><Declaration>comment_to_xml_conversion_18</Declaration><Abstract><Para> Aaa.</Para></Abstract></Variable>]
 };
 
+/// <a href="http://example.org/">
+void comment_to_xml_conversion_unsafe_html_01();
+// CHECK: comment-to-html-xml-conversion.cpp:[[@LINE-1]]:6: FunctionDecl=comment_to_xml_conversion_unsafe_html_01:{{.*}} FullCommentAsXML=[<Function file="{{[^"]+}}comment-to-html-xml-conversion.cpp" line="[[@LINE-1]]" column="6"><Name>comment_to_xml_conversion_unsafe_html_01</Name><USR>c:@F@comment_to_xml_conversion_unsafe_html_01#</USR><Declaration>void comment_to_xml_conversion_unsafe_html_01()</Declaration><Abstract><Para> <rawHTML><![CDATA[<a href="http://example.org/">]]></rawHTML></Para></Abstract></Function>]
+
+/// <a href="http://example.org/"><em>Aaa</em>
+void comment_to_xml_conversion_unsafe_html_02();
+// CHECK: comment-to-html-xml-conversion.cpp:[[@LINE-1]]:6: FunctionDecl=comment_to_xml_conversion_unsafe_html_02:{{.*}} FullCommentAsXML=[<Function file="{{[^"]+}}comment-to-html-xml-conversion.cpp" line="[[@LINE-1]]" column="6"><Name>comment_to_xml_conversion_unsafe_html_02</Name><USR>c:@F@comment_to_xml_conversion_unsafe_html_02#</USR><Declaration>void comment_to_xml_conversion_unsafe_html_02()</Declaration><Abstract><Para> <rawHTML><![CDATA[<a href="http://example.org/">]]></rawHTML><rawHTML isSafeToPassThrough="1"><![CDATA[<em>]]></rawHTML>Aaa<rawHTML isSafeToPassThrough="1">&lt;/em&gt;</rawHTML></Para></Abstract></Function>]
+
+/// <em>Aaa
+void comment_to_xml_conversion_unsafe_html_03();
+// CHECK: comment-to-html-xml-conversion.cpp:[[@LINE-1]]:6: FunctionDecl=comment_to_xml_conversion_unsafe_html_03:{{.*}} FullCommentAsXML=[<Function file="{{[^"]+}}comment-to-html-xml-conversion.cpp" line="[[@LINE-1]]" column="6"><Name>comment_to_xml_conversion_unsafe_html_03</Name><USR>c:@F@comment_to_xml_conversion_unsafe_html_03#</USR><Declaration>void comment_to_xml_conversion_unsafe_html_03()</Declaration><Abstract><Para> <rawHTML><![CDATA[<em>]]></rawHTML>Aaa</Para></Abstract></Function>]
+
+/// <em>Aaa</b></em>
+void comment_to_xml_conversion_unsafe_html_04();
+// CHECK: comment-to-html-xml-conversion.cpp:[[@LINE-1]]:6: FunctionDecl=comment_to_xml_conversion_unsafe_html_04:{{.*}} FullCommentAsXML=[<Function file="{{[^"]+}}comment-to-html-xml-conversion.cpp" line="[[@LINE-1]]" column="6"><Name>comment_to_xml_conversion_unsafe_html_04</Name><USR>c:@F@comment_to_xml_conversion_unsafe_html_04#</USR><Declaration>void comment_to_xml_conversion_unsafe_html_04()</Declaration><Abstract><Para> <rawHTML isSafeToPassThrough="1"><![CDATA[<em>]]></rawHTML>Aaa<rawHTML>&lt;/b&gt;</rawHTML><rawHTML isSafeToPassThrough="1">&lt;/em&gt;</rawHTML></Para></Abstract></Function>]
+
+/// <em>Aaa</em></b>
+void comment_to_xml_conversion_unsafe_html_05();
+// CHECK: comment-to-html-xml-conversion.cpp:[[@LINE-1]]:6: FunctionDecl=comment_to_xml_conversion_unsafe_html_05:{{.*}} FullCommentAsXML=[<Function file="{{[^"]+}}comment-to-html-xml-conversion.cpp" line="[[@LINE-1]]" column="6"><Name>comment_to_xml_conversion_unsafe_html_05</Name><USR>c:@F@comment_to_xml_conversion_unsafe_html_05#</USR><Declaration>void comment_to_xml_conversion_unsafe_html_05()</Declaration><Abstract><Para> <rawHTML isSafeToPassThrough="1"><![CDATA[<em>]]></rawHTML>Aaa<rawHTML isSafeToPassThrough="1">&lt;/em&gt;</rawHTML><rawHTML>&lt;/b&gt;</rawHTML></Para></Abstract></Function>]
+
+/// </table>
+void comment_to_xml_conversion_unsafe_html_06();
+// CHECK: comment-to-html-xml-conversion.cpp:[[@LINE-1]]:6: FunctionDecl=comment_to_xml_conversion_unsafe_html_06:{{.*}} FullCommentAsXML=[<Function file="{{[^"]+}}comment-to-html-xml-conversion.cpp" line="[[@LINE-1]]" column="6"><Name>comment_to_xml_conversion_unsafe_html_06</Name><USR>c:@F@comment_to_xml_conversion_unsafe_html_06#</USR><Declaration>void comment_to_xml_conversion_unsafe_html_06()</Declaration><Abstract><Para> <rawHTML>&lt;/table&gt;</rawHTML></Para></Abstract></Function>]
+
+/// <div onclick="alert('meow');">Aaa</div>
+void comment_to_xml_conversion_unsafe_html_07();
+// CHECK: comment-to-html-xml-conversion.cpp:[[@LINE-1]]:6: FunctionDecl=comment_to_xml_conversion_unsafe_html_07:{{.*}} FullCommentAsXML=[<Function file="{{[^"]+}}comment-to-html-xml-conversion.cpp" line="[[@LINE-1]]" column="6"><Name>comment_to_xml_conversion_unsafe_html_07</Name><USR>c:@F@comment_to_xml_conversion_unsafe_html_07#</USR><Declaration>void comment_to_xml_conversion_unsafe_html_07()</Declaration><Abstract><Para> <rawHTML><![CDATA[<div onclick="alert('meow');">]]></rawHTML>Aaa<rawHTML>&lt;/div&gt;</rawHTML></Para></Abstract></Function>]
+
 //===---
 // Check that we attach comments from the base class to derived classes if they don't have a comment.
 // rdar://13647476
index ed25d949f5594b3bad4b2d8be42b576c4a96f7d5..4375cfcf560a31a1fb2ef8ce0f297f3fcf025d83 100644 (file)
@@ -4,35 +4,43 @@
 // RUN: c-index-test -test-load-source all -comments-xml-schema=%S/../../bindings/xml/comment-xml-schema.rng %s | FileCheck %s -check-prefix=WRONG
 // WRONG-NOT: CommentXMLInvalid
 
+// expected-warning@+2 {{HTML tag 'a' requires an end tag}}
 // expected-warning@+1 {{expected quoted string after equals sign}}
 /// <a href=>
 int test_html1(int);
 
+// expected-warning@+2 {{HTML tag 'a' requires an end tag}}
 // expected-warning@+1 {{expected quoted string after equals sign}}
 /// <a href==>
 int test_html2(int);
 
+// expected-warning@+3 {{HTML tag 'a' requires an end tag}}
 // expected-warning@+2 {{expected quoted string after equals sign}}
 // expected-warning@+1 {{HTML start tag prematurely ended, expected attribute name or '>'}}
 /// <a href= blah
 int test_html3(int);
 
+// expected-warning@+2 {{HTML tag 'a' requires an end tag}}
 // expected-warning@+1 {{HTML start tag prematurely ended, expected attribute name or '>'}}
 /// <a =>
 int test_html4(int);
 
+// expected-warning@+2 {{HTML tag 'a' requires an end tag}}
 // expected-warning@+1 {{HTML start tag prematurely ended, expected attribute name or '>'}}
 /// <a "aaa">
 int test_html5(int);
 
+// expected-warning@+2 {{HTML tag 'a' requires an end tag}}
 // expected-warning@+1 {{HTML start tag prematurely ended, expected attribute name or '>'}}
 /// <a a="b" =>
 int test_html6(int);
 
+// expected-warning@+2 {{HTML tag 'a' requires an end tag}}
 // expected-warning@+1 {{HTML start tag prematurely ended, expected attribute name or '>'}}
 /// <a a="b" "aaa">
 int test_html7(int);
 
+// expected-warning@+2 {{HTML tag 'a' requires an end tag}}
 // expected-warning@+1 {{HTML start tag prematurely ended, expected attribute name or '>'}}
 /// <a a="b" =
 int test_html8(int);
@@ -67,6 +75,8 @@ int test_html_nesting3(int);
 /// Bbb</p>
 int test_html_nesting4(int);
 
+// expected-warning@+3 {{HTML tag 'b' requires an end tag}}
+// expected-warning@+2 {{HTML tag 'i' requires an end tag}}
 // expected-warning@+1 {{HTML end tag does not match any start tag}}
 /// <b><i>Meow</a>
 int test_html_nesting5(int);
@@ -81,6 +91,9 @@ int test_html_nesting6(int);
 /// <b><i>Meow</b></i>
 int test_html_nesting7(int);
 
+// expected-warning@+1 {{HTML tag 'b' requires an end tag}}
+/// <b>Meow
+int test_html_nesting8(int);
 
 // expected-warning@+1 {{empty paragraph passed to '\brief' command}}
 /// \brief\returns Aaa
index bfcd2cfd15a438532deffc1e232775cafcc6f93f..2a9110f8eba7e78dfc4ad8a4dbd873ad735de46f 100644 (file)
@@ -12,6 +12,7 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "TableGenBackends.h"
 #include "llvm/TableGen/Record.h"
 #include "llvm/TableGen/StringMatcher.h"
 #include "llvm/TableGen/TableGenBackend.h"
 
 using namespace llvm;
 
-namespace clang {
-void EmitClangCommentHTMLTags(RecordKeeper &Records, raw_ostream &OS) {
+void clang::EmitClangCommentHTMLTags(RecordKeeper &Records, raw_ostream &OS) {
   std::vector<Record *> Tags = Records.getAllDerivedDefinitions("Tag");
   std::vector<StringMatcher::StringPair> Matches;
-  for (std::vector<Record *>::iterator I = Tags.begin(), E = Tags.end();
-       I != E; ++I) {
-    Record &Tag = **I;
-    std::string Spelling = Tag.getValueAsString("Spelling");
+  for (Record *Tag : Tags) {
+    std::string Spelling = Tag->getValueAsString("Spelling");
     Matches.push_back(StringMatcher::StringPair(Spelling, "return true;"));
   }
 
@@ -38,19 +36,17 @@ void EmitClangCommentHTMLTags(RecordKeeper &Records, raw_ostream &OS) {
      << "}\n\n";
 }
 
-void EmitClangCommentHTMLTagsProperties(RecordKeeper &Records,
-                                        raw_ostream &OS) {
+void clang::EmitClangCommentHTMLTagsProperties(RecordKeeper &Records,
+                                               raw_ostream &OS) {
   std::vector<Record *> Tags = Records.getAllDerivedDefinitions("Tag");
   std::vector<StringMatcher::StringPair> MatchesEndTagOptional;
   std::vector<StringMatcher::StringPair> MatchesEndTagForbidden;
-  for (std::vector<Record *>::iterator I = Tags.begin(), E = Tags.end();
-       I != E; ++I) {
-    Record &Tag = **I;
-    std::string Spelling = Tag.getValueAsString("Spelling");
+  for (Record *Tag : Tags) {
+    std::string Spelling = Tag->getValueAsString("Spelling");
     StringMatcher::StringPair Match(Spelling, "return true;");
-    if (Tag.getValueAsBit("EndTagOptional"))
+    if (Tag->getValueAsBit("EndTagOptional"))
       MatchesEndTagOptional.push_back(Match);
-    if (Tag.getValueAsBit("EndTagForbidden"))
+    if (Tag->getValueAsBit("EndTagForbidden"))
       MatchesEndTagForbidden.push_back(Match);
   }
 
@@ -65,6 +61,21 @@ void EmitClangCommentHTMLTagsProperties(RecordKeeper &Records,
   StringMatcher("Name", MatchesEndTagForbidden, OS).Emit();
   OS << "  return false;\n"
      << "}\n\n";
+
+  std::vector<Record *> Attributes =
+      Records.getAllDerivedDefinitions("Attribute");
+  std::vector<StringMatcher::StringPair> Matches;
+  for (Record *Attribute : Attributes) {
+    std::string Spelling = Attribute->getValueAsString("Spelling");
+    if (!Attribute->getValueAsBit("IsSafeToPassThrough"))
+      Matches.push_back(StringMatcher::StringPair(Spelling, "return false;"));
+  }
+
+  emitSourceFileHeader("HTML attribute name matcher", OS);
+
+  OS << "bool isHTMLAttributeSafeToPassThrough(StringRef Name) {\n";
+  StringMatcher("Name", Matches, OS).Emit();
+  OS << "  return true;\n"
+     << "}\n\n";
 }
-} // end namespace clang