blob: 7b42c861d397741f2e892858369419a6885b1422 [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"
Dmitri Gribenko96b09862012-07-31 22:37:06 +000014#include "clang/AST/DeclTemplate.h"
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000015#include "clang/Basic/SourceManager.h"
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000016#include "llvm/ADT/StringSwitch.h"
17
18namespace clang {
19namespace comments {
20
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000021Sema::Sema(llvm::BumpPtrAllocator &Allocator, const SourceManager &SourceMgr,
22 DiagnosticsEngine &Diags) :
Dmitri Gribenko8487c522012-07-23 17:40:30 +000023 Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags), ThisDecl(NULL),
24 IsThisDeclInspected(false) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000025}
26
27void Sema::setDecl(const Decl *D) {
28 ThisDecl = D;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000029}
30
31ParagraphComment *Sema::actOnParagraphComment(
32 ArrayRef<InlineContentComment *> Content) {
33 return new (Allocator) ParagraphComment(Content);
34}
35
36BlockCommandComment *Sema::actOnBlockCommandStart(SourceLocation LocBegin,
37 SourceLocation LocEnd,
38 StringRef Name) {
39 return new (Allocator) BlockCommandComment(LocBegin, LocEnd, Name);
40}
41
42BlockCommandComment *Sema::actOnBlockCommandArgs(
43 BlockCommandComment *Command,
44 ArrayRef<BlockCommandComment::Argument> Args) {
45 Command->setArgs(Args);
46 return Command;
47}
48
49BlockCommandComment *Sema::actOnBlockCommandFinish(
50 BlockCommandComment *Command,
51 ParagraphComment *Paragraph) {
52 Command->setParagraph(Paragraph);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000053 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000054 return Command;
55}
56
57ParamCommandComment *Sema::actOnParamCommandStart(SourceLocation LocBegin,
58 SourceLocation LocEnd,
59 StringRef Name) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000060 ParamCommandComment *Command =
61 new (Allocator) ParamCommandComment(LocBegin, LocEnd, Name);
62
Dmitri Gribenko8487c522012-07-23 17:40:30 +000063 if (!isFunctionDecl())
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000064 Diag(Command->getLocation(),
65 diag::warn_doc_param_not_attached_to_a_function_decl)
66 << Command->getCommandNameRange();
67
68 return Command;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000069}
70
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000071ParamCommandComment *Sema::actOnParamCommandDirectionArg(
72 ParamCommandComment *Command,
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000073 SourceLocation ArgLocBegin,
74 SourceLocation ArgLocEnd,
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000075 StringRef Arg) {
76 ParamCommandComment::PassDirection Direction;
77 std::string ArgLower = Arg.lower();
78 // TODO: optimize: lower Name first (need an API in SmallString for that),
79 // after that StringSwitch.
80 if (ArgLower == "[in]")
81 Direction = ParamCommandComment::In;
82 else if (ArgLower == "[out]")
83 Direction = ParamCommandComment::Out;
84 else if (ArgLower == "[in,out]" || ArgLower == "[out,in]")
85 Direction = ParamCommandComment::InOut;
86 else {
87 // Remove spaces.
88 std::string::iterator O = ArgLower.begin();
89 for (std::string::iterator I = ArgLower.begin(), E = ArgLower.end();
90 I != E; ++I) {
91 const char C = *I;
92 if (C != ' ' && C != '\n' && C != '\r' &&
93 C != '\t' && C != '\v' && C != '\f')
94 *O++ = C;
95 }
96 ArgLower.resize(O - ArgLower.begin());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000097
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000098 bool RemovingWhitespaceHelped = false;
99 if (ArgLower == "[in]") {
100 Direction = ParamCommandComment::In;
101 RemovingWhitespaceHelped = true;
102 } else if (ArgLower == "[out]") {
103 Direction = ParamCommandComment::Out;
104 RemovingWhitespaceHelped = true;
105 } else if (ArgLower == "[in,out]" || ArgLower == "[out,in]") {
106 Direction = ParamCommandComment::InOut;
107 RemovingWhitespaceHelped = true;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000108 } else {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000109 Direction = ParamCommandComment::In;
110 RemovingWhitespaceHelped = false;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000111 }
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000112
113 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
114 if (RemovingWhitespaceHelped)
115 Diag(ArgLocBegin, diag::warn_doc_param_spaces_in_direction)
116 << ArgRange
117 << FixItHint::CreateReplacement(
118 ArgRange,
119 ParamCommandComment::getDirectionAsString(Direction));
120 else
121 Diag(ArgLocBegin, diag::warn_doc_param_invalid_direction)
122 << ArgRange;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000123 }
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000124 Command->setDirection(Direction, /* Explicit = */ true);
125 return Command;
126}
127
128ParamCommandComment *Sema::actOnParamCommandParamNameArg(
129 ParamCommandComment *Command,
130 SourceLocation ArgLocBegin,
131 SourceLocation ArgLocEnd,
132 StringRef Arg) {
133 // Parser will not feed us more arguments than needed.
Dmitri Gribenko0eaf69d2012-07-13 19:02:42 +0000134 assert(Command->getNumArgs() == 0);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000135
136 if (!Command->isDirectionExplicit()) {
137 // User didn't provide a direction argument.
138 Command->setDirection(ParamCommandComment::In, /* Explicit = */ false);
139 }
140 typedef BlockCommandComment::Argument Argument;
141 Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
142 ArgLocEnd),
143 Arg);
144 Command->setArgs(llvm::makeArrayRef(A, 1));
145
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000146 if (!isFunctionDecl()) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000147 // We already warned that this \\param is not attached to a function decl.
148 return Command;
149 }
150
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000151 ArrayRef<const ParmVarDecl *> ParamVars = getParamVars();
152
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000153 // Check that referenced parameter name is in the function decl.
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000154 const unsigned ResolvedParamIndex = resolveParmVarReference(Arg, ParamVars);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000155 if (ResolvedParamIndex != ParamCommandComment::InvalidParamIndex) {
156 Command->setParamIndex(ResolvedParamIndex);
Dmitri Gribenko65822772012-07-24 21:44:16 +0000157 if (ParamVarDocs[ResolvedParamIndex]) {
158 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
159 Diag(ArgLocBegin, diag::warn_doc_param_duplicate)
160 << Arg << ArgRange;
161 ParamCommandComment *PrevCommand = ParamVarDocs[ResolvedParamIndex];
162 Diag(PrevCommand->getLocation(), diag::note_doc_param_previous)
163 << PrevCommand->getParamNameRange();
164 }
165 ParamVarDocs[ResolvedParamIndex] = Command;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000166 return Command;
167 }
168
169 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
170 Diag(ArgLocBegin, diag::warn_doc_param_not_found)
171 << Arg << ArgRange;
172
Dmitri Gribenkobbb7af32012-07-27 21:34:43 +0000173 // No parameters -- can't suggest a correction.
174 if (ParamVars.size() == 0)
175 return Command;
176
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000177 unsigned CorrectedParamIndex = ParamCommandComment::InvalidParamIndex;
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000178 if (ParamVars.size() == 1) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000179 // If function has only one parameter then only that parameter
180 // can be documented.
181 CorrectedParamIndex = 0;
182 } else {
183 // Do typo correction.
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000184 CorrectedParamIndex = correctTypoInParmVarReference(Arg, ParamVars);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000185 }
186 if (CorrectedParamIndex != ParamCommandComment::InvalidParamIndex) {
187 const ParmVarDecl *CorrectedPVD = ParamVars[CorrectedParamIndex];
188 if (const IdentifierInfo *CorrectedII = CorrectedPVD->getIdentifier())
189 Diag(ArgLocBegin, diag::note_doc_param_name_suggestion)
190 << CorrectedII->getName()
191 << FixItHint::CreateReplacement(ArgRange, CorrectedII->getName());
192 }
193
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000194 return Command;
195}
196
197ParamCommandComment *Sema::actOnParamCommandFinish(ParamCommandComment *Command,
198 ParagraphComment *Paragraph) {
199 Command->setParagraph(Paragraph);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000200 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000201 return Command;
202}
203
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000204TParamCommandComment *Sema::actOnTParamCommandStart(SourceLocation LocBegin,
205 SourceLocation LocEnd,
206 StringRef Name) {
207 TParamCommandComment *Command =
208 new (Allocator) TParamCommandComment(LocBegin, LocEnd, Name);
209
210 if (!isTemplateDecl())
211 Diag(Command->getLocation(),
212 diag::warn_doc_tparam_not_attached_to_a_template_decl)
213 << Command->getCommandNameRange();
214
215 return Command;
216}
217
218TParamCommandComment *Sema::actOnTParamCommandParamNameArg(
219 TParamCommandComment *Command,
220 SourceLocation ArgLocBegin,
221 SourceLocation ArgLocEnd,
222 StringRef Arg) {
223 // Parser will not feed us more arguments than needed.
224 assert(Command->getNumArgs() == 0);
225
226 typedef BlockCommandComment::Argument Argument;
227 Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
228 ArgLocEnd),
229 Arg);
230 Command->setArgs(llvm::makeArrayRef(A, 1));
231
232 if (!isTemplateDecl()) {
233 // We already warned that this \\tparam is not attached to a template decl.
234 return Command;
235 }
236
237 SmallVector<unsigned, 2> Position;
238 if (resolveTParamReference(Arg, TemplateParameters, &Position)) {
239 Command->setPosition(copyArray(llvm::makeArrayRef(Position)));
240 llvm::StringMap<TParamCommandComment *>::iterator PrevCommandIt =
241 TemplateParameterDocs.find(Arg);
242 if (PrevCommandIt != TemplateParameterDocs.end()) {
243 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
244 Diag(ArgLocBegin, diag::warn_doc_tparam_duplicate)
245 << Arg << ArgRange;
246 TParamCommandComment *PrevCommand = PrevCommandIt->second;
247 Diag(PrevCommand->getLocation(), diag::note_doc_tparam_previous)
248 << PrevCommand->getParamNameRange();
249 }
250 TemplateParameterDocs[Arg] = Command;
251 return Command;
252 }
253
254 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
255 Diag(ArgLocBegin, diag::warn_doc_tparam_not_found)
256 << Arg << ArgRange;
257
258 if (!TemplateParameters || TemplateParameters->size() == 0)
259 return Command;
260
261 StringRef CorrectedName;
262 if (TemplateParameters->size() == 1) {
263 const NamedDecl *Param = TemplateParameters->getParam(0);
264 const IdentifierInfo *II = Param->getIdentifier();
265 if (II)
266 CorrectedName = II->getName();
267 } else {
268 CorrectedName = correctTypoInTParamReference(Arg, TemplateParameters);
269 }
270
271 if (!CorrectedName.empty()) {
272 Diag(ArgLocBegin, diag::note_doc_tparam_name_suggestion)
273 << CorrectedName
274 << FixItHint::CreateReplacement(ArgRange, CorrectedName);
275 }
276
277 return Command;
278}
279
280TParamCommandComment *Sema::actOnTParamCommandFinish(
281 TParamCommandComment *Command,
282 ParagraphComment *Paragraph) {
283 Command->setParagraph(Paragraph);
284 checkBlockCommandEmptyParagraph(Command);
285 return Command;
286}
287
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000288InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
289 SourceLocation CommandLocEnd,
290 StringRef CommandName) {
291 ArrayRef<InlineCommandComment::Argument> Args;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000292 return new (Allocator) InlineCommandComment(
293 CommandLocBegin,
294 CommandLocEnd,
295 CommandName,
296 getInlineCommandRenderKind(CommandName),
297 Args);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000298}
299
300InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
301 SourceLocation CommandLocEnd,
302 StringRef CommandName,
303 SourceLocation ArgLocBegin,
304 SourceLocation ArgLocEnd,
305 StringRef Arg) {
306 typedef InlineCommandComment::Argument Argument;
307 Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
308 ArgLocEnd),
309 Arg);
310
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000311 return new (Allocator) InlineCommandComment(
312 CommandLocBegin,
313 CommandLocEnd,
314 CommandName,
315 getInlineCommandRenderKind(CommandName),
316 llvm::makeArrayRef(A, 1));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000317}
318
319InlineContentComment *Sema::actOnUnknownCommand(SourceLocation LocBegin,
320 SourceLocation LocEnd,
321 StringRef Name) {
322 ArrayRef<InlineCommandComment::Argument> Args;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000323 return new (Allocator) InlineCommandComment(
324 LocBegin, LocEnd, Name,
325 InlineCommandComment::RenderNormal,
326 Args);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000327}
328
329TextComment *Sema::actOnText(SourceLocation LocBegin,
330 SourceLocation LocEnd,
331 StringRef Text) {
332 return new (Allocator) TextComment(LocBegin, LocEnd, Text);
333}
334
335VerbatimBlockComment *Sema::actOnVerbatimBlockStart(SourceLocation Loc,
336 StringRef Name) {
337 return new (Allocator) VerbatimBlockComment(
338 Loc,
339 Loc.getLocWithOffset(1 + Name.size()),
340 Name);
341}
342
343VerbatimBlockLineComment *Sema::actOnVerbatimBlockLine(SourceLocation Loc,
344 StringRef Text) {
345 return new (Allocator) VerbatimBlockLineComment(Loc, Text);
346}
347
348VerbatimBlockComment *Sema::actOnVerbatimBlockFinish(
349 VerbatimBlockComment *Block,
350 SourceLocation CloseNameLocBegin,
351 StringRef CloseName,
352 ArrayRef<VerbatimBlockLineComment *> Lines) {
353 Block->setCloseName(CloseName, CloseNameLocBegin);
354 Block->setLines(Lines);
355 return Block;
356}
357
358VerbatimLineComment *Sema::actOnVerbatimLine(SourceLocation LocBegin,
359 StringRef Name,
360 SourceLocation TextBegin,
361 StringRef Text) {
362 return new (Allocator) VerbatimLineComment(
363 LocBegin,
364 TextBegin.getLocWithOffset(Text.size()),
365 Name,
366 TextBegin,
367 Text);
368}
369
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000370HTMLStartTagComment *Sema::actOnHTMLStartTagStart(SourceLocation LocBegin,
371 StringRef TagName) {
372 return new (Allocator) HTMLStartTagComment(LocBegin, TagName);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000373}
374
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000375HTMLStartTagComment *Sema::actOnHTMLStartTagFinish(
376 HTMLStartTagComment *Tag,
377 ArrayRef<HTMLStartTagComment::Attribute> Attrs,
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000378 SourceLocation GreaterLoc,
379 bool IsSelfClosing) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000380 Tag->setAttrs(Attrs);
381 Tag->setGreaterLoc(GreaterLoc);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000382 if (IsSelfClosing)
383 Tag->setSelfClosing();
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000384 else if (!isHTMLEndTagForbidden(Tag->getTagName()))
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000385 HTMLOpenTags.push_back(Tag);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000386 return Tag;
387}
388
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000389HTMLEndTagComment *Sema::actOnHTMLEndTag(SourceLocation LocBegin,
390 SourceLocation LocEnd,
391 StringRef TagName) {
392 HTMLEndTagComment *HET =
393 new (Allocator) HTMLEndTagComment(LocBegin, LocEnd, TagName);
394 if (isHTMLEndTagForbidden(TagName)) {
395 Diag(HET->getLocation(), diag::warn_doc_html_end_forbidden)
396 << TagName << HET->getSourceRange();
397 return HET;
Dmitri Gribenko3d986982012-07-12 23:37:09 +0000398 }
399
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000400 bool FoundOpen = false;
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000401 for (SmallVectorImpl<HTMLStartTagComment *>::const_reverse_iterator
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000402 I = HTMLOpenTags.rbegin(), E = HTMLOpenTags.rend();
403 I != E; ++I) {
404 if ((*I)->getTagName() == TagName) {
405 FoundOpen = true;
406 break;
407 }
408 }
409 if (!FoundOpen) {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000410 Diag(HET->getLocation(), diag::warn_doc_html_end_unbalanced)
411 << HET->getSourceRange();
412 return HET;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000413 }
414
415 while (!HTMLOpenTags.empty()) {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000416 const HTMLStartTagComment *HST = HTMLOpenTags.back();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000417 HTMLOpenTags.pop_back();
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000418 StringRef LastNotClosedTagName = HST->getTagName();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000419 if (LastNotClosedTagName == TagName)
420 break;
421
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000422 if (isHTMLEndTagOptional(LastNotClosedTagName))
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000423 continue;
424
425 bool OpenLineInvalid;
426 const unsigned OpenLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000427 HST->getLocation(),
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000428 &OpenLineInvalid);
429 bool CloseLineInvalid;
430 const unsigned CloseLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000431 HET->getLocation(),
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000432 &CloseLineInvalid);
433
434 if (OpenLineInvalid || CloseLineInvalid || OpenLine == CloseLine)
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000435 Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
436 << HST->getTagName() << HET->getTagName()
437 << HST->getSourceRange() << HET->getSourceRange();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000438 else {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000439 Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
440 << HST->getTagName() << HET->getTagName()
441 << HST->getSourceRange();
442 Diag(HET->getLocation(), diag::note_doc_html_end_tag)
443 << HET->getSourceRange();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000444 }
445 }
446
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000447 return HET;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000448}
449
450FullComment *Sema::actOnFullComment(
451 ArrayRef<BlockContentComment *> Blocks) {
452 return new (Allocator) FullComment(Blocks);
453}
454
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000455void Sema::checkBlockCommandEmptyParagraph(BlockCommandComment *Command) {
456 ParagraphComment *Paragraph = Command->getParagraph();
457 if (Paragraph->isWhitespace()) {
458 SourceLocation DiagLoc;
Dmitri Gribenko0eaf69d2012-07-13 19:02:42 +0000459 if (Command->getNumArgs() > 0)
460 DiagLoc = Command->getArgRange(Command->getNumArgs() - 1).getEnd();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000461 if (!DiagLoc.isValid())
462 DiagLoc = Command->getCommandNameRange().getEnd();
463 Diag(DiagLoc, diag::warn_doc_block_command_empty_paragraph)
464 << Command->getCommandName()
465 << Command->getSourceRange();
466 }
467}
468
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000469bool Sema::isFunctionDecl() {
Dmitri Gribenko00c59f72012-07-24 20:58:46 +0000470 if (!IsThisDeclInspected)
471 inspectThisDecl();
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000472 return IsFunctionDecl;
473}
474
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000475bool Sema::isTemplateDecl() {
476 if (!IsThisDeclInspected)
477 inspectThisDecl();
478 return IsTemplateDecl;
479}
480
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000481ArrayRef<const ParmVarDecl *> Sema::getParamVars() {
Dmitri Gribenko00c59f72012-07-24 20:58:46 +0000482 if (!IsThisDeclInspected)
483 inspectThisDecl();
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000484 return ParamVars;
485}
486
487void Sema::inspectThisDecl() {
Dmitri Gribenko65822772012-07-24 21:44:16 +0000488 assert(!IsThisDeclInspected);
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000489 if (!ThisDecl) {
490 IsFunctionDecl = false;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000491 IsTemplateDecl = false;
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000492 ParamVars = ArrayRef<const ParmVarDecl *>();
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000493 TemplateParameters = NULL;
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000494 } else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(ThisDecl)) {
495 IsFunctionDecl = true;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000496 IsTemplateDecl = false;
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000497 ParamVars = ArrayRef<const ParmVarDecl *>(FD->param_begin(),
498 FD->getNumParams());
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000499 TemplateParameters = NULL;
500 unsigned NumLists = FD->getNumTemplateParameterLists();
501 if (NumLists != 0) {
502 IsTemplateDecl = true;
503 TemplateParameters = FD->getTemplateParameterList(NumLists - 1);
504 }
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000505 } else if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(ThisDecl)) {
506 IsFunctionDecl = true;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000507 IsTemplateDecl = false;
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000508 ParamVars = ArrayRef<const ParmVarDecl *>(MD->param_begin(),
509 MD->param_size());
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000510 TemplateParameters = NULL;
511 } else if (const FunctionTemplateDecl *FTD =
512 dyn_cast<FunctionTemplateDecl>(ThisDecl)) {
513 IsFunctionDecl = true;
514 IsTemplateDecl = true;
515 const FunctionDecl *FD = FTD->getTemplatedDecl();
516 ParamVars = ArrayRef<const ParmVarDecl *>(FD->param_begin(),
517 FD->getNumParams());
518 TemplateParameters = FTD->getTemplateParameters();
519 } else if (const ClassTemplateDecl *CTD =
520 dyn_cast<ClassTemplateDecl>(ThisDecl)) {
521 IsFunctionDecl = false;
522 IsTemplateDecl = true;
523 ParamVars = ArrayRef<const ParmVarDecl *>();
524 TemplateParameters = CTD->getTemplateParameters();
525 } else if (const ClassTemplatePartialSpecializationDecl *CTPSD =
526 dyn_cast<ClassTemplatePartialSpecializationDecl>(ThisDecl)) {
527 IsFunctionDecl = false;
528 IsTemplateDecl = true;
529 ParamVars = ArrayRef<const ParmVarDecl *>();
530 TemplateParameters = CTPSD->getTemplateParameters();
531 } else if (isa<ClassTemplateSpecializationDecl>(ThisDecl)) {
532 IsFunctionDecl = false;
533 IsTemplateDecl = true;
534 ParamVars = ArrayRef<const ParmVarDecl *>();
535 TemplateParameters = NULL;
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000536 } else {
537 IsFunctionDecl = false;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000538 IsTemplateDecl = false;
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000539 ParamVars = ArrayRef<const ParmVarDecl *>();
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000540 TemplateParameters = NULL;
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000541 }
Dmitri Gribenko65822772012-07-24 21:44:16 +0000542 ParamVarDocs.resize(ParamVars.size(), NULL);
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000543 IsThisDeclInspected = true;
544}
545
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000546unsigned Sema::resolveParmVarReference(StringRef Name,
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000547 ArrayRef<const ParmVarDecl *> ParamVars) {
548 for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000549 const IdentifierInfo *II = ParamVars[i]->getIdentifier();
550 if (II && II->getName() == Name)
551 return i;
552 }
553 return ParamCommandComment::InvalidParamIndex;
554}
555
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000556namespace {
557class SimpleTypoCorrector {
558 StringRef Typo;
559 const unsigned MaxEditDistance;
560
561 const NamedDecl *BestDecl;
562 unsigned BestEditDistance;
563 unsigned BestIndex;
564 unsigned NextIndex;
565
566public:
567 SimpleTypoCorrector(StringRef Typo) :
568 Typo(Typo), MaxEditDistance((Typo.size() + 2) / 3),
569 BestDecl(NULL), BestEditDistance(MaxEditDistance + 1),
570 BestIndex(0), NextIndex(0)
571 { }
572
573 void addDecl(const NamedDecl *ND);
574
575 const NamedDecl *getBestDecl() const {
576 if (BestEditDistance > MaxEditDistance)
577 return NULL;
578
579 return BestDecl;
580 }
581
582 unsigned getBestDeclIndex() const {
583 assert(getBestDecl());
584 return BestIndex;
585 }
586};
587
588void SimpleTypoCorrector::addDecl(const NamedDecl *ND) {
589 unsigned CurrIndex = NextIndex++;
590
591 const IdentifierInfo *II = ND->getIdentifier();
592 if (!II)
593 return;
594
595 StringRef Name = II->getName();
596 unsigned MinPossibleEditDistance = abs((int)Name.size() - (int)Typo.size());
597 if (MinPossibleEditDistance > 0 &&
598 Typo.size() / MinPossibleEditDistance < 3)
599 return;
600
601 unsigned EditDistance = Typo.edit_distance(Name, true, MaxEditDistance);
602 if (EditDistance < BestEditDistance) {
603 BestEditDistance = EditDistance;
604 BestDecl = ND;
605 BestIndex = CurrIndex;
606 }
607}
608} // unnamed namespace
609
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000610unsigned Sema::correctTypoInParmVarReference(
611 StringRef Typo,
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000612 ArrayRef<const ParmVarDecl *> ParamVars) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000613 SimpleTypoCorrector Corrector(Typo);
614 for (unsigned i = 0, e = ParamVars.size(); i != e; ++i)
615 Corrector.addDecl(ParamVars[i]);
616 if (Corrector.getBestDecl())
617 return Corrector.getBestDeclIndex();
618 else
619 return ParamCommandComment::InvalidParamIndex;;
620}
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000621
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000622namespace {
623bool ResolveTParamReferenceHelper(
624 StringRef Name,
625 const TemplateParameterList *TemplateParameters,
626 SmallVectorImpl<unsigned> *Position) {
627 for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
628 const NamedDecl *Param = TemplateParameters->getParam(i);
629 const IdentifierInfo *II = Param->getIdentifier();
630 if (II && II->getName() == Name) {
631 Position->push_back(i);
632 return true;
633 }
634
635 if (const TemplateTemplateParmDecl *TTP =
636 dyn_cast<TemplateTemplateParmDecl>(Param)) {
637 Position->push_back(i);
638 if (ResolveTParamReferenceHelper(Name, TTP->getTemplateParameters(),
639 Position))
640 return true;
641 Position->pop_back();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000642 }
643 }
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000644 return false;
645}
646} // unnamed namespace
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000647
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000648bool Sema::resolveTParamReference(
649 StringRef Name,
650 const TemplateParameterList *TemplateParameters,
651 SmallVectorImpl<unsigned> *Position) {
652 Position->clear();
653 if (!TemplateParameters)
654 return false;
655
656 return ResolveTParamReferenceHelper(Name, TemplateParameters, Position);
657}
658
659namespace {
660void CorrectTypoInTParamReferenceHelper(
661 const TemplateParameterList *TemplateParameters,
662 SimpleTypoCorrector &Corrector) {
663 for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
664 const NamedDecl *Param = TemplateParameters->getParam(i);
665 Corrector.addDecl(Param);
666
667 if (const TemplateTemplateParmDecl *TTP =
668 dyn_cast<TemplateTemplateParmDecl>(Param))
669 CorrectTypoInTParamReferenceHelper(TTP->getTemplateParameters(),
670 Corrector);
671 }
672}
673} // unnamed namespace
674
675StringRef Sema::correctTypoInTParamReference(
676 StringRef Typo,
677 const TemplateParameterList *TemplateParameters) {
678 SimpleTypoCorrector Corrector(Typo);
679 CorrectTypoInTParamReferenceHelper(TemplateParameters, Corrector);
680 if (const NamedDecl *ND = Corrector.getBestDecl()) {
681 const IdentifierInfo *II = ND->getIdentifier();
682 assert(II && "SimpleTypoCorrector should not return this decl");
683 return II->getName();
684 }
685 return StringRef();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000686}
687
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000688// TODO: tablegen
689bool Sema::isBlockCommand(StringRef Name) {
690 return llvm::StringSwitch<bool>(Name)
Dmitri Gribenko3d3d22c2012-07-18 00:44:55 +0000691 .Cases("brief", "short", true)
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000692 .Case("result", true)
693 .Case("return", true)
694 .Case("returns", true)
695 .Case("author", true)
696 .Case("authors", true)
697 .Case("pre", true)
698 .Case("post", true)
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000699 .Default(false) || isParamCommand(Name) || isTParamCommand(Name);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000700}
701
702bool Sema::isParamCommand(StringRef Name) {
703 return llvm::StringSwitch<bool>(Name)
704 .Case("param", true)
705 .Case("arg", true)
706 .Default(false);
707}
708
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000709bool Sema::isTParamCommand(StringRef Name) {
710 return Name == "tparam";
711}
712
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000713unsigned Sema::getBlockCommandNumArgs(StringRef Name) {
714 return llvm::StringSwitch<unsigned>(Name)
Dmitri Gribenko3d3d22c2012-07-18 00:44:55 +0000715 .Cases("brief", "short", 0)
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000716 .Case("pre", 0)
717 .Case("post", 0)
718 .Case("author", 0)
719 .Case("authors", 0)
720 .Default(0);
721}
722
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000723bool Sema::isInlineCommand(StringRef Name) const {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000724 return llvm::StringSwitch<bool>(Name)
Dmitri Gribenkoc48dd8e2012-07-19 00:21:03 +0000725 .Case("b", true)
726 .Cases("c", "p", true)
727 .Cases("a", "e", "em", true)
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000728 .Default(false);
729}
730
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000731InlineCommandComment::RenderKind
732Sema::getInlineCommandRenderKind(StringRef Name) const {
733 assert(isInlineCommand(Name));
734
735 return llvm::StringSwitch<InlineCommandComment::RenderKind>(Name)
736 .Case("b", InlineCommandComment::RenderBold)
737 .Cases("c", "p", InlineCommandComment::RenderMonospaced)
738 .Cases("a", "e", "em", InlineCommandComment::RenderEmphasized)
739 .Default(InlineCommandComment::RenderNormal);
740}
741
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000742bool Sema::isHTMLEndTagOptional(StringRef Name) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000743 return llvm::StringSwitch<bool>(Name)
Dmitri Gribenko3d986982012-07-12 23:37:09 +0000744 .Case("p", true)
745 .Case("li", true)
746 .Case("dt", true)
747 .Case("dd", true)
748 .Case("tr", true)
749 .Case("th", true)
750 .Case("td", true)
751 .Case("thead", true)
752 .Case("tfoot", true)
753 .Case("tbody", true)
754 .Case("colgroup", true)
755 .Default(false);
756}
757
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000758bool Sema::isHTMLEndTagForbidden(StringRef Name) {
Dmitri Gribenko3d986982012-07-12 23:37:09 +0000759 return llvm::StringSwitch<bool>(Name)
760 .Case("br", true)
761 .Case("hr", true)
762 .Case("img", true)
763 .Case("col", true)
764 .Default(false);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000765}
766
767} // end namespace comments
768} // end namespace clang
769