blob: 5301bfebe962bd40b2e292033256740b24f528f3 [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);
Dmitri Gribenko65822772012-07-24 21:44:16 +0000156 if (ParamVarDocs[ResolvedParamIndex]) {
157 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
158 Diag(ArgLocBegin, diag::warn_doc_param_duplicate)
159 << Arg << ArgRange;
160 ParamCommandComment *PrevCommand = ParamVarDocs[ResolvedParamIndex];
161 Diag(PrevCommand->getLocation(), diag::note_doc_param_previous)
162 << PrevCommand->getParamNameRange();
163 }
164 ParamVarDocs[ResolvedParamIndex] = Command;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000165 return Command;
166 }
167
168 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
169 Diag(ArgLocBegin, diag::warn_doc_param_not_found)
170 << Arg << ArgRange;
171
172 unsigned CorrectedParamIndex = ParamCommandComment::InvalidParamIndex;
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000173 if (ParamVars.size() == 1) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000174 // If function has only one parameter then only that parameter
175 // can be documented.
176 CorrectedParamIndex = 0;
177 } else {
178 // Do typo correction.
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000179 CorrectedParamIndex = correctTypoInParmVarReference(Arg, ParamVars);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000180 }
181 if (CorrectedParamIndex != ParamCommandComment::InvalidParamIndex) {
182 const ParmVarDecl *CorrectedPVD = ParamVars[CorrectedParamIndex];
183 if (const IdentifierInfo *CorrectedII = CorrectedPVD->getIdentifier())
184 Diag(ArgLocBegin, diag::note_doc_param_name_suggestion)
185 << CorrectedII->getName()
186 << FixItHint::CreateReplacement(ArgRange, CorrectedII->getName());
187 }
188
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000189 return Command;
190}
191
192ParamCommandComment *Sema::actOnParamCommandFinish(ParamCommandComment *Command,
193 ParagraphComment *Paragraph) {
194 Command->setParagraph(Paragraph);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000195 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000196 return Command;
197}
198
199InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
200 SourceLocation CommandLocEnd,
201 StringRef CommandName) {
202 ArrayRef<InlineCommandComment::Argument> Args;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000203 return new (Allocator) InlineCommandComment(
204 CommandLocBegin,
205 CommandLocEnd,
206 CommandName,
207 getInlineCommandRenderKind(CommandName),
208 Args);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000209}
210
211InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
212 SourceLocation CommandLocEnd,
213 StringRef CommandName,
214 SourceLocation ArgLocBegin,
215 SourceLocation ArgLocEnd,
216 StringRef Arg) {
217 typedef InlineCommandComment::Argument Argument;
218 Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
219 ArgLocEnd),
220 Arg);
221
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000222 return new (Allocator) InlineCommandComment(
223 CommandLocBegin,
224 CommandLocEnd,
225 CommandName,
226 getInlineCommandRenderKind(CommandName),
227 llvm::makeArrayRef(A, 1));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000228}
229
230InlineContentComment *Sema::actOnUnknownCommand(SourceLocation LocBegin,
231 SourceLocation LocEnd,
232 StringRef Name) {
233 ArrayRef<InlineCommandComment::Argument> Args;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000234 return new (Allocator) InlineCommandComment(
235 LocBegin, LocEnd, Name,
236 InlineCommandComment::RenderNormal,
237 Args);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000238}
239
240TextComment *Sema::actOnText(SourceLocation LocBegin,
241 SourceLocation LocEnd,
242 StringRef Text) {
243 return new (Allocator) TextComment(LocBegin, LocEnd, Text);
244}
245
246VerbatimBlockComment *Sema::actOnVerbatimBlockStart(SourceLocation Loc,
247 StringRef Name) {
248 return new (Allocator) VerbatimBlockComment(
249 Loc,
250 Loc.getLocWithOffset(1 + Name.size()),
251 Name);
252}
253
254VerbatimBlockLineComment *Sema::actOnVerbatimBlockLine(SourceLocation Loc,
255 StringRef Text) {
256 return new (Allocator) VerbatimBlockLineComment(Loc, Text);
257}
258
259VerbatimBlockComment *Sema::actOnVerbatimBlockFinish(
260 VerbatimBlockComment *Block,
261 SourceLocation CloseNameLocBegin,
262 StringRef CloseName,
263 ArrayRef<VerbatimBlockLineComment *> Lines) {
264 Block->setCloseName(CloseName, CloseNameLocBegin);
265 Block->setLines(Lines);
266 return Block;
267}
268
269VerbatimLineComment *Sema::actOnVerbatimLine(SourceLocation LocBegin,
270 StringRef Name,
271 SourceLocation TextBegin,
272 StringRef Text) {
273 return new (Allocator) VerbatimLineComment(
274 LocBegin,
275 TextBegin.getLocWithOffset(Text.size()),
276 Name,
277 TextBegin,
278 Text);
279}
280
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000281HTMLStartTagComment *Sema::actOnHTMLStartTagStart(SourceLocation LocBegin,
282 StringRef TagName) {
283 return new (Allocator) HTMLStartTagComment(LocBegin, TagName);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000284}
285
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000286HTMLStartTagComment *Sema::actOnHTMLStartTagFinish(
287 HTMLStartTagComment *Tag,
288 ArrayRef<HTMLStartTagComment::Attribute> Attrs,
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000289 SourceLocation GreaterLoc,
290 bool IsSelfClosing) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000291 Tag->setAttrs(Attrs);
292 Tag->setGreaterLoc(GreaterLoc);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000293 if (IsSelfClosing)
294 Tag->setSelfClosing();
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000295 else if (!isHTMLEndTagForbidden(Tag->getTagName()))
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000296 HTMLOpenTags.push_back(Tag);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000297 return Tag;
298}
299
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000300HTMLEndTagComment *Sema::actOnHTMLEndTag(SourceLocation LocBegin,
301 SourceLocation LocEnd,
302 StringRef TagName) {
303 HTMLEndTagComment *HET =
304 new (Allocator) HTMLEndTagComment(LocBegin, LocEnd, TagName);
305 if (isHTMLEndTagForbidden(TagName)) {
306 Diag(HET->getLocation(), diag::warn_doc_html_end_forbidden)
307 << TagName << HET->getSourceRange();
308 return HET;
Dmitri Gribenko3d986982012-07-12 23:37:09 +0000309 }
310
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000311 bool FoundOpen = false;
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000312 for (SmallVectorImpl<HTMLStartTagComment *>::const_reverse_iterator
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000313 I = HTMLOpenTags.rbegin(), E = HTMLOpenTags.rend();
314 I != E; ++I) {
315 if ((*I)->getTagName() == TagName) {
316 FoundOpen = true;
317 break;
318 }
319 }
320 if (!FoundOpen) {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000321 Diag(HET->getLocation(), diag::warn_doc_html_end_unbalanced)
322 << HET->getSourceRange();
323 return HET;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000324 }
325
326 while (!HTMLOpenTags.empty()) {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000327 const HTMLStartTagComment *HST = HTMLOpenTags.back();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000328 HTMLOpenTags.pop_back();
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000329 StringRef LastNotClosedTagName = HST->getTagName();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000330 if (LastNotClosedTagName == TagName)
331 break;
332
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000333 if (isHTMLEndTagOptional(LastNotClosedTagName))
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000334 continue;
335
336 bool OpenLineInvalid;
337 const unsigned OpenLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000338 HST->getLocation(),
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000339 &OpenLineInvalid);
340 bool CloseLineInvalid;
341 const unsigned CloseLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000342 HET->getLocation(),
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000343 &CloseLineInvalid);
344
345 if (OpenLineInvalid || CloseLineInvalid || OpenLine == CloseLine)
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000346 Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
347 << HST->getTagName() << HET->getTagName()
348 << HST->getSourceRange() << HET->getSourceRange();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000349 else {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000350 Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
351 << HST->getTagName() << HET->getTagName()
352 << HST->getSourceRange();
353 Diag(HET->getLocation(), diag::note_doc_html_end_tag)
354 << HET->getSourceRange();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000355 }
356 }
357
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000358 return HET;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000359}
360
361FullComment *Sema::actOnFullComment(
362 ArrayRef<BlockContentComment *> Blocks) {
363 return new (Allocator) FullComment(Blocks);
364}
365
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000366void Sema::checkBlockCommandEmptyParagraph(BlockCommandComment *Command) {
367 ParagraphComment *Paragraph = Command->getParagraph();
368 if (Paragraph->isWhitespace()) {
369 SourceLocation DiagLoc;
Dmitri Gribenko0eaf69d2012-07-13 19:02:42 +0000370 if (Command->getNumArgs() > 0)
371 DiagLoc = Command->getArgRange(Command->getNumArgs() - 1).getEnd();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000372 if (!DiagLoc.isValid())
373 DiagLoc = Command->getCommandNameRange().getEnd();
374 Diag(DiagLoc, diag::warn_doc_block_command_empty_paragraph)
375 << Command->getCommandName()
376 << Command->getSourceRange();
377 }
378}
379
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000380bool Sema::isFunctionDecl() {
Dmitri Gribenko00c59f72012-07-24 20:58:46 +0000381 if (!IsThisDeclInspected)
382 inspectThisDecl();
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000383 return IsFunctionDecl;
384}
385
386ArrayRef<const ParmVarDecl *> Sema::getParamVars() {
Dmitri Gribenko00c59f72012-07-24 20:58:46 +0000387 if (!IsThisDeclInspected)
388 inspectThisDecl();
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000389 return ParamVars;
390}
391
392void Sema::inspectThisDecl() {
Dmitri Gribenko65822772012-07-24 21:44:16 +0000393 assert(!IsThisDeclInspected);
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000394 if (!ThisDecl) {
395 IsFunctionDecl = false;
396 ParamVars = ArrayRef<const ParmVarDecl *>();
397 } else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(ThisDecl)) {
398 IsFunctionDecl = true;
399 ParamVars = ArrayRef<const ParmVarDecl *>(FD->param_begin(),
400 FD->getNumParams());
401 } else if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(ThisDecl)) {
402 IsFunctionDecl = true;
403 ParamVars = ArrayRef<const ParmVarDecl *>(MD->param_begin(),
404 MD->param_size());
405 } else {
406 IsFunctionDecl = false;
407 ParamVars = ArrayRef<const ParmVarDecl *>();
408 }
Dmitri Gribenko65822772012-07-24 21:44:16 +0000409 ParamVarDocs.resize(ParamVars.size(), NULL);
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000410 IsThisDeclInspected = true;
411}
412
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000413unsigned Sema::resolveParmVarReference(StringRef Name,
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000414 ArrayRef<const ParmVarDecl *> ParamVars) {
415 for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000416 const IdentifierInfo *II = ParamVars[i]->getIdentifier();
417 if (II && II->getName() == Name)
418 return i;
419 }
420 return ParamCommandComment::InvalidParamIndex;
421}
422
423unsigned Sema::correctTypoInParmVarReference(
424 StringRef Typo,
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000425 ArrayRef<const ParmVarDecl *> ParamVars) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000426 const unsigned MaxEditDistance = (Typo.size() + 2) / 3;
Richard Smith18b7f952012-07-11 22:33:59 +0000427 unsigned BestPVDIndex = 0;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000428 unsigned BestEditDistance = MaxEditDistance + 1;
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000429 for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000430 const IdentifierInfo *II = ParamVars[i]->getIdentifier();
431 if (II) {
432 StringRef Name = II->getName();
NAKAMURA Takumidc5796c2012-07-12 00:45:08 +0000433 unsigned MinPossibleEditDistance =
434 abs((int)Name.size() - (int)Typo.size());
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000435 if (MinPossibleEditDistance > 0 &&
436 Typo.size() / MinPossibleEditDistance < 3)
437 continue;
438
439 unsigned EditDistance = Typo.edit_distance(Name, true, MaxEditDistance);
440 if (EditDistance < BestEditDistance) {
441 BestEditDistance = EditDistance;
442 BestPVDIndex = i;
443 }
444 }
445 }
446
447 if (BestEditDistance <= MaxEditDistance)
448 return BestPVDIndex;
449 else
450 return ParamCommandComment::InvalidParamIndex;;
451}
452
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000453// TODO: tablegen
454bool Sema::isBlockCommand(StringRef Name) {
455 return llvm::StringSwitch<bool>(Name)
Dmitri Gribenko3d3d22c2012-07-18 00:44:55 +0000456 .Cases("brief", "short", true)
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000457 .Case("result", true)
458 .Case("return", true)
459 .Case("returns", true)
460 .Case("author", true)
461 .Case("authors", true)
462 .Case("pre", true)
463 .Case("post", true)
464 .Default(false) || isParamCommand(Name);
465}
466
467bool Sema::isParamCommand(StringRef Name) {
468 return llvm::StringSwitch<bool>(Name)
469 .Case("param", true)
470 .Case("arg", true)
471 .Default(false);
472}
473
474unsigned Sema::getBlockCommandNumArgs(StringRef Name) {
475 return llvm::StringSwitch<unsigned>(Name)
Dmitri Gribenko3d3d22c2012-07-18 00:44:55 +0000476 .Cases("brief", "short", 0)
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000477 .Case("pre", 0)
478 .Case("post", 0)
479 .Case("author", 0)
480 .Case("authors", 0)
481 .Default(0);
482}
483
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000484bool Sema::isInlineCommand(StringRef Name) const {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000485 return llvm::StringSwitch<bool>(Name)
Dmitri Gribenkoc48dd8e2012-07-19 00:21:03 +0000486 .Case("b", true)
487 .Cases("c", "p", true)
488 .Cases("a", "e", "em", true)
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000489 .Default(false);
490}
491
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000492InlineCommandComment::RenderKind
493Sema::getInlineCommandRenderKind(StringRef Name) const {
494 assert(isInlineCommand(Name));
495
496 return llvm::StringSwitch<InlineCommandComment::RenderKind>(Name)
497 .Case("b", InlineCommandComment::RenderBold)
498 .Cases("c", "p", InlineCommandComment::RenderMonospaced)
499 .Cases("a", "e", "em", InlineCommandComment::RenderEmphasized)
500 .Default(InlineCommandComment::RenderNormal);
501}
502
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000503bool Sema::isHTMLEndTagOptional(StringRef Name) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000504 return llvm::StringSwitch<bool>(Name)
Dmitri Gribenko3d986982012-07-12 23:37:09 +0000505 .Case("p", true)
506 .Case("li", true)
507 .Case("dt", true)
508 .Case("dd", true)
509 .Case("tr", true)
510 .Case("th", true)
511 .Case("td", true)
512 .Case("thead", true)
513 .Case("tfoot", true)
514 .Case("tbody", true)
515 .Case("colgroup", true)
516 .Default(false);
517}
518
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000519bool Sema::isHTMLEndTagForbidden(StringRef Name) {
Dmitri Gribenko3d986982012-07-12 23:37:09 +0000520 return llvm::StringSwitch<bool>(Name)
521 .Case("br", true)
522 .Case("hr", true)
523 .Case("img", true)
524 .Case("col", true)
525 .Default(false);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000526}
527
528} // end namespace comments
529} // end namespace clang
530