</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>
};
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;
InlineContentCommentBitfields InlineContentCommentBits;
TextCommentBitfields TextCommentBits;
InlineCommandCommentBitfields InlineCommandCommentBits;
+ HTMLTagCommentBitfields HTMLTagCommentBits;
HTMLStartTagCommentBitfields HTMLStartTagCommentBits;
ParagraphCommentBitfields ParagraphCommentBits;
BlockCommandCommentBitfields BlockCommandCommentBits;
};
/// 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;
TagName(TagName),
TagNameRange(TagNameBegin, TagNameEnd) {
setLocation(TagNameBegin);
+ HTMLTagCommentBits.IsSafeToPassThrough = 1;
}
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.
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>;
+}
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<
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();
if (isHTMLEndTagForbidden(TagName)) {
Diag(HET->getLocation(), diag::warn_doc_html_end_forbidden)
<< TagName << HET->getSourceRange();
+ HET->setUnsafeToPassThrough();
return HET;
}
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;
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();
}
}
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;
}
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
<?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>
/// <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
/// <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
// 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
// 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);
/// 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);
/// <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
//
//===----------------------------------------------------------------------===//
+#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;"));
}
<< "}\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);
}
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