diff --git a/include/clang/AST/CommentSema.h b/include/clang/AST/CommentSema.h index 7b45320f6b..aae8e5cddb 100644 --- a/include/clang/AST/CommentSema.h +++ b/include/clang/AST/CommentSema.h @@ -156,7 +156,9 @@ public: unsigned getBlockCommandNumArgs(StringRef Name); bool isInlineCommand(StringRef Name); - bool HTMLOpenTagNeedsClosing(StringRef Name); + + bool isHTMLCloseTagOptional(StringRef Name); + bool isHTMLCloseTagForbidden(StringRef Name); }; } // end namespace comments diff --git a/include/clang/Basic/DiagnosticCommentKinds.td b/include/clang/Basic/DiagnosticCommentKinds.td index 7500d402df..a4b974ae82 100644 --- a/include/clang/Basic/DiagnosticCommentKinds.td +++ b/include/clang/Basic/DiagnosticCommentKinds.td @@ -26,6 +26,10 @@ def note_doc_html_tag_started_here : Note< // HTML semantic errors +def warn_doc_html_close_forbidden : Warning< + "HTML closing tag '%0' is forbidden">, + InGroup, DefaultIgnore; + def warn_doc_html_close_unbalanced : Warning< "HTML closing tag does not match any opening tag">, InGroup, DefaultIgnore; diff --git a/lib/AST/CommentSema.cpp b/lib/AST/CommentSema.cpp index ae13fa285a..69cc01683c 100644 --- a/lib/AST/CommentSema.cpp +++ b/lib/AST/CommentSema.cpp @@ -289,7 +289,7 @@ HTMLOpenTagComment *Sema::actOnHTMLOpenTagFinish( Tag->setGreaterLoc(GreaterLoc); if (IsSelfClosing) Tag->setSelfClosing(); - else + else if (!isHTMLCloseTagForbidden(Tag->getTagName())) HTMLOpenTags.push_back(Tag); return Tag; } @@ -299,6 +299,12 @@ HTMLCloseTagComment *Sema::actOnHTMLCloseTag(SourceLocation LocBegin, StringRef TagName) { HTMLCloseTagComment *HCT = new (Allocator) HTMLCloseTagComment(LocBegin, LocEnd, TagName); + if (isHTMLCloseTagForbidden(TagName)) { + Diag(HCT->getLocation(), diag::warn_doc_html_close_forbidden) + << TagName << HCT->getSourceRange(); + return HCT; + } + bool FoundOpen = false; for (SmallVectorImpl::const_reverse_iterator I = HTMLOpenTags.rbegin(), E = HTMLOpenTags.rend(); @@ -321,7 +327,7 @@ HTMLCloseTagComment *Sema::actOnHTMLCloseTag(SourceLocation LocBegin, if (LastNotClosedTagName == TagName) break; - if (!HTMLOpenTagNeedsClosing(LastNotClosedTagName)) + if (isHTMLCloseTagOptional(LastNotClosedTagName)) continue; bool OpenLineInvalid; @@ -448,12 +454,29 @@ bool Sema::isInlineCommand(StringRef Name) { .Default(false); } -bool Sema::HTMLOpenTagNeedsClosing(StringRef Name) { +bool Sema::isHTMLCloseTagOptional(StringRef Name) { return llvm::StringSwitch(Name) - .Case("br", false) - .Case("hr", false) - .Case("li", false) - .Default(true); + .Case("p", true) + .Case("li", true) + .Case("dt", true) + .Case("dd", true) + .Case("tr", true) + .Case("th", true) + .Case("td", true) + .Case("thead", true) + .Case("tfoot", true) + .Case("tbody", true) + .Case("colgroup", true) + .Default(false); +} + +bool Sema::isHTMLCloseTagForbidden(StringRef Name) { + return llvm::StringSwitch(Name) + .Case("br", true) + .Case("hr", true) + .Case("img", true) + .Case("col", true) + .Default(false); } } // end namespace comments diff --git a/test/Sema/warn-documentation.cpp b/test/Sema/warn-documentation.cpp index 3949b2ee49..b1c871a5fd 100644 --- a/test/Sema/warn-documentation.cpp +++ b/test/Sema/warn-documentation.cpp @@ -45,6 +45,9 @@ int test_html9(int); */ int test_html10(int); +// expected-warning@+1 {{HTML closing tag 'br' is forbidden}} +///

+int test_html11(int); ///
Meow
int test_html_nesting1(int);