blob: 6c37452e070bb9fa450e725b57350bdd93b176c3 [file] [log] [blame]
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +00001//===--- CommentSema.cpp - Doxygen comment semantic analysis --------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "clang/AST/CommentSema.h"
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000011#include "clang/AST/CommentDiagnostic.h"
12#include "clang/AST/Decl.h"
13#include "clang/AST/DeclObjC.h"
14#include "clang/Basic/SourceManager.h"
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000015#include "llvm/ADT/StringSwitch.h"
16
17namespace clang {
18namespace comments {
19
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000020Sema::Sema(llvm::BumpPtrAllocator &Allocator, const SourceManager &SourceMgr,
21 DiagnosticsEngine &Diags) :
22 Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags), ThisDecl(NULL) {
23}
24
25void Sema::setDecl(const Decl *D) {
26 ThisDecl = D;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000027}
28
29ParagraphComment *Sema::actOnParagraphComment(
30 ArrayRef<InlineContentComment *> Content) {
31 return new (Allocator) ParagraphComment(Content);
32}
33
34BlockCommandComment *Sema::actOnBlockCommandStart(SourceLocation LocBegin,
35 SourceLocation LocEnd,
36 StringRef Name) {
37 return new (Allocator) BlockCommandComment(LocBegin, LocEnd, Name);
38}
39
40BlockCommandComment *Sema::actOnBlockCommandArgs(
41 BlockCommandComment *Command,
42 ArrayRef<BlockCommandComment::Argument> Args) {
43 Command->setArgs(Args);
44 return Command;
45}
46
47BlockCommandComment *Sema::actOnBlockCommandFinish(
48 BlockCommandComment *Command,
49 ParagraphComment *Paragraph) {
50 Command->setParagraph(Paragraph);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000051 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000052 return Command;
53}
54
55ParamCommandComment *Sema::actOnParamCommandStart(SourceLocation LocBegin,
56 SourceLocation LocEnd,
57 StringRef Name) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000058 ParamCommandComment *Command =
59 new (Allocator) ParamCommandComment(LocBegin, LocEnd, Name);
60
61 if (!ThisDecl ||
62 !(isa<FunctionDecl>(ThisDecl) || isa<ObjCMethodDecl>(ThisDecl)))
63 Diag(Command->getLocation(),
64 diag::warn_doc_param_not_attached_to_a_function_decl)
65 << Command->getCommandNameRange();
66
67 return Command;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000068}
69
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000070ParamCommandComment *Sema::actOnParamCommandDirectionArg(
71 ParamCommandComment *Command,
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000072 SourceLocation ArgLocBegin,
73 SourceLocation ArgLocEnd,
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000074 StringRef Arg) {
75 ParamCommandComment::PassDirection Direction;
76 std::string ArgLower = Arg.lower();
77 // TODO: optimize: lower Name first (need an API in SmallString for that),
78 // after that StringSwitch.
79 if (ArgLower == "[in]")
80 Direction = ParamCommandComment::In;
81 else if (ArgLower == "[out]")
82 Direction = ParamCommandComment::Out;
83 else if (ArgLower == "[in,out]" || ArgLower == "[out,in]")
84 Direction = ParamCommandComment::InOut;
85 else {
86 // Remove spaces.
87 std::string::iterator O = ArgLower.begin();
88 for (std::string::iterator I = ArgLower.begin(), E = ArgLower.end();
89 I != E; ++I) {
90 const char C = *I;
91 if (C != ' ' && C != '\n' && C != '\r' &&
92 C != '\t' && C != '\v' && C != '\f')
93 *O++ = C;
94 }
95 ArgLower.resize(O - ArgLower.begin());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000096
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000097 bool RemovingWhitespaceHelped = false;
98 if (ArgLower == "[in]") {
99 Direction = ParamCommandComment::In;
100 RemovingWhitespaceHelped = true;
101 } else if (ArgLower == "[out]") {
102 Direction = ParamCommandComment::Out;
103 RemovingWhitespaceHelped = true;
104 } else if (ArgLower == "[in,out]" || ArgLower == "[out,in]") {
105 Direction = ParamCommandComment::InOut;
106 RemovingWhitespaceHelped = true;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000107 } else {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000108 Direction = ParamCommandComment::In;
109 RemovingWhitespaceHelped = false;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000110 }
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000111
112 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
113 if (RemovingWhitespaceHelped)
114 Diag(ArgLocBegin, diag::warn_doc_param_spaces_in_direction)
115 << ArgRange
116 << FixItHint::CreateReplacement(
117 ArgRange,
118 ParamCommandComment::getDirectionAsString(Direction));
119 else
120 Diag(ArgLocBegin, diag::warn_doc_param_invalid_direction)
121 << ArgRange;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000122 }
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000123 Command->setDirection(Direction, /* Explicit = */ true);
124 return Command;
125}
126
127ParamCommandComment *Sema::actOnParamCommandParamNameArg(
128 ParamCommandComment *Command,
129 SourceLocation ArgLocBegin,
130 SourceLocation ArgLocEnd,
131 StringRef Arg) {
132 // Parser will not feed us more arguments than needed.
Dmitri Gribenko0eaf69d2012-07-13 19:02:42 +0000133 assert(Command->getNumArgs() == 0);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000134
135 if (!Command->isDirectionExplicit()) {
136 // User didn't provide a direction argument.
137 Command->setDirection(ParamCommandComment::In, /* Explicit = */ false);
138 }
139 typedef BlockCommandComment::Argument Argument;
140 Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
141 ArgLocEnd),
142 Arg);
143 Command->setArgs(llvm::makeArrayRef(A, 1));
144
145 if (!ThisDecl)
146 return Command;
147
148 const ParmVarDecl * const *ParamVars;
149 unsigned NumParams;
150 if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(ThisDecl)) {
151 ParamVars = FD->param_begin();
152 NumParams = FD->getNumParams();
153 } else if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(ThisDecl)) {
154 ParamVars = MD->param_begin();
155 NumParams = MD->param_size();
156 } else {
157 // We already warned that this \\param is not attached to a function decl.
158 return Command;
159 }
160
161 // Check that referenced parameter name is in the function decl.
162 const unsigned ResolvedParamIndex = resolveParmVarReference(Arg, ParamVars,
163 NumParams);
164 if (ResolvedParamIndex != ParamCommandComment::InvalidParamIndex) {
165 Command->setParamIndex(ResolvedParamIndex);
166 return Command;
167 }
168
169 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
170 Diag(ArgLocBegin, diag::warn_doc_param_not_found)
171 << Arg << ArgRange;
172
173 unsigned CorrectedParamIndex = ParamCommandComment::InvalidParamIndex;
174 if (NumParams == 1) {
175 // If function has only one parameter then only that parameter
176 // can be documented.
177 CorrectedParamIndex = 0;
178 } else {
179 // Do typo correction.
180 CorrectedParamIndex = correctTypoInParmVarReference(Arg, ParamVars,
181 NumParams);
182 }
183 if (CorrectedParamIndex != ParamCommandComment::InvalidParamIndex) {
184 const ParmVarDecl *CorrectedPVD = ParamVars[CorrectedParamIndex];
185 if (const IdentifierInfo *CorrectedII = CorrectedPVD->getIdentifier())
186 Diag(ArgLocBegin, diag::note_doc_param_name_suggestion)
187 << CorrectedII->getName()
188 << FixItHint::CreateReplacement(ArgRange, CorrectedII->getName());
189 }
190
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000191 return Command;
192}
193
194ParamCommandComment *Sema::actOnParamCommandFinish(ParamCommandComment *Command,
195 ParagraphComment *Paragraph) {
196 Command->setParagraph(Paragraph);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000197 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000198 return Command;
199}
200
201InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
202 SourceLocation CommandLocEnd,
203 StringRef CommandName) {
204 ArrayRef<InlineCommandComment::Argument> Args;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000205 return new (Allocator) InlineCommandComment(
206 CommandLocBegin,
207 CommandLocEnd,
208 CommandName,
209 getInlineCommandRenderKind(CommandName),
210 Args);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000211}
212
213InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
214 SourceLocation CommandLocEnd,
215 StringRef CommandName,
216 SourceLocation ArgLocBegin,
217 SourceLocation ArgLocEnd,
218 StringRef Arg) {
219 typedef InlineCommandComment::Argument Argument;
220 Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
221 ArgLocEnd),
222 Arg);
223
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000224 return new (Allocator) InlineCommandComment(
225 CommandLocBegin,
226 CommandLocEnd,
227 CommandName,
228 getInlineCommandRenderKind(CommandName),
229 llvm::makeArrayRef(A, 1));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000230}
231
232InlineContentComment *Sema::actOnUnknownCommand(SourceLocation LocBegin,
233 SourceLocation LocEnd,
234 StringRef Name) {
235 ArrayRef<InlineCommandComment::Argument> Args;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000236 return new (Allocator) InlineCommandComment(
237 LocBegin, LocEnd, Name,
238 InlineCommandComment::RenderNormal,
239 Args);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000240}
241
242TextComment *Sema::actOnText(SourceLocation LocBegin,
243 SourceLocation LocEnd,
244 StringRef Text) {
245 return new (Allocator) TextComment(LocBegin, LocEnd, Text);
246}
247
248VerbatimBlockComment *Sema::actOnVerbatimBlockStart(SourceLocation Loc,
249 StringRef Name) {
250 return new (Allocator) VerbatimBlockComment(
251 Loc,
252 Loc.getLocWithOffset(1 + Name.size()),
253 Name);
254}
255
256VerbatimBlockLineComment *Sema::actOnVerbatimBlockLine(SourceLocation Loc,
257 StringRef Text) {
258 return new (Allocator) VerbatimBlockLineComment(Loc, Text);
259}
260
261VerbatimBlockComment *Sema::actOnVerbatimBlockFinish(
262 VerbatimBlockComment *Block,
263 SourceLocation CloseNameLocBegin,
264 StringRef CloseName,
265 ArrayRef<VerbatimBlockLineComment *> Lines) {
266 Block->setCloseName(CloseName, CloseNameLocBegin);
267 Block->setLines(Lines);
268 return Block;
269}
270
271VerbatimLineComment *Sema::actOnVerbatimLine(SourceLocation LocBegin,
272 StringRef Name,
273 SourceLocation TextBegin,
274 StringRef Text) {
275 return new (Allocator) VerbatimLineComment(
276 LocBegin,
277 TextBegin.getLocWithOffset(Text.size()),
278 Name,
279 TextBegin,
280 Text);
281}
282
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000283HTMLStartTagComment *Sema::actOnHTMLStartTagStart(SourceLocation LocBegin,
284 StringRef TagName) {
285 return new (Allocator) HTMLStartTagComment(LocBegin, TagName);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000286}
287
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000288HTMLStartTagComment *Sema::actOnHTMLStartTagFinish(
289 HTMLStartTagComment *Tag,
290 ArrayRef<HTMLStartTagComment::Attribute> Attrs,
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000291 SourceLocation GreaterLoc,
292 bool IsSelfClosing) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000293 Tag->setAttrs(Attrs);
294 Tag->setGreaterLoc(GreaterLoc);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000295 if (IsSelfClosing)
296 Tag->setSelfClosing();
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000297 else if (!isHTMLEndTagForbidden(Tag->getTagName()))
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000298 HTMLOpenTags.push_back(Tag);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000299 return Tag;
300}
301
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000302HTMLEndTagComment *Sema::actOnHTMLEndTag(SourceLocation LocBegin,
303 SourceLocation LocEnd,
304 StringRef TagName) {
305 HTMLEndTagComment *HET =
306 new (Allocator) HTMLEndTagComment(LocBegin, LocEnd, TagName);
307 if (isHTMLEndTagForbidden(TagName)) {
308 Diag(HET->getLocation(), diag::warn_doc_html_end_forbidden)
309 << TagName << HET->getSourceRange();
310 return HET;
Dmitri Gribenko3d986982012-07-12 23:37:09 +0000311 }
312
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000313 bool FoundOpen = false;
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000314 for (SmallVectorImpl<HTMLStartTagComment *>::const_reverse_iterator
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000315 I = HTMLOpenTags.rbegin(), E = HTMLOpenTags.rend();
316 I != E; ++I) {
317 if ((*I)->getTagName() == TagName) {
318 FoundOpen = true;
319 break;
320 }
321 }
322 if (!FoundOpen) {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000323 Diag(HET->getLocation(), diag::warn_doc_html_end_unbalanced)
324 << HET->getSourceRange();
325 return HET;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000326 }
327
328 while (!HTMLOpenTags.empty()) {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000329 const HTMLStartTagComment *HST = HTMLOpenTags.back();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000330 HTMLOpenTags.pop_back();
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000331 StringRef LastNotClosedTagName = HST->getTagName();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000332 if (LastNotClosedTagName == TagName)
333 break;
334
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000335 if (isHTMLEndTagOptional(LastNotClosedTagName))
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000336 continue;
337
338 bool OpenLineInvalid;
339 const unsigned OpenLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000340 HST->getLocation(),
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000341 &OpenLineInvalid);
342 bool CloseLineInvalid;
343 const unsigned CloseLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000344 HET->getLocation(),
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000345 &CloseLineInvalid);
346
347 if (OpenLineInvalid || CloseLineInvalid || OpenLine == CloseLine)
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000348 Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
349 << HST->getTagName() << HET->getTagName()
350 << HST->getSourceRange() << HET->getSourceRange();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000351 else {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000352 Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
353 << HST->getTagName() << HET->getTagName()
354 << HST->getSourceRange();
355 Diag(HET->getLocation(), diag::note_doc_html_end_tag)
356 << HET->getSourceRange();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000357 }
358 }
359
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000360 return HET;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000361}
362
363FullComment *Sema::actOnFullComment(
364 ArrayRef<BlockContentComment *> Blocks) {
365 return new (Allocator) FullComment(Blocks);
366}
367
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000368void Sema::checkBlockCommandEmptyParagraph(BlockCommandComment *Command) {
369 ParagraphComment *Paragraph = Command->getParagraph();
370 if (Paragraph->isWhitespace()) {
371 SourceLocation DiagLoc;
Dmitri Gribenko0eaf69d2012-07-13 19:02:42 +0000372 if (Command->getNumArgs() > 0)
373 DiagLoc = Command->getArgRange(Command->getNumArgs() - 1).getEnd();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000374 if (!DiagLoc.isValid())
375 DiagLoc = Command->getCommandNameRange().getEnd();
376 Diag(DiagLoc, diag::warn_doc_block_command_empty_paragraph)
377 << Command->getCommandName()
378 << Command->getSourceRange();
379 }
380}
381
382unsigned Sema::resolveParmVarReference(StringRef Name,
383 const ParmVarDecl * const *ParamVars,
384 unsigned NumParams) {
385 for (unsigned i = 0; i != NumParams; ++i) {
386 const IdentifierInfo *II = ParamVars[i]->getIdentifier();
387 if (II && II->getName() == Name)
388 return i;
389 }
390 return ParamCommandComment::InvalidParamIndex;
391}
392
393unsigned Sema::correctTypoInParmVarReference(
394 StringRef Typo,
395 const ParmVarDecl * const *ParamVars,
396 unsigned NumParams) {
397 const unsigned MaxEditDistance = (Typo.size() + 2) / 3;
Richard Smith18b7f952012-07-11 22:33:59 +0000398 unsigned BestPVDIndex = 0;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000399 unsigned BestEditDistance = MaxEditDistance + 1;
400 for (unsigned i = 0; i != NumParams; ++i) {
401 const IdentifierInfo *II = ParamVars[i]->getIdentifier();
402 if (II) {
403 StringRef Name = II->getName();
NAKAMURA Takumidc5796c2012-07-12 00:45:08 +0000404 unsigned MinPossibleEditDistance =
405 abs((int)Name.size() - (int)Typo.size());
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000406 if (MinPossibleEditDistance > 0 &&
407 Typo.size() / MinPossibleEditDistance < 3)
408 continue;
409
410 unsigned EditDistance = Typo.edit_distance(Name, true, MaxEditDistance);
411 if (EditDistance < BestEditDistance) {
412 BestEditDistance = EditDistance;
413 BestPVDIndex = i;
414 }
415 }
416 }
417
418 if (BestEditDistance <= MaxEditDistance)
419 return BestPVDIndex;
420 else
421 return ParamCommandComment::InvalidParamIndex;;
422}
423
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000424// TODO: tablegen
425bool Sema::isBlockCommand(StringRef Name) {
426 return llvm::StringSwitch<bool>(Name)
Dmitri Gribenko3d3d22c2012-07-18 00:44:55 +0000427 .Cases("brief", "short", true)
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000428 .Case("result", true)
429 .Case("return", true)
430 .Case("returns", true)
431 .Case("author", true)
432 .Case("authors", true)
433 .Case("pre", true)
434 .Case("post", true)
435 .Default(false) || isParamCommand(Name);
436}
437
438bool Sema::isParamCommand(StringRef Name) {
439 return llvm::StringSwitch<bool>(Name)
440 .Case("param", true)
441 .Case("arg", true)
442 .Default(false);
443}
444
445unsigned Sema::getBlockCommandNumArgs(StringRef Name) {
446 return llvm::StringSwitch<unsigned>(Name)
Dmitri Gribenko3d3d22c2012-07-18 00:44:55 +0000447 .Cases("brief", "short", 0)
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000448 .Case("pre", 0)
449 .Case("post", 0)
450 .Case("author", 0)
451 .Case("authors", 0)
452 .Default(0);
453}
454
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000455bool Sema::isInlineCommand(StringRef Name) const {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000456 return llvm::StringSwitch<bool>(Name)
Dmitri Gribenkoc48dd8e2012-07-19 00:21:03 +0000457 .Case("b", true)
458 .Cases("c", "p", true)
459 .Cases("a", "e", "em", true)
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000460 .Default(false);
461}
462
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000463InlineCommandComment::RenderKind
464Sema::getInlineCommandRenderKind(StringRef Name) const {
465 assert(isInlineCommand(Name));
466
467 return llvm::StringSwitch<InlineCommandComment::RenderKind>(Name)
468 .Case("b", InlineCommandComment::RenderBold)
469 .Cases("c", "p", InlineCommandComment::RenderMonospaced)
470 .Cases("a", "e", "em", InlineCommandComment::RenderEmphasized)
471 .Default(InlineCommandComment::RenderNormal);
472}
473
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000474bool Sema::isHTMLEndTagOptional(StringRef Name) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000475 return llvm::StringSwitch<bool>(Name)
Dmitri Gribenko3d986982012-07-12 23:37:09 +0000476 .Case("p", true)
477 .Case("li", true)
478 .Case("dt", true)
479 .Case("dd", true)
480 .Case("tr", true)
481 .Case("th", true)
482 .Case("td", true)
483 .Case("thead", true)
484 .Case("tfoot", true)
485 .Case("tbody", true)
486 .Case("colgroup", true)
487 .Default(false);
488}
489
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000490bool Sema::isHTMLEndTagForbidden(StringRef Name) {
Dmitri Gribenko3d986982012-07-12 23:37:09 +0000491 return llvm::StringSwitch<bool>(Name)
492 .Case("br", true)
493 .Case("hr", true)
494 .Case("img", true)
495 .Case("col", true)
496 .Default(false);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000497}
498
499} // end namespace comments
500} // end namespace clang
501