blob: c8422508d38174df7470c88bacec4d418af04eb4 [file] [log] [blame]
Dmitri Gribenkoec925312012-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 Gribenkof26054f2012-07-11 21:38:39 +000011#include "clang/AST/CommentDiagnostic.h"
12#include "clang/AST/Decl.h"
Dmitri Gribenko34df2202012-07-31 22:37:06 +000013#include "clang/AST/DeclTemplate.h"
Dmitri Gribenkof26054f2012-07-11 21:38:39 +000014#include "clang/Basic/SourceManager.h"
Dmitri Gribenkoec925312012-07-06 00:28:32 +000015#include "llvm/ADT/StringSwitch.h"
16
17namespace clang {
18namespace comments {
19
Dmitri Gribenkof26054f2012-07-11 21:38:39 +000020Sema::Sema(llvm::BumpPtrAllocator &Allocator, const SourceManager &SourceMgr,
21 DiagnosticsEngine &Diags) :
Dmitri Gribenko527ab212012-08-01 23:08:09 +000022 Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags),
23 ThisDeclInfo(NULL) {
Dmitri Gribenkof26054f2012-07-11 21:38:39 +000024}
25
26void Sema::setDecl(const Decl *D) {
Dmitri Gribenko527ab212012-08-01 23:08:09 +000027 if (!D)
28 return;
29
30 ThisDeclInfo = new (Allocator) DeclInfo;
31 ThisDeclInfo->ThisDecl = D;
Dmitri Gribenkoe6213dd2012-08-01 23:21:57 +000032 ThisDeclInfo->IsFilled = false;
Dmitri Gribenkoec925312012-07-06 00:28:32 +000033}
34
35ParagraphComment *Sema::actOnParagraphComment(
36 ArrayRef<InlineContentComment *> Content) {
37 return new (Allocator) ParagraphComment(Content);
38}
39
40BlockCommandComment *Sema::actOnBlockCommandStart(SourceLocation LocBegin,
41 SourceLocation LocEnd,
42 StringRef Name) {
43 return new (Allocator) BlockCommandComment(LocBegin, LocEnd, Name);
44}
45
46BlockCommandComment *Sema::actOnBlockCommandArgs(
47 BlockCommandComment *Command,
48 ArrayRef<BlockCommandComment::Argument> Args) {
49 Command->setArgs(Args);
50 return Command;
51}
52
53BlockCommandComment *Sema::actOnBlockCommandFinish(
54 BlockCommandComment *Command,
55 ParagraphComment *Paragraph) {
56 Command->setParagraph(Paragraph);
Dmitri Gribenkof26054f2012-07-11 21:38:39 +000057 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenko64305832012-08-03 21:15:32 +000058 checkReturnsCommand(Command);
Dmitri Gribenkoec925312012-07-06 00:28:32 +000059 return Command;
60}
61
62ParamCommandComment *Sema::actOnParamCommandStart(SourceLocation LocBegin,
63 SourceLocation LocEnd,
64 StringRef Name) {
Dmitri Gribenkof26054f2012-07-11 21:38:39 +000065 ParamCommandComment *Command =
66 new (Allocator) ParamCommandComment(LocBegin, LocEnd, Name);
67
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +000068 if (!isFunctionDecl())
Dmitri Gribenkof26054f2012-07-11 21:38:39 +000069 Diag(Command->getLocation(),
70 diag::warn_doc_param_not_attached_to_a_function_decl)
71 << Command->getCommandNameRange();
72
73 return Command;
Dmitri Gribenkoec925312012-07-06 00:28:32 +000074}
75
Dmitri Gribenkof26054f2012-07-11 21:38:39 +000076ParamCommandComment *Sema::actOnParamCommandDirectionArg(
77 ParamCommandComment *Command,
Dmitri Gribenkoec925312012-07-06 00:28:32 +000078 SourceLocation ArgLocBegin,
79 SourceLocation ArgLocEnd,
Dmitri Gribenkof26054f2012-07-11 21:38:39 +000080 StringRef Arg) {
81 ParamCommandComment::PassDirection Direction;
82 std::string ArgLower = Arg.lower();
83 // TODO: optimize: lower Name first (need an API in SmallString for that),
84 // after that StringSwitch.
85 if (ArgLower == "[in]")
86 Direction = ParamCommandComment::In;
87 else if (ArgLower == "[out]")
88 Direction = ParamCommandComment::Out;
89 else if (ArgLower == "[in,out]" || ArgLower == "[out,in]")
90 Direction = ParamCommandComment::InOut;
91 else {
92 // Remove spaces.
93 std::string::iterator O = ArgLower.begin();
94 for (std::string::iterator I = ArgLower.begin(), E = ArgLower.end();
95 I != E; ++I) {
96 const char C = *I;
97 if (C != ' ' && C != '\n' && C != '\r' &&
98 C != '\t' && C != '\v' && C != '\f')
99 *O++ = C;
100 }
101 ArgLower.resize(O - ArgLower.begin());
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000102
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000103 bool RemovingWhitespaceHelped = false;
104 if (ArgLower == "[in]") {
105 Direction = ParamCommandComment::In;
106 RemovingWhitespaceHelped = true;
107 } else if (ArgLower == "[out]") {
108 Direction = ParamCommandComment::Out;
109 RemovingWhitespaceHelped = true;
110 } else if (ArgLower == "[in,out]" || ArgLower == "[out,in]") {
111 Direction = ParamCommandComment::InOut;
112 RemovingWhitespaceHelped = true;
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000113 } else {
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000114 Direction = ParamCommandComment::In;
115 RemovingWhitespaceHelped = false;
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000116 }
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000117
118 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
119 if (RemovingWhitespaceHelped)
120 Diag(ArgLocBegin, diag::warn_doc_param_spaces_in_direction)
121 << ArgRange
122 << FixItHint::CreateReplacement(
123 ArgRange,
124 ParamCommandComment::getDirectionAsString(Direction));
125 else
126 Diag(ArgLocBegin, diag::warn_doc_param_invalid_direction)
127 << ArgRange;
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000128 }
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000129 Command->setDirection(Direction, /* Explicit = */ true);
130 return Command;
131}
132
133ParamCommandComment *Sema::actOnParamCommandParamNameArg(
134 ParamCommandComment *Command,
135 SourceLocation ArgLocBegin,
136 SourceLocation ArgLocEnd,
137 StringRef Arg) {
138 // Parser will not feed us more arguments than needed.
Dmitri Gribenko619e75e2012-07-13 19:02:42 +0000139 assert(Command->getNumArgs() == 0);
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000140
141 if (!Command->isDirectionExplicit()) {
142 // User didn't provide a direction argument.
143 Command->setDirection(ParamCommandComment::In, /* Explicit = */ false);
144 }
145 typedef BlockCommandComment::Argument Argument;
146 Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
147 ArgLocEnd),
148 Arg);
149 Command->setArgs(llvm::makeArrayRef(A, 1));
150
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000151 if (!isFunctionDecl()) {
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000152 // We already warned that this \\param is not attached to a function decl.
153 return Command;
154 }
155
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000156 ArrayRef<const ParmVarDecl *> ParamVars = getParamVars();
157
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000158 // Check that referenced parameter name is in the function decl.
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000159 const unsigned ResolvedParamIndex = resolveParmVarReference(Arg, ParamVars);
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000160 if (ResolvedParamIndex != ParamCommandComment::InvalidParamIndex) {
161 Command->setParamIndex(ResolvedParamIndex);
Dmitri Gribenko8f0f1b02012-07-24 21:44:16 +0000162 if (ParamVarDocs[ResolvedParamIndex]) {
163 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
164 Diag(ArgLocBegin, diag::warn_doc_param_duplicate)
165 << Arg << ArgRange;
166 ParamCommandComment *PrevCommand = ParamVarDocs[ResolvedParamIndex];
167 Diag(PrevCommand->getLocation(), diag::note_doc_param_previous)
168 << PrevCommand->getParamNameRange();
169 }
170 ParamVarDocs[ResolvedParamIndex] = Command;
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000171 return Command;
172 }
173
174 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
175 Diag(ArgLocBegin, diag::warn_doc_param_not_found)
176 << Arg << ArgRange;
177
Dmitri Gribenkof24990a2012-07-27 21:34:43 +0000178 // No parameters -- can't suggest a correction.
179 if (ParamVars.size() == 0)
180 return Command;
181
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000182 unsigned CorrectedParamIndex = ParamCommandComment::InvalidParamIndex;
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000183 if (ParamVars.size() == 1) {
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000184 // If function has only one parameter then only that parameter
185 // can be documented.
186 CorrectedParamIndex = 0;
187 } else {
188 // Do typo correction.
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000189 CorrectedParamIndex = correctTypoInParmVarReference(Arg, ParamVars);
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000190 }
191 if (CorrectedParamIndex != ParamCommandComment::InvalidParamIndex) {
192 const ParmVarDecl *CorrectedPVD = ParamVars[CorrectedParamIndex];
193 if (const IdentifierInfo *CorrectedII = CorrectedPVD->getIdentifier())
194 Diag(ArgLocBegin, diag::note_doc_param_name_suggestion)
195 << CorrectedII->getName()
196 << FixItHint::CreateReplacement(ArgRange, CorrectedII->getName());
197 }
198
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000199 return Command;
200}
201
202ParamCommandComment *Sema::actOnParamCommandFinish(ParamCommandComment *Command,
203 ParagraphComment *Paragraph) {
204 Command->setParagraph(Paragraph);
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000205 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000206 return Command;
207}
208
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000209TParamCommandComment *Sema::actOnTParamCommandStart(SourceLocation LocBegin,
210 SourceLocation LocEnd,
211 StringRef Name) {
212 TParamCommandComment *Command =
213 new (Allocator) TParamCommandComment(LocBegin, LocEnd, Name);
214
215 if (!isTemplateDecl())
216 Diag(Command->getLocation(),
217 diag::warn_doc_tparam_not_attached_to_a_template_decl)
218 << Command->getCommandNameRange();
219
220 return Command;
221}
222
223TParamCommandComment *Sema::actOnTParamCommandParamNameArg(
224 TParamCommandComment *Command,
225 SourceLocation ArgLocBegin,
226 SourceLocation ArgLocEnd,
227 StringRef Arg) {
228 // Parser will not feed us more arguments than needed.
229 assert(Command->getNumArgs() == 0);
230
231 typedef BlockCommandComment::Argument Argument;
232 Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
233 ArgLocEnd),
234 Arg);
235 Command->setArgs(llvm::makeArrayRef(A, 1));
236
237 if (!isTemplateDecl()) {
238 // We already warned that this \\tparam is not attached to a template decl.
239 return Command;
240 }
241
Dmitri Gribenko527ab212012-08-01 23:08:09 +0000242 const TemplateParameterList *TemplateParameters =
243 ThisDeclInfo->TemplateParameters;
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000244 SmallVector<unsigned, 2> Position;
245 if (resolveTParamReference(Arg, TemplateParameters, &Position)) {
246 Command->setPosition(copyArray(llvm::makeArrayRef(Position)));
247 llvm::StringMap<TParamCommandComment *>::iterator PrevCommandIt =
248 TemplateParameterDocs.find(Arg);
249 if (PrevCommandIt != TemplateParameterDocs.end()) {
250 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
251 Diag(ArgLocBegin, diag::warn_doc_tparam_duplicate)
252 << Arg << ArgRange;
253 TParamCommandComment *PrevCommand = PrevCommandIt->second;
254 Diag(PrevCommand->getLocation(), diag::note_doc_tparam_previous)
255 << PrevCommand->getParamNameRange();
256 }
257 TemplateParameterDocs[Arg] = Command;
258 return Command;
259 }
260
261 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
262 Diag(ArgLocBegin, diag::warn_doc_tparam_not_found)
263 << Arg << ArgRange;
264
265 if (!TemplateParameters || TemplateParameters->size() == 0)
266 return Command;
267
268 StringRef CorrectedName;
269 if (TemplateParameters->size() == 1) {
270 const NamedDecl *Param = TemplateParameters->getParam(0);
271 const IdentifierInfo *II = Param->getIdentifier();
272 if (II)
273 CorrectedName = II->getName();
274 } else {
275 CorrectedName = correctTypoInTParamReference(Arg, TemplateParameters);
276 }
277
278 if (!CorrectedName.empty()) {
279 Diag(ArgLocBegin, diag::note_doc_tparam_name_suggestion)
280 << CorrectedName
281 << FixItHint::CreateReplacement(ArgRange, CorrectedName);
282 }
283
284 return Command;
285}
286
287TParamCommandComment *Sema::actOnTParamCommandFinish(
288 TParamCommandComment *Command,
289 ParagraphComment *Paragraph) {
290 Command->setParagraph(Paragraph);
291 checkBlockCommandEmptyParagraph(Command);
292 return Command;
293}
294
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000295InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
296 SourceLocation CommandLocEnd,
297 StringRef CommandName) {
298 ArrayRef<InlineCommandComment::Argument> Args;
Dmitri Gribenkod73e4ce2012-07-23 16:43:01 +0000299 return new (Allocator) InlineCommandComment(
300 CommandLocBegin,
301 CommandLocEnd,
302 CommandName,
303 getInlineCommandRenderKind(CommandName),
304 Args);
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000305}
306
307InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
308 SourceLocation CommandLocEnd,
309 StringRef CommandName,
310 SourceLocation ArgLocBegin,
311 SourceLocation ArgLocEnd,
312 StringRef Arg) {
313 typedef InlineCommandComment::Argument Argument;
314 Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
315 ArgLocEnd),
316 Arg);
317
Dmitri Gribenkod73e4ce2012-07-23 16:43:01 +0000318 return new (Allocator) InlineCommandComment(
319 CommandLocBegin,
320 CommandLocEnd,
321 CommandName,
322 getInlineCommandRenderKind(CommandName),
323 llvm::makeArrayRef(A, 1));
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000324}
325
326InlineContentComment *Sema::actOnUnknownCommand(SourceLocation LocBegin,
327 SourceLocation LocEnd,
328 StringRef Name) {
329 ArrayRef<InlineCommandComment::Argument> Args;
Dmitri Gribenkod73e4ce2012-07-23 16:43:01 +0000330 return new (Allocator) InlineCommandComment(
331 LocBegin, LocEnd, Name,
332 InlineCommandComment::RenderNormal,
333 Args);
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000334}
335
336TextComment *Sema::actOnText(SourceLocation LocBegin,
337 SourceLocation LocEnd,
338 StringRef Text) {
339 return new (Allocator) TextComment(LocBegin, LocEnd, Text);
340}
341
342VerbatimBlockComment *Sema::actOnVerbatimBlockStart(SourceLocation Loc,
343 StringRef Name) {
344 return new (Allocator) VerbatimBlockComment(
345 Loc,
346 Loc.getLocWithOffset(1 + Name.size()),
347 Name);
348}
349
350VerbatimBlockLineComment *Sema::actOnVerbatimBlockLine(SourceLocation Loc,
351 StringRef Text) {
352 return new (Allocator) VerbatimBlockLineComment(Loc, Text);
353}
354
355VerbatimBlockComment *Sema::actOnVerbatimBlockFinish(
356 VerbatimBlockComment *Block,
357 SourceLocation CloseNameLocBegin,
358 StringRef CloseName,
359 ArrayRef<VerbatimBlockLineComment *> Lines) {
360 Block->setCloseName(CloseName, CloseNameLocBegin);
361 Block->setLines(Lines);
362 return Block;
363}
364
365VerbatimLineComment *Sema::actOnVerbatimLine(SourceLocation LocBegin,
366 StringRef Name,
367 SourceLocation TextBegin,
368 StringRef Text) {
369 return new (Allocator) VerbatimLineComment(
370 LocBegin,
371 TextBegin.getLocWithOffset(Text.size()),
372 Name,
373 TextBegin,
374 Text);
375}
376
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000377HTMLStartTagComment *Sema::actOnHTMLStartTagStart(SourceLocation LocBegin,
378 StringRef TagName) {
379 return new (Allocator) HTMLStartTagComment(LocBegin, TagName);
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000380}
381
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000382HTMLStartTagComment *Sema::actOnHTMLStartTagFinish(
383 HTMLStartTagComment *Tag,
384 ArrayRef<HTMLStartTagComment::Attribute> Attrs,
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000385 SourceLocation GreaterLoc,
386 bool IsSelfClosing) {
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000387 Tag->setAttrs(Attrs);
388 Tag->setGreaterLoc(GreaterLoc);
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000389 if (IsSelfClosing)
390 Tag->setSelfClosing();
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000391 else if (!isHTMLEndTagForbidden(Tag->getTagName()))
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000392 HTMLOpenTags.push_back(Tag);
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000393 return Tag;
394}
395
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000396HTMLEndTagComment *Sema::actOnHTMLEndTag(SourceLocation LocBegin,
397 SourceLocation LocEnd,
398 StringRef TagName) {
399 HTMLEndTagComment *HET =
400 new (Allocator) HTMLEndTagComment(LocBegin, LocEnd, TagName);
401 if (isHTMLEndTagForbidden(TagName)) {
402 Diag(HET->getLocation(), diag::warn_doc_html_end_forbidden)
403 << TagName << HET->getSourceRange();
404 return HET;
Dmitri Gribenko9460fbf2012-07-12 23:37:09 +0000405 }
406
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000407 bool FoundOpen = false;
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000408 for (SmallVectorImpl<HTMLStartTagComment *>::const_reverse_iterator
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000409 I = HTMLOpenTags.rbegin(), E = HTMLOpenTags.rend();
410 I != E; ++I) {
411 if ((*I)->getTagName() == TagName) {
412 FoundOpen = true;
413 break;
414 }
415 }
416 if (!FoundOpen) {
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000417 Diag(HET->getLocation(), diag::warn_doc_html_end_unbalanced)
418 << HET->getSourceRange();
419 return HET;
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000420 }
421
422 while (!HTMLOpenTags.empty()) {
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000423 const HTMLStartTagComment *HST = HTMLOpenTags.back();
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000424 HTMLOpenTags.pop_back();
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000425 StringRef LastNotClosedTagName = HST->getTagName();
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000426 if (LastNotClosedTagName == TagName)
427 break;
428
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000429 if (isHTMLEndTagOptional(LastNotClosedTagName))
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000430 continue;
431
432 bool OpenLineInvalid;
433 const unsigned OpenLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000434 HST->getLocation(),
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000435 &OpenLineInvalid);
436 bool CloseLineInvalid;
437 const unsigned CloseLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000438 HET->getLocation(),
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000439 &CloseLineInvalid);
440
441 if (OpenLineInvalid || CloseLineInvalid || OpenLine == CloseLine)
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000442 Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
443 << HST->getTagName() << HET->getTagName()
444 << HST->getSourceRange() << HET->getSourceRange();
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000445 else {
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000446 Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
447 << HST->getTagName() << HET->getTagName()
448 << HST->getSourceRange();
449 Diag(HET->getLocation(), diag::note_doc_html_end_tag)
450 << HET->getSourceRange();
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000451 }
452 }
453
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000454 return HET;
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000455}
456
457FullComment *Sema::actOnFullComment(
458 ArrayRef<BlockContentComment *> Blocks) {
Dmitri Gribenko527ab212012-08-01 23:08:09 +0000459 return new (Allocator) FullComment(Blocks, ThisDeclInfo);
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000460}
461
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000462void Sema::checkBlockCommandEmptyParagraph(BlockCommandComment *Command) {
463 ParagraphComment *Paragraph = Command->getParagraph();
464 if (Paragraph->isWhitespace()) {
465 SourceLocation DiagLoc;
Dmitri Gribenko619e75e2012-07-13 19:02:42 +0000466 if (Command->getNumArgs() > 0)
467 DiagLoc = Command->getArgRange(Command->getNumArgs() - 1).getEnd();
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000468 if (!DiagLoc.isValid())
469 DiagLoc = Command->getCommandNameRange().getEnd();
470 Diag(DiagLoc, diag::warn_doc_block_command_empty_paragraph)
471 << Command->getCommandName()
472 << Command->getSourceRange();
473 }
474}
475
Dmitri Gribenko64305832012-08-03 21:15:32 +0000476void Sema::checkReturnsCommand(const BlockCommandComment *Command) {
477 if (!isReturnsCommand(Command->getCommandName()))
478 return;
479 if (isFunctionDecl()) {
480 if (ThisDeclInfo->ResultType->isVoidType()) {
481 unsigned DiagKind;
482 switch (ThisDeclInfo->ThisDecl->getKind()) {
483 default:
484 DiagKind = 0;
485 break;
486 case Decl::CXXConstructor:
487 DiagKind = 1;
488 break;
489 case Decl::CXXDestructor:
490 DiagKind = 2;
491 break;
492 }
493 Diag(Command->getLocation(),
494 diag::warn_doc_returns_attached_to_a_void_function)
495 << Command->getCommandName()
496 << DiagKind
497 << Command->getSourceRange();
498 }
499 return;
500 }
501 Diag(Command->getLocation(),
502 diag::warn_doc_returns_not_attached_to_a_function_decl)
503 << Command->getCommandName()
504 << Command->getSourceRange();
505}
506
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000507bool Sema::isFunctionDecl() {
Dmitri Gribenko527ab212012-08-01 23:08:09 +0000508 if (!ThisDeclInfo)
509 return false;
510 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko52cb2182012-07-24 20:58:46 +0000511 inspectThisDecl();
Dmitri Gribenko37a7faf2012-08-02 21:45:39 +0000512 return ThisDeclInfo->getKind() == DeclInfo::FunctionKind;
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000513}
514
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000515bool Sema::isTemplateDecl() {
Dmitri Gribenko527ab212012-08-01 23:08:09 +0000516 if (!ThisDeclInfo)
517 return false;
518 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000519 inspectThisDecl();
Dmitri Gribenko527ab212012-08-01 23:08:09 +0000520 return ThisDeclInfo->IsTemplateDecl;
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000521}
522
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000523ArrayRef<const ParmVarDecl *> Sema::getParamVars() {
Dmitri Gribenko527ab212012-08-01 23:08:09 +0000524 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko52cb2182012-07-24 20:58:46 +0000525 inspectThisDecl();
Dmitri Gribenko527ab212012-08-01 23:08:09 +0000526 return ThisDeclInfo->ParamVars;
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000527}
528
529void Sema::inspectThisDecl() {
Dmitri Gribenko527ab212012-08-01 23:08:09 +0000530 ThisDeclInfo->fill();
531 ParamVarDocs.resize(ThisDeclInfo->ParamVars.size(), NULL);
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000532}
533
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000534unsigned Sema::resolveParmVarReference(StringRef Name,
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000535 ArrayRef<const ParmVarDecl *> ParamVars) {
536 for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) {
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000537 const IdentifierInfo *II = ParamVars[i]->getIdentifier();
538 if (II && II->getName() == Name)
539 return i;
540 }
541 return ParamCommandComment::InvalidParamIndex;
542}
543
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000544namespace {
545class SimpleTypoCorrector {
546 StringRef Typo;
547 const unsigned MaxEditDistance;
548
549 const NamedDecl *BestDecl;
550 unsigned BestEditDistance;
551 unsigned BestIndex;
552 unsigned NextIndex;
553
554public:
555 SimpleTypoCorrector(StringRef Typo) :
556 Typo(Typo), MaxEditDistance((Typo.size() + 2) / 3),
557 BestDecl(NULL), BestEditDistance(MaxEditDistance + 1),
558 BestIndex(0), NextIndex(0)
559 { }
560
561 void addDecl(const NamedDecl *ND);
562
563 const NamedDecl *getBestDecl() const {
564 if (BestEditDistance > MaxEditDistance)
565 return NULL;
566
567 return BestDecl;
568 }
569
570 unsigned getBestDeclIndex() const {
571 assert(getBestDecl());
572 return BestIndex;
573 }
574};
575
576void SimpleTypoCorrector::addDecl(const NamedDecl *ND) {
577 unsigned CurrIndex = NextIndex++;
578
579 const IdentifierInfo *II = ND->getIdentifier();
580 if (!II)
581 return;
582
583 StringRef Name = II->getName();
584 unsigned MinPossibleEditDistance = abs((int)Name.size() - (int)Typo.size());
585 if (MinPossibleEditDistance > 0 &&
586 Typo.size() / MinPossibleEditDistance < 3)
587 return;
588
589 unsigned EditDistance = Typo.edit_distance(Name, true, MaxEditDistance);
590 if (EditDistance < BestEditDistance) {
591 BestEditDistance = EditDistance;
592 BestDecl = ND;
593 BestIndex = CurrIndex;
594 }
595}
596} // unnamed namespace
597
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000598unsigned Sema::correctTypoInParmVarReference(
599 StringRef Typo,
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000600 ArrayRef<const ParmVarDecl *> ParamVars) {
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000601 SimpleTypoCorrector Corrector(Typo);
602 for (unsigned i = 0, e = ParamVars.size(); i != e; ++i)
603 Corrector.addDecl(ParamVars[i]);
604 if (Corrector.getBestDecl())
605 return Corrector.getBestDeclIndex();
606 else
607 return ParamCommandComment::InvalidParamIndex;;
608}
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000609
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000610namespace {
611bool ResolveTParamReferenceHelper(
612 StringRef Name,
613 const TemplateParameterList *TemplateParameters,
614 SmallVectorImpl<unsigned> *Position) {
615 for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
616 const NamedDecl *Param = TemplateParameters->getParam(i);
617 const IdentifierInfo *II = Param->getIdentifier();
618 if (II && II->getName() == Name) {
619 Position->push_back(i);
620 return true;
621 }
622
623 if (const TemplateTemplateParmDecl *TTP =
624 dyn_cast<TemplateTemplateParmDecl>(Param)) {
625 Position->push_back(i);
626 if (ResolveTParamReferenceHelper(Name, TTP->getTemplateParameters(),
627 Position))
628 return true;
629 Position->pop_back();
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000630 }
631 }
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000632 return false;
633}
634} // unnamed namespace
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000635
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000636bool Sema::resolveTParamReference(
637 StringRef Name,
638 const TemplateParameterList *TemplateParameters,
639 SmallVectorImpl<unsigned> *Position) {
640 Position->clear();
641 if (!TemplateParameters)
642 return false;
643
644 return ResolveTParamReferenceHelper(Name, TemplateParameters, Position);
645}
646
647namespace {
648void CorrectTypoInTParamReferenceHelper(
649 const TemplateParameterList *TemplateParameters,
650 SimpleTypoCorrector &Corrector) {
651 for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
652 const NamedDecl *Param = TemplateParameters->getParam(i);
653 Corrector.addDecl(Param);
654
655 if (const TemplateTemplateParmDecl *TTP =
656 dyn_cast<TemplateTemplateParmDecl>(Param))
657 CorrectTypoInTParamReferenceHelper(TTP->getTemplateParameters(),
658 Corrector);
659 }
660}
661} // unnamed namespace
662
663StringRef Sema::correctTypoInTParamReference(
664 StringRef Typo,
665 const TemplateParameterList *TemplateParameters) {
666 SimpleTypoCorrector Corrector(Typo);
667 CorrectTypoInTParamReferenceHelper(TemplateParameters, Corrector);
668 if (const NamedDecl *ND = Corrector.getBestDecl()) {
669 const IdentifierInfo *II = ND->getIdentifier();
670 assert(II && "SimpleTypoCorrector should not return this decl");
671 return II->getName();
672 }
673 return StringRef();
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000674}
675
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000676// TODO: tablegen
677bool Sema::isBlockCommand(StringRef Name) {
Dmitri Gribenko64305832012-08-03 21:15:32 +0000678 return isReturnsCommand(Name) ||
679 isParamCommand(Name) || isTParamCommand(Name) ||
680 llvm::StringSwitch<bool>(Name)
Dmitri Gribenko31005192012-07-18 00:44:55 +0000681 .Cases("brief", "short", true)
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000682 .Case("author", true)
683 .Case("authors", true)
684 .Case("pre", true)
685 .Case("post", true)
Dmitri Gribenko64305832012-08-03 21:15:32 +0000686 .Default(false);
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000687}
688
689bool Sema::isParamCommand(StringRef Name) {
690 return llvm::StringSwitch<bool>(Name)
691 .Case("param", true)
692 .Case("arg", true)
693 .Default(false);
694}
695
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000696bool Sema::isTParamCommand(StringRef Name) {
697 return Name == "tparam";
698}
699
Dmitri Gribenko64305832012-08-03 21:15:32 +0000700bool Sema::isReturnsCommand(StringRef Name) {
701 return Name == "returns" || Name == "return" || Name == "result";
702}
703
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000704unsigned Sema::getBlockCommandNumArgs(StringRef Name) {
705 return llvm::StringSwitch<unsigned>(Name)
Dmitri Gribenko31005192012-07-18 00:44:55 +0000706 .Cases("brief", "short", 0)
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000707 .Case("pre", 0)
708 .Case("post", 0)
709 .Case("author", 0)
710 .Case("authors", 0)
711 .Default(0);
712}
713
Dmitri Gribenkod73e4ce2012-07-23 16:43:01 +0000714bool Sema::isInlineCommand(StringRef Name) const {
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000715 return llvm::StringSwitch<bool>(Name)
Dmitri Gribenkoe82ae812012-07-19 00:21:03 +0000716 .Case("b", true)
717 .Cases("c", "p", true)
718 .Cases("a", "e", "em", true)
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000719 .Default(false);
720}
721
Dmitri Gribenkod73e4ce2012-07-23 16:43:01 +0000722InlineCommandComment::RenderKind
723Sema::getInlineCommandRenderKind(StringRef Name) const {
724 assert(isInlineCommand(Name));
725
726 return llvm::StringSwitch<InlineCommandComment::RenderKind>(Name)
727 .Case("b", InlineCommandComment::RenderBold)
728 .Cases("c", "p", InlineCommandComment::RenderMonospaced)
729 .Cases("a", "e", "em", InlineCommandComment::RenderEmphasized)
730 .Default(InlineCommandComment::RenderNormal);
731}
732
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000733bool Sema::isHTMLEndTagOptional(StringRef Name) {
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000734 return llvm::StringSwitch<bool>(Name)
Dmitri Gribenko9460fbf2012-07-12 23:37:09 +0000735 .Case("p", true)
736 .Case("li", true)
737 .Case("dt", true)
738 .Case("dd", true)
739 .Case("tr", true)
740 .Case("th", true)
741 .Case("td", true)
742 .Case("thead", true)
743 .Case("tfoot", true)
744 .Case("tbody", true)
745 .Case("colgroup", true)
746 .Default(false);
747}
748
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000749bool Sema::isHTMLEndTagForbidden(StringRef Name) {
Dmitri Gribenko9460fbf2012-07-12 23:37:09 +0000750 return llvm::StringSwitch<bool>(Name)
751 .Case("br", true)
752 .Case("hr", true)
753 .Case("img", true)
754 .Case("col", true)
755 .Default(false);
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000756}
757
758} // end namespace comments
759} // end namespace clang
760