blob: 8fa8ab7d06f5804ffe89bfa8561d29eb084e1bca [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) :
Dmitri Gribenko8487c522012-07-23 17:40:30 +000022 Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags), ThisDecl(NULL),
23 IsThisDeclInspected(false) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000024}
25
26void Sema::setDecl(const Decl *D) {
27 ThisDecl = D;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000028}
29
30ParagraphComment *Sema::actOnParagraphComment(
31 ArrayRef<InlineContentComment *> Content) {
32 return new (Allocator) ParagraphComment(Content);
33}
34
35BlockCommandComment *Sema::actOnBlockCommandStart(SourceLocation LocBegin,
36 SourceLocation LocEnd,
37 StringRef Name) {
38 return new (Allocator) BlockCommandComment(LocBegin, LocEnd, Name);
39}
40
41BlockCommandComment *Sema::actOnBlockCommandArgs(
42 BlockCommandComment *Command,
43 ArrayRef<BlockCommandComment::Argument> Args) {
44 Command->setArgs(Args);
45 return Command;
46}
47
48BlockCommandComment *Sema::actOnBlockCommandFinish(
49 BlockCommandComment *Command,
50 ParagraphComment *Paragraph) {
51 Command->setParagraph(Paragraph);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000052 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000053 return Command;
54}
55
56ParamCommandComment *Sema::actOnParamCommandStart(SourceLocation LocBegin,
57 SourceLocation LocEnd,
58 StringRef Name) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000059 ParamCommandComment *Command =
60 new (Allocator) ParamCommandComment(LocBegin, LocEnd, Name);
61
Dmitri Gribenko8487c522012-07-23 17:40:30 +000062 if (!isFunctionDecl())
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000063 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
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000145 if (!isFunctionDecl()) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000146 // We already warned that this \\param is not attached to a function decl.
147 return Command;
148 }
149
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000150 ArrayRef<const ParmVarDecl *> ParamVars = getParamVars();
151
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000152 // Check that referenced parameter name is in the function decl.
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000153 const unsigned ResolvedParamIndex = resolveParmVarReference(Arg, ParamVars);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000154 if (ResolvedParamIndex != ParamCommandComment::InvalidParamIndex) {
155 Command->setParamIndex(ResolvedParamIndex);
156 return Command;
157 }
158
159 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
160 Diag(ArgLocBegin, diag::warn_doc_param_not_found)
161 << Arg << ArgRange;
162
163 unsigned CorrectedParamIndex = ParamCommandComment::InvalidParamIndex;
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000164 if (ParamVars.size() == 1) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000165 // If function has only one parameter then only that parameter
166 // can be documented.
167 CorrectedParamIndex = 0;
168 } else {
169 // Do typo correction.
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000170 CorrectedParamIndex = correctTypoInParmVarReference(Arg, ParamVars);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000171 }
172 if (CorrectedParamIndex != ParamCommandComment::InvalidParamIndex) {
173 const ParmVarDecl *CorrectedPVD = ParamVars[CorrectedParamIndex];
174 if (const IdentifierInfo *CorrectedII = CorrectedPVD->getIdentifier())
175 Diag(ArgLocBegin, diag::note_doc_param_name_suggestion)
176 << CorrectedII->getName()
177 << FixItHint::CreateReplacement(ArgRange, CorrectedII->getName());
178 }
179
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000180 return Command;
181}
182
183ParamCommandComment *Sema::actOnParamCommandFinish(ParamCommandComment *Command,
184 ParagraphComment *Paragraph) {
185 Command->setParagraph(Paragraph);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000186 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000187 return Command;
188}
189
190InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
191 SourceLocation CommandLocEnd,
192 StringRef CommandName) {
193 ArrayRef<InlineCommandComment::Argument> Args;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000194 return new (Allocator) InlineCommandComment(
195 CommandLocBegin,
196 CommandLocEnd,
197 CommandName,
198 getInlineCommandRenderKind(CommandName),
199 Args);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000200}
201
202InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
203 SourceLocation CommandLocEnd,
204 StringRef CommandName,
205 SourceLocation ArgLocBegin,
206 SourceLocation ArgLocEnd,
207 StringRef Arg) {
208 typedef InlineCommandComment::Argument Argument;
209 Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
210 ArgLocEnd),
211 Arg);
212
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000213 return new (Allocator) InlineCommandComment(
214 CommandLocBegin,
215 CommandLocEnd,
216 CommandName,
217 getInlineCommandRenderKind(CommandName),
218 llvm::makeArrayRef(A, 1));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000219}
220
221InlineContentComment *Sema::actOnUnknownCommand(SourceLocation LocBegin,
222 SourceLocation LocEnd,
223 StringRef Name) {
224 ArrayRef<InlineCommandComment::Argument> Args;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000225 return new (Allocator) InlineCommandComment(
226 LocBegin, LocEnd, Name,
227 InlineCommandComment::RenderNormal,
228 Args);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000229}
230
231TextComment *Sema::actOnText(SourceLocation LocBegin,
232 SourceLocation LocEnd,
233 StringRef Text) {
234 return new (Allocator) TextComment(LocBegin, LocEnd, Text);
235}
236
237VerbatimBlockComment *Sema::actOnVerbatimBlockStart(SourceLocation Loc,
238 StringRef Name) {
239 return new (Allocator) VerbatimBlockComment(
240 Loc,
241 Loc.getLocWithOffset(1 + Name.size()),
242 Name);
243}
244
245VerbatimBlockLineComment *Sema::actOnVerbatimBlockLine(SourceLocation Loc,
246 StringRef Text) {
247 return new (Allocator) VerbatimBlockLineComment(Loc, Text);
248}
249
250VerbatimBlockComment *Sema::actOnVerbatimBlockFinish(
251 VerbatimBlockComment *Block,
252 SourceLocation CloseNameLocBegin,
253 StringRef CloseName,
254 ArrayRef<VerbatimBlockLineComment *> Lines) {
255 Block->setCloseName(CloseName, CloseNameLocBegin);
256 Block->setLines(Lines);
257 return Block;
258}
259
260VerbatimLineComment *Sema::actOnVerbatimLine(SourceLocation LocBegin,
261 StringRef Name,
262 SourceLocation TextBegin,
263 StringRef Text) {
264 return new (Allocator) VerbatimLineComment(
265 LocBegin,
266 TextBegin.getLocWithOffset(Text.size()),
267 Name,
268 TextBegin,
269 Text);
270}
271
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000272HTMLStartTagComment *Sema::actOnHTMLStartTagStart(SourceLocation LocBegin,
273 StringRef TagName) {
274 return new (Allocator) HTMLStartTagComment(LocBegin, TagName);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000275}
276
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000277HTMLStartTagComment *Sema::actOnHTMLStartTagFinish(
278 HTMLStartTagComment *Tag,
279 ArrayRef<HTMLStartTagComment::Attribute> Attrs,
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000280 SourceLocation GreaterLoc,
281 bool IsSelfClosing) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000282 Tag->setAttrs(Attrs);
283 Tag->setGreaterLoc(GreaterLoc);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000284 if (IsSelfClosing)
285 Tag->setSelfClosing();
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000286 else if (!isHTMLEndTagForbidden(Tag->getTagName()))
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000287 HTMLOpenTags.push_back(Tag);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000288 return Tag;
289}
290
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000291HTMLEndTagComment *Sema::actOnHTMLEndTag(SourceLocation LocBegin,
292 SourceLocation LocEnd,
293 StringRef TagName) {
294 HTMLEndTagComment *HET =
295 new (Allocator) HTMLEndTagComment(LocBegin, LocEnd, TagName);
296 if (isHTMLEndTagForbidden(TagName)) {
297 Diag(HET->getLocation(), diag::warn_doc_html_end_forbidden)
298 << TagName << HET->getSourceRange();
299 return HET;
Dmitri Gribenko3d986982012-07-12 23:37:09 +0000300 }
301
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000302 bool FoundOpen = false;
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000303 for (SmallVectorImpl<HTMLStartTagComment *>::const_reverse_iterator
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000304 I = HTMLOpenTags.rbegin(), E = HTMLOpenTags.rend();
305 I != E; ++I) {
306 if ((*I)->getTagName() == TagName) {
307 FoundOpen = true;
308 break;
309 }
310 }
311 if (!FoundOpen) {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000312 Diag(HET->getLocation(), diag::warn_doc_html_end_unbalanced)
313 << HET->getSourceRange();
314 return HET;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000315 }
316
317 while (!HTMLOpenTags.empty()) {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000318 const HTMLStartTagComment *HST = HTMLOpenTags.back();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000319 HTMLOpenTags.pop_back();
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000320 StringRef LastNotClosedTagName = HST->getTagName();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000321 if (LastNotClosedTagName == TagName)
322 break;
323
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000324 if (isHTMLEndTagOptional(LastNotClosedTagName))
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000325 continue;
326
327 bool OpenLineInvalid;
328 const unsigned OpenLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000329 HST->getLocation(),
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000330 &OpenLineInvalid);
331 bool CloseLineInvalid;
332 const unsigned CloseLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000333 HET->getLocation(),
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000334 &CloseLineInvalid);
335
336 if (OpenLineInvalid || CloseLineInvalid || OpenLine == CloseLine)
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000337 Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
338 << HST->getTagName() << HET->getTagName()
339 << HST->getSourceRange() << HET->getSourceRange();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000340 else {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000341 Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
342 << HST->getTagName() << HET->getTagName()
343 << HST->getSourceRange();
344 Diag(HET->getLocation(), diag::note_doc_html_end_tag)
345 << HET->getSourceRange();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000346 }
347 }
348
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000349 return HET;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000350}
351
352FullComment *Sema::actOnFullComment(
353 ArrayRef<BlockContentComment *> Blocks) {
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000354 SmallVector<ParamCommandComment *, 8> Params;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000355 return new (Allocator) FullComment(Blocks);
356}
357
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000358void Sema::checkBlockCommandEmptyParagraph(BlockCommandComment *Command) {
359 ParagraphComment *Paragraph = Command->getParagraph();
360 if (Paragraph->isWhitespace()) {
361 SourceLocation DiagLoc;
Dmitri Gribenko0eaf69d2012-07-13 19:02:42 +0000362 if (Command->getNumArgs() > 0)
363 DiagLoc = Command->getArgRange(Command->getNumArgs() - 1).getEnd();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000364 if (!DiagLoc.isValid())
365 DiagLoc = Command->getCommandNameRange().getEnd();
366 Diag(DiagLoc, diag::warn_doc_block_command_empty_paragraph)
367 << Command->getCommandName()
368 << Command->getSourceRange();
369 }
370}
371
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000372bool Sema::isFunctionDecl() {
Dmitri Gribenko00c59f72012-07-24 20:58:46 +0000373 if (!IsThisDeclInspected)
374 inspectThisDecl();
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000375 return IsFunctionDecl;
376}
377
378ArrayRef<const ParmVarDecl *> Sema::getParamVars() {
Dmitri Gribenko00c59f72012-07-24 20:58:46 +0000379 if (!IsThisDeclInspected)
380 inspectThisDecl();
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000381 return ParamVars;
382}
383
384void Sema::inspectThisDecl() {
385 if (!ThisDecl) {
386 IsFunctionDecl = false;
387 ParamVars = ArrayRef<const ParmVarDecl *>();
388 } else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(ThisDecl)) {
389 IsFunctionDecl = true;
390 ParamVars = ArrayRef<const ParmVarDecl *>(FD->param_begin(),
391 FD->getNumParams());
392 } else if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(ThisDecl)) {
393 IsFunctionDecl = true;
394 ParamVars = ArrayRef<const ParmVarDecl *>(MD->param_begin(),
395 MD->param_size());
396 } else {
397 IsFunctionDecl = false;
398 ParamVars = ArrayRef<const ParmVarDecl *>();
399 }
400 IsThisDeclInspected = true;
401}
402
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000403unsigned Sema::resolveParmVarReference(StringRef Name,
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000404 ArrayRef<const ParmVarDecl *> ParamVars) {
405 for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000406 const IdentifierInfo *II = ParamVars[i]->getIdentifier();
407 if (II && II->getName() == Name)
408 return i;
409 }
410 return ParamCommandComment::InvalidParamIndex;
411}
412
413unsigned Sema::correctTypoInParmVarReference(
414 StringRef Typo,
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000415 ArrayRef<const ParmVarDecl *> ParamVars) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000416 const unsigned MaxEditDistance = (Typo.size() + 2) / 3;
Richard Smith18b7f952012-07-11 22:33:59 +0000417 unsigned BestPVDIndex = 0;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000418 unsigned BestEditDistance = MaxEditDistance + 1;
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000419 for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000420 const IdentifierInfo *II = ParamVars[i]->getIdentifier();
421 if (II) {
422 StringRef Name = II->getName();
NAKAMURA Takumidc5796c2012-07-12 00:45:08 +0000423 unsigned MinPossibleEditDistance =
424 abs((int)Name.size() - (int)Typo.size());
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000425 if (MinPossibleEditDistance > 0 &&
426 Typo.size() / MinPossibleEditDistance < 3)
427 continue;
428
429 unsigned EditDistance = Typo.edit_distance(Name, true, MaxEditDistance);
430 if (EditDistance < BestEditDistance) {
431 BestEditDistance = EditDistance;
432 BestPVDIndex = i;
433 }
434 }
435 }
436
437 if (BestEditDistance <= MaxEditDistance)
438 return BestPVDIndex;
439 else
440 return ParamCommandComment::InvalidParamIndex;;
441}
442
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000443// TODO: tablegen
444bool Sema::isBlockCommand(StringRef Name) {
445 return llvm::StringSwitch<bool>(Name)
Dmitri Gribenko3d3d22c2012-07-18 00:44:55 +0000446 .Cases("brief", "short", true)
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000447 .Case("result", true)
448 .Case("return", true)
449 .Case("returns", true)
450 .Case("author", true)
451 .Case("authors", true)
452 .Case("pre", true)
453 .Case("post", true)
454 .Default(false) || isParamCommand(Name);
455}
456
457bool Sema::isParamCommand(StringRef Name) {
458 return llvm::StringSwitch<bool>(Name)
459 .Case("param", true)
460 .Case("arg", true)
461 .Default(false);
462}
463
464unsigned Sema::getBlockCommandNumArgs(StringRef Name) {
465 return llvm::StringSwitch<unsigned>(Name)
Dmitri Gribenko3d3d22c2012-07-18 00:44:55 +0000466 .Cases("brief", "short", 0)
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000467 .Case("pre", 0)
468 .Case("post", 0)
469 .Case("author", 0)
470 .Case("authors", 0)
471 .Default(0);
472}
473
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000474bool Sema::isInlineCommand(StringRef Name) const {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000475 return llvm::StringSwitch<bool>(Name)
Dmitri Gribenkoc48dd8e2012-07-19 00:21:03 +0000476 .Case("b", true)
477 .Cases("c", "p", true)
478 .Cases("a", "e", "em", true)
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000479 .Default(false);
480}
481
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000482InlineCommandComment::RenderKind
483Sema::getInlineCommandRenderKind(StringRef Name) const {
484 assert(isInlineCommand(Name));
485
486 return llvm::StringSwitch<InlineCommandComment::RenderKind>(Name)
487 .Case("b", InlineCommandComment::RenderBold)
488 .Cases("c", "p", InlineCommandComment::RenderMonospaced)
489 .Cases("a", "e", "em", InlineCommandComment::RenderEmphasized)
490 .Default(InlineCommandComment::RenderNormal);
491}
492
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000493bool Sema::isHTMLEndTagOptional(StringRef Name) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000494 return llvm::StringSwitch<bool>(Name)
Dmitri Gribenko3d986982012-07-12 23:37:09 +0000495 .Case("p", true)
496 .Case("li", true)
497 .Case("dt", true)
498 .Case("dd", true)
499 .Case("tr", true)
500 .Case("th", true)
501 .Case("td", true)
502 .Case("thead", true)
503 .Case("tfoot", true)
504 .Case("tbody", true)
505 .Case("colgroup", true)
506 .Default(false);
507}
508
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000509bool Sema::isHTMLEndTagForbidden(StringRef Name) {
Dmitri Gribenko3d986982012-07-12 23:37:09 +0000510 return llvm::StringSwitch<bool>(Name)
511 .Case("br", true)
512 .Case("hr", true)
513 .Case("img", true)
514 .Case("col", true)
515 .Default(false);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000516}
517
518} // end namespace comments
519} // end namespace clang
520