diff options
-rw-r--r-- | bindings/xml/comment-xml-schema.rng | 6 | ||||
-rw-r--r-- | include/clang/AST/Comment.h | 28 | ||||
-rw-r--r-- | include/clang/AST/CommentHTMLTags.td | 80 | ||||
-rw-r--r-- | include/clang/Basic/DiagnosticCommentKinds.td | 4 | ||||
-rw-r--r-- | lib/AST/CommentSema.cpp | 33 | ||||
-rw-r--r-- | lib/Index/CommentToXML.cpp | 10 | ||||
-rw-r--r-- | test/Index/Inputs/CommentXML/valid-function-02.xml | 11 | ||||
-rw-r--r-- | test/Index/comment-to-html-xml-conversion.cpp | 32 | ||||
-rw-r--r-- | test/Sema/warn-documentation.cpp | 13 | ||||
-rw-r--r-- | utils/TableGen/ClangCommentHTMLTagsEmitter.cpp | 41 |
10 files changed, 230 insertions, 28 deletions
diff --git a/bindings/xml/comment-xml-schema.rng b/bindings/xml/comment-xml-schema.rng index a8913a360b..29a91bf674 100644 --- a/bindings/xml/comment-xml-schema.rng +++ b/bindings/xml/comment-xml-schema.rng @@ -580,6 +580,12 @@ </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> diff --git a/include/clang/AST/Comment.h b/include/clang/AST/Comment.h index 50e9196b1a..90dfb2570a 100644 --- a/include/clang/AST/Comment.h +++ b/include/clang/AST/Comment.h @@ -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. diff --git a/include/clang/AST/CommentHTMLTags.td b/include/clang/AST/CommentHTMLTags.td index f98e32ddca..79951b80ee 100644 --- a/include/clang/AST/CommentHTMLTags.td +++ b/include/clang/AST/CommentHTMLTags.td @@ -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>; +} diff --git a/include/clang/Basic/DiagnosticCommentKinds.td b/include/clang/Basic/DiagnosticCommentKinds.td index 49781fec9a..49296f9b5e 100644 --- a/include/clang/Basic/DiagnosticCommentKinds.td +++ b/include/clang/Basic/DiagnosticCommentKinds.td @@ -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< diff --git a/lib/AST/CommentSema.cpp b/lib/AST/CommentSema.cpp index e51f8781c1..5a0bf37739 100644 --- a/lib/AST/CommentSema.cpp +++ b/lib/AST/CommentSema.cpp @@ -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; } diff --git a/lib/Index/CommentToXML.cpp b/lib/Index/CommentToXML.cpp index 43c423274d..377440f81d 100644 --- a/lib/Index/CommentToXML.cpp +++ b/lib/Index/CommentToXML.cpp @@ -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></" << C->getTagName() << "></rawHTML>"; + Result << "<rawHTML"; + if (C->isSafeToPassThrough()) + Result << " isSafeToPassThrough=\"1\""; + Result << "></" << C->getTagName() << "></rawHTML>"; } void diff --git a/test/Index/Inputs/CommentXML/valid-function-02.xml b/test/Index/Inputs/CommentXML/valid-function-02.xml index 989d6a7c14..98e4fd1c1e 100644 --- a/test/Index/Inputs/CommentXML/valid-function-02.xml +++ b/test/Index/Inputs/CommentXML/valid-function-02.xml @@ -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><eee></rawHTML> + <rawHTML isSafeToPassThrough="0"><fff></rawHTML> + <rawHTML isSafeToPassThrough="1"><ggg></rawHTML>. + </Para> +</Abstract> </Function> diff --git a/test/Index/comment-to-html-xml-conversion.cpp b/test/Index/comment-to-html-xml-conversion.cpp index 327fa64483..590e187197 100644 --- a/test/Index/comment-to-html-xml-conversion.cpp +++ b/test/Index/comment-to-html-xml-conversion.cpp @@ -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></a></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"></a></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<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<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<i<rawHTML></em></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<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<i<rawHTML isSafeToPassThrough="1"></em></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"></em></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></b></rawHTML><rawHTML isSafeToPassThrough="1"></em></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"></em></rawHTML><rawHTML></b></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></table></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></div></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 diff --git a/test/Sema/warn-documentation.cpp b/test/Sema/warn-documentation.cpp index ed25d949f5..4375cfcf56 100644 --- a/test/Sema/warn-documentation.cpp +++ b/test/Sema/warn-documentation.cpp @@ -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 diff --git a/utils/TableGen/ClangCommentHTMLTagsEmitter.cpp b/utils/TableGen/ClangCommentHTMLTagsEmitter.cpp index bfcd2cfd15..2a9110f8eb 100644 --- a/utils/TableGen/ClangCommentHTMLTagsEmitter.cpp +++ b/utils/TableGen/ClangCommentHTMLTagsEmitter.cpp @@ -12,6 +12,7 @@ // //===----------------------------------------------------------------------===// +#include "TableGenBackends.h" #include "llvm/TableGen/Record.h" #include "llvm/TableGen/StringMatcher.h" #include "llvm/TableGen/TableGenBackend.h" @@ -19,14 +20,11 @@ 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 |