blob: 356609165509efff7a0f4049b54317670eb808d8 [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"
Dmitri Gribenko96b09862012-07-31 22:37:06 +000013#include "clang/AST/DeclTemplate.h"
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000014#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 Gribenko1ca7ecc2012-08-01 23:08:09 +000022 Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags),
23 ThisDeclInfo(NULL) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000024}
25
26void Sema::setDecl(const Decl *D) {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +000027 if (!D)
28 return;
29
30 ThisDeclInfo = new (Allocator) DeclInfo;
31 ThisDeclInfo->ThisDecl = D;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000032}
33
34ParagraphComment *Sema::actOnParagraphComment(
35 ArrayRef<InlineContentComment *> Content) {
36 return new (Allocator) ParagraphComment(Content);
37}
38
39BlockCommandComment *Sema::actOnBlockCommandStart(SourceLocation LocBegin,
40 SourceLocation LocEnd,
41 StringRef Name) {
42 return new (Allocator) BlockCommandComment(LocBegin, LocEnd, Name);
43}
44
45BlockCommandComment *Sema::actOnBlockCommandArgs(
46 BlockCommandComment *Command,
47 ArrayRef<BlockCommandComment::Argument> Args) {
48 Command->setArgs(Args);
49 return Command;
50}
51
52BlockCommandComment *Sema::actOnBlockCommandFinish(
53 BlockCommandComment *Command,
54 ParagraphComment *Paragraph) {
55 Command->setParagraph(Paragraph);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000056 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000057 return Command;
58}
59
60ParamCommandComment *Sema::actOnParamCommandStart(SourceLocation LocBegin,
61 SourceLocation LocEnd,
62 StringRef Name) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000063 ParamCommandComment *Command =
64 new (Allocator) ParamCommandComment(LocBegin, LocEnd, Name);
65
Dmitri Gribenko8487c522012-07-23 17:40:30 +000066 if (!isFunctionDecl())
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000067 Diag(Command->getLocation(),
68 diag::warn_doc_param_not_attached_to_a_function_decl)
69 << Command->getCommandNameRange();
70
71 return Command;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000072}
73
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000074ParamCommandComment *Sema::actOnParamCommandDirectionArg(
75 ParamCommandComment *Command,
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000076 SourceLocation ArgLocBegin,
77 SourceLocation ArgLocEnd,
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000078 StringRef Arg) {
79 ParamCommandComment::PassDirection Direction;
80 std::string ArgLower = Arg.lower();
81 // TODO: optimize: lower Name first (need an API in SmallString for that),
82 // after that StringSwitch.
83 if (ArgLower == "[in]")
84 Direction = ParamCommandComment::In;
85 else if (ArgLower == "[out]")
86 Direction = ParamCommandComment::Out;
87 else if (ArgLower == "[in,out]" || ArgLower == "[out,in]")
88 Direction = ParamCommandComment::InOut;
89 else {
90 // Remove spaces.
91 std::string::iterator O = ArgLower.begin();
92 for (std::string::iterator I = ArgLower.begin(), E = ArgLower.end();
93 I != E; ++I) {
94 const char C = *I;
95 if (C != ' ' && C != '\n' && C != '\r' &&
96 C != '\t' && C != '\v' && C != '\f')
97 *O++ = C;
98 }
99 ArgLower.resize(O - ArgLower.begin());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000100
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000101 bool RemovingWhitespaceHelped = false;
102 if (ArgLower == "[in]") {
103 Direction = ParamCommandComment::In;
104 RemovingWhitespaceHelped = true;
105 } else if (ArgLower == "[out]") {
106 Direction = ParamCommandComment::Out;
107 RemovingWhitespaceHelped = true;
108 } else if (ArgLower == "[in,out]" || ArgLower == "[out,in]") {
109 Direction = ParamCommandComment::InOut;
110 RemovingWhitespaceHelped = true;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000111 } else {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000112 Direction = ParamCommandComment::In;
113 RemovingWhitespaceHelped = false;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000114 }
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000115
116 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
117 if (RemovingWhitespaceHelped)
118 Diag(ArgLocBegin, diag::warn_doc_param_spaces_in_direction)
119 << ArgRange
120 << FixItHint::CreateReplacement(
121 ArgRange,
122 ParamCommandComment::getDirectionAsString(Direction));
123 else
124 Diag(ArgLocBegin, diag::warn_doc_param_invalid_direction)
125 << ArgRange;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000126 }
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000127 Command->setDirection(Direction, /* Explicit = */ true);
128 return Command;
129}
130
131ParamCommandComment *Sema::actOnParamCommandParamNameArg(
132 ParamCommandComment *Command,
133 SourceLocation ArgLocBegin,
134 SourceLocation ArgLocEnd,
135 StringRef Arg) {
136 // Parser will not feed us more arguments than needed.
Dmitri Gribenko0eaf69d2012-07-13 19:02:42 +0000137 assert(Command->getNumArgs() == 0);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000138
139 if (!Command->isDirectionExplicit()) {
140 // User didn't provide a direction argument.
141 Command->setDirection(ParamCommandComment::In, /* Explicit = */ false);
142 }
143 typedef BlockCommandComment::Argument Argument;
144 Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
145 ArgLocEnd),
146 Arg);
147 Command->setArgs(llvm::makeArrayRef(A, 1));
148
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000149 if (!isFunctionDecl()) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000150 // We already warned that this \\param is not attached to a function decl.
151 return Command;
152 }
153
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000154 ArrayRef<const ParmVarDecl *> ParamVars = getParamVars();
155
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000156 // Check that referenced parameter name is in the function decl.
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000157 const unsigned ResolvedParamIndex = resolveParmVarReference(Arg, ParamVars);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000158 if (ResolvedParamIndex != ParamCommandComment::InvalidParamIndex) {
159 Command->setParamIndex(ResolvedParamIndex);
Dmitri Gribenko65822772012-07-24 21:44:16 +0000160 if (ParamVarDocs[ResolvedParamIndex]) {
161 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
162 Diag(ArgLocBegin, diag::warn_doc_param_duplicate)
163 << Arg << ArgRange;
164 ParamCommandComment *PrevCommand = ParamVarDocs[ResolvedParamIndex];
165 Diag(PrevCommand->getLocation(), diag::note_doc_param_previous)
166 << PrevCommand->getParamNameRange();
167 }
168 ParamVarDocs[ResolvedParamIndex] = Command;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000169 return Command;
170 }
171
172 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
173 Diag(ArgLocBegin, diag::warn_doc_param_not_found)
174 << Arg << ArgRange;
175
Dmitri Gribenkobbb7af32012-07-27 21:34:43 +0000176 // No parameters -- can't suggest a correction.
177 if (ParamVars.size() == 0)
178 return Command;
179
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000180 unsigned CorrectedParamIndex = ParamCommandComment::InvalidParamIndex;
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000181 if (ParamVars.size() == 1) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000182 // If function has only one parameter then only that parameter
183 // can be documented.
184 CorrectedParamIndex = 0;
185 } else {
186 // Do typo correction.
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000187 CorrectedParamIndex = correctTypoInParmVarReference(Arg, ParamVars);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000188 }
189 if (CorrectedParamIndex != ParamCommandComment::InvalidParamIndex) {
190 const ParmVarDecl *CorrectedPVD = ParamVars[CorrectedParamIndex];
191 if (const IdentifierInfo *CorrectedII = CorrectedPVD->getIdentifier())
192 Diag(ArgLocBegin, diag::note_doc_param_name_suggestion)
193 << CorrectedII->getName()
194 << FixItHint::CreateReplacement(ArgRange, CorrectedII->getName());
195 }
196
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000197 return Command;
198}
199
200ParamCommandComment *Sema::actOnParamCommandFinish(ParamCommandComment *Command,
201 ParagraphComment *Paragraph) {
202 Command->setParagraph(Paragraph);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000203 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000204 return Command;
205}
206
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000207TParamCommandComment *Sema::actOnTParamCommandStart(SourceLocation LocBegin,
208 SourceLocation LocEnd,
209 StringRef Name) {
210 TParamCommandComment *Command =
211 new (Allocator) TParamCommandComment(LocBegin, LocEnd, Name);
212
213 if (!isTemplateDecl())
214 Diag(Command->getLocation(),
215 diag::warn_doc_tparam_not_attached_to_a_template_decl)
216 << Command->getCommandNameRange();
217
218 return Command;
219}
220
221TParamCommandComment *Sema::actOnTParamCommandParamNameArg(
222 TParamCommandComment *Command,
223 SourceLocation ArgLocBegin,
224 SourceLocation ArgLocEnd,
225 StringRef Arg) {
226 // Parser will not feed us more arguments than needed.
227 assert(Command->getNumArgs() == 0);
228
229 typedef BlockCommandComment::Argument Argument;
230 Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
231 ArgLocEnd),
232 Arg);
233 Command->setArgs(llvm::makeArrayRef(A, 1));
234
235 if (!isTemplateDecl()) {
236 // We already warned that this \\tparam is not attached to a template decl.
237 return Command;
238 }
239
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000240 const TemplateParameterList *TemplateParameters =
241 ThisDeclInfo->TemplateParameters;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000242 SmallVector<unsigned, 2> Position;
243 if (resolveTParamReference(Arg, TemplateParameters, &Position)) {
244 Command->setPosition(copyArray(llvm::makeArrayRef(Position)));
245 llvm::StringMap<TParamCommandComment *>::iterator PrevCommandIt =
246 TemplateParameterDocs.find(Arg);
247 if (PrevCommandIt != TemplateParameterDocs.end()) {
248 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
249 Diag(ArgLocBegin, diag::warn_doc_tparam_duplicate)
250 << Arg << ArgRange;
251 TParamCommandComment *PrevCommand = PrevCommandIt->second;
252 Diag(PrevCommand->getLocation(), diag::note_doc_tparam_previous)
253 << PrevCommand->getParamNameRange();
254 }
255 TemplateParameterDocs[Arg] = Command;
256 return Command;
257 }
258
259 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
260 Diag(ArgLocBegin, diag::warn_doc_tparam_not_found)
261 << Arg << ArgRange;
262
263 if (!TemplateParameters || TemplateParameters->size() == 0)
264 return Command;
265
266 StringRef CorrectedName;
267 if (TemplateParameters->size() == 1) {
268 const NamedDecl *Param = TemplateParameters->getParam(0);
269 const IdentifierInfo *II = Param->getIdentifier();
270 if (II)
271 CorrectedName = II->getName();
272 } else {
273 CorrectedName = correctTypoInTParamReference(Arg, TemplateParameters);
274 }
275
276 if (!CorrectedName.empty()) {
277 Diag(ArgLocBegin, diag::note_doc_tparam_name_suggestion)
278 << CorrectedName
279 << FixItHint::CreateReplacement(ArgRange, CorrectedName);
280 }
281
282 return Command;
283}
284
285TParamCommandComment *Sema::actOnTParamCommandFinish(
286 TParamCommandComment *Command,
287 ParagraphComment *Paragraph) {
288 Command->setParagraph(Paragraph);
289 checkBlockCommandEmptyParagraph(Command);
290 return Command;
291}
292
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000293InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
294 SourceLocation CommandLocEnd,
295 StringRef CommandName) {
296 ArrayRef<InlineCommandComment::Argument> Args;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000297 return new (Allocator) InlineCommandComment(
298 CommandLocBegin,
299 CommandLocEnd,
300 CommandName,
301 getInlineCommandRenderKind(CommandName),
302 Args);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000303}
304
305InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
306 SourceLocation CommandLocEnd,
307 StringRef CommandName,
308 SourceLocation ArgLocBegin,
309 SourceLocation ArgLocEnd,
310 StringRef Arg) {
311 typedef InlineCommandComment::Argument Argument;
312 Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
313 ArgLocEnd),
314 Arg);
315
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000316 return new (Allocator) InlineCommandComment(
317 CommandLocBegin,
318 CommandLocEnd,
319 CommandName,
320 getInlineCommandRenderKind(CommandName),
321 llvm::makeArrayRef(A, 1));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000322}
323
324InlineContentComment *Sema::actOnUnknownCommand(SourceLocation LocBegin,
325 SourceLocation LocEnd,
326 StringRef Name) {
327 ArrayRef<InlineCommandComment::Argument> Args;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000328 return new (Allocator) InlineCommandComment(
329 LocBegin, LocEnd, Name,
330 InlineCommandComment::RenderNormal,
331 Args);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000332}
333
334TextComment *Sema::actOnText(SourceLocation LocBegin,
335 SourceLocation LocEnd,
336 StringRef Text) {
337 return new (Allocator) TextComment(LocBegin, LocEnd, Text);
338}
339
340VerbatimBlockComment *Sema::actOnVerbatimBlockStart(SourceLocation Loc,
341 StringRef Name) {
342 return new (Allocator) VerbatimBlockComment(
343 Loc,
344 Loc.getLocWithOffset(1 + Name.size()),
345 Name);
346}
347
348VerbatimBlockLineComment *Sema::actOnVerbatimBlockLine(SourceLocation Loc,
349 StringRef Text) {
350 return new (Allocator) VerbatimBlockLineComment(Loc, Text);
351}
352
353VerbatimBlockComment *Sema::actOnVerbatimBlockFinish(
354 VerbatimBlockComment *Block,
355 SourceLocation CloseNameLocBegin,
356 StringRef CloseName,
357 ArrayRef<VerbatimBlockLineComment *> Lines) {
358 Block->setCloseName(CloseName, CloseNameLocBegin);
359 Block->setLines(Lines);
360 return Block;
361}
362
363VerbatimLineComment *Sema::actOnVerbatimLine(SourceLocation LocBegin,
364 StringRef Name,
365 SourceLocation TextBegin,
366 StringRef Text) {
367 return new (Allocator) VerbatimLineComment(
368 LocBegin,
369 TextBegin.getLocWithOffset(Text.size()),
370 Name,
371 TextBegin,
372 Text);
373}
374
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000375HTMLStartTagComment *Sema::actOnHTMLStartTagStart(SourceLocation LocBegin,
376 StringRef TagName) {
377 return new (Allocator) HTMLStartTagComment(LocBegin, TagName);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000378}
379
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000380HTMLStartTagComment *Sema::actOnHTMLStartTagFinish(
381 HTMLStartTagComment *Tag,
382 ArrayRef<HTMLStartTagComment::Attribute> Attrs,
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000383 SourceLocation GreaterLoc,
384 bool IsSelfClosing) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000385 Tag->setAttrs(Attrs);
386 Tag->setGreaterLoc(GreaterLoc);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000387 if (IsSelfClosing)
388 Tag->setSelfClosing();
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000389 else if (!isHTMLEndTagForbidden(Tag->getTagName()))
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000390 HTMLOpenTags.push_back(Tag);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000391 return Tag;
392}
393
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000394HTMLEndTagComment *Sema::actOnHTMLEndTag(SourceLocation LocBegin,
395 SourceLocation LocEnd,
396 StringRef TagName) {
397 HTMLEndTagComment *HET =
398 new (Allocator) HTMLEndTagComment(LocBegin, LocEnd, TagName);
399 if (isHTMLEndTagForbidden(TagName)) {
400 Diag(HET->getLocation(), diag::warn_doc_html_end_forbidden)
401 << TagName << HET->getSourceRange();
402 return HET;
Dmitri Gribenko3d986982012-07-12 23:37:09 +0000403 }
404
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000405 bool FoundOpen = false;
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000406 for (SmallVectorImpl<HTMLStartTagComment *>::const_reverse_iterator
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000407 I = HTMLOpenTags.rbegin(), E = HTMLOpenTags.rend();
408 I != E; ++I) {
409 if ((*I)->getTagName() == TagName) {
410 FoundOpen = true;
411 break;
412 }
413 }
414 if (!FoundOpen) {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000415 Diag(HET->getLocation(), diag::warn_doc_html_end_unbalanced)
416 << HET->getSourceRange();
417 return HET;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000418 }
419
420 while (!HTMLOpenTags.empty()) {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000421 const HTMLStartTagComment *HST = HTMLOpenTags.back();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000422 HTMLOpenTags.pop_back();
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000423 StringRef LastNotClosedTagName = HST->getTagName();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000424 if (LastNotClosedTagName == TagName)
425 break;
426
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000427 if (isHTMLEndTagOptional(LastNotClosedTagName))
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000428 continue;
429
430 bool OpenLineInvalid;
431 const unsigned OpenLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000432 HST->getLocation(),
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000433 &OpenLineInvalid);
434 bool CloseLineInvalid;
435 const unsigned CloseLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000436 HET->getLocation(),
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000437 &CloseLineInvalid);
438
439 if (OpenLineInvalid || CloseLineInvalid || OpenLine == CloseLine)
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000440 Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
441 << HST->getTagName() << HET->getTagName()
442 << HST->getSourceRange() << HET->getSourceRange();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000443 else {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000444 Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
445 << HST->getTagName() << HET->getTagName()
446 << HST->getSourceRange();
447 Diag(HET->getLocation(), diag::note_doc_html_end_tag)
448 << HET->getSourceRange();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000449 }
450 }
451
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000452 return HET;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000453}
454
455FullComment *Sema::actOnFullComment(
456 ArrayRef<BlockContentComment *> Blocks) {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000457 return new (Allocator) FullComment(Blocks, ThisDeclInfo);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000458}
459
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000460void Sema::checkBlockCommandEmptyParagraph(BlockCommandComment *Command) {
461 ParagraphComment *Paragraph = Command->getParagraph();
462 if (Paragraph->isWhitespace()) {
463 SourceLocation DiagLoc;
Dmitri Gribenko0eaf69d2012-07-13 19:02:42 +0000464 if (Command->getNumArgs() > 0)
465 DiagLoc = Command->getArgRange(Command->getNumArgs() - 1).getEnd();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000466 if (!DiagLoc.isValid())
467 DiagLoc = Command->getCommandNameRange().getEnd();
468 Diag(DiagLoc, diag::warn_doc_block_command_empty_paragraph)
469 << Command->getCommandName()
470 << Command->getSourceRange();
471 }
472}
473
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000474bool Sema::isFunctionDecl() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000475 if (!ThisDeclInfo)
476 return false;
477 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko00c59f72012-07-24 20:58:46 +0000478 inspectThisDecl();
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000479 return ThisDeclInfo->IsFunctionDecl;
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000480}
481
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000482bool Sema::isTemplateDecl() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000483 if (!ThisDeclInfo)
484 return false;
485 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000486 inspectThisDecl();
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000487 return ThisDeclInfo->IsTemplateDecl;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000488}
489
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000490ArrayRef<const ParmVarDecl *> Sema::getParamVars() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000491 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko00c59f72012-07-24 20:58:46 +0000492 inspectThisDecl();
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000493 return ThisDeclInfo->ParamVars;
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000494}
495
496void Sema::inspectThisDecl() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000497 ThisDeclInfo->fill();
498 ParamVarDocs.resize(ThisDeclInfo->ParamVars.size(), NULL);
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000499}
500
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000501unsigned Sema::resolveParmVarReference(StringRef Name,
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000502 ArrayRef<const ParmVarDecl *> ParamVars) {
503 for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000504 const IdentifierInfo *II = ParamVars[i]->getIdentifier();
505 if (II && II->getName() == Name)
506 return i;
507 }
508 return ParamCommandComment::InvalidParamIndex;
509}
510
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000511namespace {
512class SimpleTypoCorrector {
513 StringRef Typo;
514 const unsigned MaxEditDistance;
515
516 const NamedDecl *BestDecl;
517 unsigned BestEditDistance;
518 unsigned BestIndex;
519 unsigned NextIndex;
520
521public:
522 SimpleTypoCorrector(StringRef Typo) :
523 Typo(Typo), MaxEditDistance((Typo.size() + 2) / 3),
524 BestDecl(NULL), BestEditDistance(MaxEditDistance + 1),
525 BestIndex(0), NextIndex(0)
526 { }
527
528 void addDecl(const NamedDecl *ND);
529
530 const NamedDecl *getBestDecl() const {
531 if (BestEditDistance > MaxEditDistance)
532 return NULL;
533
534 return BestDecl;
535 }
536
537 unsigned getBestDeclIndex() const {
538 assert(getBestDecl());
539 return BestIndex;
540 }
541};
542
543void SimpleTypoCorrector::addDecl(const NamedDecl *ND) {
544 unsigned CurrIndex = NextIndex++;
545
546 const IdentifierInfo *II = ND->getIdentifier();
547 if (!II)
548 return;
549
550 StringRef Name = II->getName();
551 unsigned MinPossibleEditDistance = abs((int)Name.size() - (int)Typo.size());
552 if (MinPossibleEditDistance > 0 &&
553 Typo.size() / MinPossibleEditDistance < 3)
554 return;
555
556 unsigned EditDistance = Typo.edit_distance(Name, true, MaxEditDistance);
557 if (EditDistance < BestEditDistance) {
558 BestEditDistance = EditDistance;
559 BestDecl = ND;
560 BestIndex = CurrIndex;
561 }
562}
563} // unnamed namespace
564
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000565unsigned Sema::correctTypoInParmVarReference(
566 StringRef Typo,
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000567 ArrayRef<const ParmVarDecl *> ParamVars) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000568 SimpleTypoCorrector Corrector(Typo);
569 for (unsigned i = 0, e = ParamVars.size(); i != e; ++i)
570 Corrector.addDecl(ParamVars[i]);
571 if (Corrector.getBestDecl())
572 return Corrector.getBestDeclIndex();
573 else
574 return ParamCommandComment::InvalidParamIndex;;
575}
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000576
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000577namespace {
578bool ResolveTParamReferenceHelper(
579 StringRef Name,
580 const TemplateParameterList *TemplateParameters,
581 SmallVectorImpl<unsigned> *Position) {
582 for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
583 const NamedDecl *Param = TemplateParameters->getParam(i);
584 const IdentifierInfo *II = Param->getIdentifier();
585 if (II && II->getName() == Name) {
586 Position->push_back(i);
587 return true;
588 }
589
590 if (const TemplateTemplateParmDecl *TTP =
591 dyn_cast<TemplateTemplateParmDecl>(Param)) {
592 Position->push_back(i);
593 if (ResolveTParamReferenceHelper(Name, TTP->getTemplateParameters(),
594 Position))
595 return true;
596 Position->pop_back();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000597 }
598 }
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000599 return false;
600}
601} // unnamed namespace
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000602
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000603bool Sema::resolveTParamReference(
604 StringRef Name,
605 const TemplateParameterList *TemplateParameters,
606 SmallVectorImpl<unsigned> *Position) {
607 Position->clear();
608 if (!TemplateParameters)
609 return false;
610
611 return ResolveTParamReferenceHelper(Name, TemplateParameters, Position);
612}
613
614namespace {
615void CorrectTypoInTParamReferenceHelper(
616 const TemplateParameterList *TemplateParameters,
617 SimpleTypoCorrector &Corrector) {
618 for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
619 const NamedDecl *Param = TemplateParameters->getParam(i);
620 Corrector.addDecl(Param);
621
622 if (const TemplateTemplateParmDecl *TTP =
623 dyn_cast<TemplateTemplateParmDecl>(Param))
624 CorrectTypoInTParamReferenceHelper(TTP->getTemplateParameters(),
625 Corrector);
626 }
627}
628} // unnamed namespace
629
630StringRef Sema::correctTypoInTParamReference(
631 StringRef Typo,
632 const TemplateParameterList *TemplateParameters) {
633 SimpleTypoCorrector Corrector(Typo);
634 CorrectTypoInTParamReferenceHelper(TemplateParameters, Corrector);
635 if (const NamedDecl *ND = Corrector.getBestDecl()) {
636 const IdentifierInfo *II = ND->getIdentifier();
637 assert(II && "SimpleTypoCorrector should not return this decl");
638 return II->getName();
639 }
640 return StringRef();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000641}
642
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000643// TODO: tablegen
644bool Sema::isBlockCommand(StringRef Name) {
645 return llvm::StringSwitch<bool>(Name)
Dmitri Gribenko3d3d22c2012-07-18 00:44:55 +0000646 .Cases("brief", "short", true)
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000647 .Case("result", true)
648 .Case("return", true)
649 .Case("returns", true)
650 .Case("author", true)
651 .Case("authors", true)
652 .Case("pre", true)
653 .Case("post", true)
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000654 .Default(false) || isParamCommand(Name) || isTParamCommand(Name);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000655}
656
657bool Sema::isParamCommand(StringRef Name) {
658 return llvm::StringSwitch<bool>(Name)
659 .Case("param", true)
660 .Case("arg", true)
661 .Default(false);
662}
663
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000664bool Sema::isTParamCommand(StringRef Name) {
665 return Name == "tparam";
666}
667
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000668unsigned Sema::getBlockCommandNumArgs(StringRef Name) {
669 return llvm::StringSwitch<unsigned>(Name)
Dmitri Gribenko3d3d22c2012-07-18 00:44:55 +0000670 .Cases("brief", "short", 0)
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000671 .Case("pre", 0)
672 .Case("post", 0)
673 .Case("author", 0)
674 .Case("authors", 0)
675 .Default(0);
676}
677
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000678bool Sema::isInlineCommand(StringRef Name) const {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000679 return llvm::StringSwitch<bool>(Name)
Dmitri Gribenkoc48dd8e2012-07-19 00:21:03 +0000680 .Case("b", true)
681 .Cases("c", "p", true)
682 .Cases("a", "e", "em", true)
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000683 .Default(false);
684}
685
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000686InlineCommandComment::RenderKind
687Sema::getInlineCommandRenderKind(StringRef Name) const {
688 assert(isInlineCommand(Name));
689
690 return llvm::StringSwitch<InlineCommandComment::RenderKind>(Name)
691 .Case("b", InlineCommandComment::RenderBold)
692 .Cases("c", "p", InlineCommandComment::RenderMonospaced)
693 .Cases("a", "e", "em", InlineCommandComment::RenderEmphasized)
694 .Default(InlineCommandComment::RenderNormal);
695}
696
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000697bool Sema::isHTMLEndTagOptional(StringRef Name) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000698 return llvm::StringSwitch<bool>(Name)
Dmitri Gribenko3d986982012-07-12 23:37:09 +0000699 .Case("p", true)
700 .Case("li", true)
701 .Case("dt", true)
702 .Case("dd", true)
703 .Case("tr", true)
704 .Case("th", true)
705 .Case("td", true)
706 .Case("thead", true)
707 .Case("tfoot", true)
708 .Case("tbody", true)
709 .Case("colgroup", true)
710 .Default(false);
711}
712
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000713bool Sema::isHTMLEndTagForbidden(StringRef Name) {
Dmitri Gribenko3d986982012-07-12 23:37:09 +0000714 return llvm::StringSwitch<bool>(Name)
715 .Case("br", true)
716 .Case("hr", true)
717 .Case("img", true)
718 .Case("col", true)
719 .Default(false);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000720}
721
722} // end namespace comments
723} // end namespace clang
724