blob: 57f8fda07c8f278ce819ed165a144d48421eb504 [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),
Dmitri Gribenko9443c572012-08-06 17:08:27 +000023 ThisDeclInfo(NULL), BriefCommand(NULL), ReturnsCommand(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 Gribenko651f8ce2012-08-01 23:21:57 +000032 ThisDeclInfo->IsFilled = false;
Dmitri Gribenko8d3ba232012-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 Gribenkoa5ef44f2012-07-11 21:38:39 +000057 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenko9443c572012-08-06 17:08:27 +000058 checkBlockCommandDuplicate(Command);
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +000059 checkReturnsCommand(Command);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000060 return Command;
61}
62
63ParamCommandComment *Sema::actOnParamCommandStart(SourceLocation LocBegin,
64 SourceLocation LocEnd,
65 StringRef Name) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000066 ParamCommandComment *Command =
67 new (Allocator) ParamCommandComment(LocBegin, LocEnd, Name);
68
Dmitri Gribenko8487c522012-07-23 17:40:30 +000069 if (!isFunctionDecl())
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000070 Diag(Command->getLocation(),
71 diag::warn_doc_param_not_attached_to_a_function_decl)
72 << Command->getCommandNameRange();
73
74 return Command;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000075}
76
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000077ParamCommandComment *Sema::actOnParamCommandDirectionArg(
78 ParamCommandComment *Command,
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000079 SourceLocation ArgLocBegin,
80 SourceLocation ArgLocEnd,
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000081 StringRef Arg) {
82 ParamCommandComment::PassDirection Direction;
83 std::string ArgLower = Arg.lower();
84 // TODO: optimize: lower Name first (need an API in SmallString for that),
85 // after that StringSwitch.
86 if (ArgLower == "[in]")
87 Direction = ParamCommandComment::In;
88 else if (ArgLower == "[out]")
89 Direction = ParamCommandComment::Out;
90 else if (ArgLower == "[in,out]" || ArgLower == "[out,in]")
91 Direction = ParamCommandComment::InOut;
92 else {
93 // Remove spaces.
94 std::string::iterator O = ArgLower.begin();
95 for (std::string::iterator I = ArgLower.begin(), E = ArgLower.end();
96 I != E; ++I) {
97 const char C = *I;
98 if (C != ' ' && C != '\n' && C != '\r' &&
99 C != '\t' && C != '\v' && C != '\f')
100 *O++ = C;
101 }
102 ArgLower.resize(O - ArgLower.begin());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000103
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000104 bool RemovingWhitespaceHelped = false;
105 if (ArgLower == "[in]") {
106 Direction = ParamCommandComment::In;
107 RemovingWhitespaceHelped = true;
108 } else if (ArgLower == "[out]") {
109 Direction = ParamCommandComment::Out;
110 RemovingWhitespaceHelped = true;
111 } else if (ArgLower == "[in,out]" || ArgLower == "[out,in]") {
112 Direction = ParamCommandComment::InOut;
113 RemovingWhitespaceHelped = true;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000114 } else {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000115 Direction = ParamCommandComment::In;
116 RemovingWhitespaceHelped = false;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000117 }
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000118
119 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
120 if (RemovingWhitespaceHelped)
121 Diag(ArgLocBegin, diag::warn_doc_param_spaces_in_direction)
122 << ArgRange
123 << FixItHint::CreateReplacement(
124 ArgRange,
125 ParamCommandComment::getDirectionAsString(Direction));
126 else
127 Diag(ArgLocBegin, diag::warn_doc_param_invalid_direction)
128 << ArgRange;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000129 }
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000130 Command->setDirection(Direction, /* Explicit = */ true);
131 return Command;
132}
133
134ParamCommandComment *Sema::actOnParamCommandParamNameArg(
135 ParamCommandComment *Command,
136 SourceLocation ArgLocBegin,
137 SourceLocation ArgLocEnd,
138 StringRef Arg) {
139 // Parser will not feed us more arguments than needed.
Dmitri Gribenko0eaf69d2012-07-13 19:02:42 +0000140 assert(Command->getNumArgs() == 0);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000141
142 if (!Command->isDirectionExplicit()) {
143 // User didn't provide a direction argument.
144 Command->setDirection(ParamCommandComment::In, /* Explicit = */ false);
145 }
146 typedef BlockCommandComment::Argument Argument;
147 Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
148 ArgLocEnd),
149 Arg);
150 Command->setArgs(llvm::makeArrayRef(A, 1));
151
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000152 if (!isFunctionDecl()) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000153 // We already warned that this \\param is not attached to a function decl.
154 return Command;
155 }
156
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000157 ArrayRef<const ParmVarDecl *> ParamVars = getParamVars();
158
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000159 // Check that referenced parameter name is in the function decl.
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000160 const unsigned ResolvedParamIndex = resolveParmVarReference(Arg, ParamVars);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000161 if (ResolvedParamIndex != ParamCommandComment::InvalidParamIndex) {
162 Command->setParamIndex(ResolvedParamIndex);
Dmitri Gribenko65822772012-07-24 21:44:16 +0000163 if (ParamVarDocs[ResolvedParamIndex]) {
164 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
165 Diag(ArgLocBegin, diag::warn_doc_param_duplicate)
166 << Arg << ArgRange;
167 ParamCommandComment *PrevCommand = ParamVarDocs[ResolvedParamIndex];
168 Diag(PrevCommand->getLocation(), diag::note_doc_param_previous)
169 << PrevCommand->getParamNameRange();
170 }
171 ParamVarDocs[ResolvedParamIndex] = Command;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000172 return Command;
173 }
174
175 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
176 Diag(ArgLocBegin, diag::warn_doc_param_not_found)
177 << Arg << ArgRange;
178
Dmitri Gribenkobbb7af32012-07-27 21:34:43 +0000179 // No parameters -- can't suggest a correction.
180 if (ParamVars.size() == 0)
181 return Command;
182
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000183 unsigned CorrectedParamIndex = ParamCommandComment::InvalidParamIndex;
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000184 if (ParamVars.size() == 1) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000185 // If function has only one parameter then only that parameter
186 // can be documented.
187 CorrectedParamIndex = 0;
188 } else {
189 // Do typo correction.
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000190 CorrectedParamIndex = correctTypoInParmVarReference(Arg, ParamVars);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000191 }
192 if (CorrectedParamIndex != ParamCommandComment::InvalidParamIndex) {
193 const ParmVarDecl *CorrectedPVD = ParamVars[CorrectedParamIndex];
194 if (const IdentifierInfo *CorrectedII = CorrectedPVD->getIdentifier())
195 Diag(ArgLocBegin, diag::note_doc_param_name_suggestion)
196 << CorrectedII->getName()
197 << FixItHint::CreateReplacement(ArgRange, CorrectedII->getName());
198 }
199
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000200 return Command;
201}
202
203ParamCommandComment *Sema::actOnParamCommandFinish(ParamCommandComment *Command,
204 ParagraphComment *Paragraph) {
205 Command->setParagraph(Paragraph);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000206 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000207 return Command;
208}
209
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000210TParamCommandComment *Sema::actOnTParamCommandStart(SourceLocation LocBegin,
211 SourceLocation LocEnd,
212 StringRef Name) {
213 TParamCommandComment *Command =
214 new (Allocator) TParamCommandComment(LocBegin, LocEnd, Name);
215
216 if (!isTemplateDecl())
217 Diag(Command->getLocation(),
218 diag::warn_doc_tparam_not_attached_to_a_template_decl)
219 << Command->getCommandNameRange();
220
221 return Command;
222}
223
224TParamCommandComment *Sema::actOnTParamCommandParamNameArg(
225 TParamCommandComment *Command,
226 SourceLocation ArgLocBegin,
227 SourceLocation ArgLocEnd,
228 StringRef Arg) {
229 // Parser will not feed us more arguments than needed.
230 assert(Command->getNumArgs() == 0);
231
232 typedef BlockCommandComment::Argument Argument;
233 Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
234 ArgLocEnd),
235 Arg);
236 Command->setArgs(llvm::makeArrayRef(A, 1));
237
238 if (!isTemplateDecl()) {
239 // We already warned that this \\tparam is not attached to a template decl.
240 return Command;
241 }
242
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000243 const TemplateParameterList *TemplateParameters =
244 ThisDeclInfo->TemplateParameters;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000245 SmallVector<unsigned, 2> Position;
246 if (resolveTParamReference(Arg, TemplateParameters, &Position)) {
247 Command->setPosition(copyArray(llvm::makeArrayRef(Position)));
248 llvm::StringMap<TParamCommandComment *>::iterator PrevCommandIt =
249 TemplateParameterDocs.find(Arg);
250 if (PrevCommandIt != TemplateParameterDocs.end()) {
251 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
252 Diag(ArgLocBegin, diag::warn_doc_tparam_duplicate)
253 << Arg << ArgRange;
254 TParamCommandComment *PrevCommand = PrevCommandIt->second;
255 Diag(PrevCommand->getLocation(), diag::note_doc_tparam_previous)
256 << PrevCommand->getParamNameRange();
257 }
258 TemplateParameterDocs[Arg] = Command;
259 return Command;
260 }
261
262 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
263 Diag(ArgLocBegin, diag::warn_doc_tparam_not_found)
264 << Arg << ArgRange;
265
266 if (!TemplateParameters || TemplateParameters->size() == 0)
267 return Command;
268
269 StringRef CorrectedName;
270 if (TemplateParameters->size() == 1) {
271 const NamedDecl *Param = TemplateParameters->getParam(0);
272 const IdentifierInfo *II = Param->getIdentifier();
273 if (II)
274 CorrectedName = II->getName();
275 } else {
276 CorrectedName = correctTypoInTParamReference(Arg, TemplateParameters);
277 }
278
279 if (!CorrectedName.empty()) {
280 Diag(ArgLocBegin, diag::note_doc_tparam_name_suggestion)
281 << CorrectedName
282 << FixItHint::CreateReplacement(ArgRange, CorrectedName);
283 }
284
285 return Command;
286}
287
288TParamCommandComment *Sema::actOnTParamCommandFinish(
289 TParamCommandComment *Command,
290 ParagraphComment *Paragraph) {
291 Command->setParagraph(Paragraph);
292 checkBlockCommandEmptyParagraph(Command);
293 return Command;
294}
295
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000296InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
297 SourceLocation CommandLocEnd,
298 StringRef CommandName) {
299 ArrayRef<InlineCommandComment::Argument> Args;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000300 return new (Allocator) InlineCommandComment(
301 CommandLocBegin,
302 CommandLocEnd,
303 CommandName,
304 getInlineCommandRenderKind(CommandName),
305 Args);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000306}
307
308InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
309 SourceLocation CommandLocEnd,
310 StringRef CommandName,
311 SourceLocation ArgLocBegin,
312 SourceLocation ArgLocEnd,
313 StringRef Arg) {
314 typedef InlineCommandComment::Argument Argument;
315 Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
316 ArgLocEnd),
317 Arg);
318
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000319 return new (Allocator) InlineCommandComment(
320 CommandLocBegin,
321 CommandLocEnd,
322 CommandName,
323 getInlineCommandRenderKind(CommandName),
324 llvm::makeArrayRef(A, 1));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000325}
326
327InlineContentComment *Sema::actOnUnknownCommand(SourceLocation LocBegin,
328 SourceLocation LocEnd,
329 StringRef Name) {
330 ArrayRef<InlineCommandComment::Argument> Args;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000331 return new (Allocator) InlineCommandComment(
332 LocBegin, LocEnd, Name,
333 InlineCommandComment::RenderNormal,
334 Args);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000335}
336
337TextComment *Sema::actOnText(SourceLocation LocBegin,
338 SourceLocation LocEnd,
339 StringRef Text) {
340 return new (Allocator) TextComment(LocBegin, LocEnd, Text);
341}
342
343VerbatimBlockComment *Sema::actOnVerbatimBlockStart(SourceLocation Loc,
344 StringRef Name) {
345 return new (Allocator) VerbatimBlockComment(
346 Loc,
347 Loc.getLocWithOffset(1 + Name.size()),
348 Name);
349}
350
351VerbatimBlockLineComment *Sema::actOnVerbatimBlockLine(SourceLocation Loc,
352 StringRef Text) {
353 return new (Allocator) VerbatimBlockLineComment(Loc, Text);
354}
355
356VerbatimBlockComment *Sema::actOnVerbatimBlockFinish(
357 VerbatimBlockComment *Block,
358 SourceLocation CloseNameLocBegin,
359 StringRef CloseName,
360 ArrayRef<VerbatimBlockLineComment *> Lines) {
361 Block->setCloseName(CloseName, CloseNameLocBegin);
362 Block->setLines(Lines);
363 return Block;
364}
365
366VerbatimLineComment *Sema::actOnVerbatimLine(SourceLocation LocBegin,
367 StringRef Name,
368 SourceLocation TextBegin,
369 StringRef Text) {
370 return new (Allocator) VerbatimLineComment(
371 LocBegin,
372 TextBegin.getLocWithOffset(Text.size()),
373 Name,
374 TextBegin,
375 Text);
376}
377
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000378HTMLStartTagComment *Sema::actOnHTMLStartTagStart(SourceLocation LocBegin,
379 StringRef TagName) {
380 return new (Allocator) HTMLStartTagComment(LocBegin, TagName);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000381}
382
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000383HTMLStartTagComment *Sema::actOnHTMLStartTagFinish(
384 HTMLStartTagComment *Tag,
385 ArrayRef<HTMLStartTagComment::Attribute> Attrs,
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000386 SourceLocation GreaterLoc,
387 bool IsSelfClosing) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000388 Tag->setAttrs(Attrs);
389 Tag->setGreaterLoc(GreaterLoc);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000390 if (IsSelfClosing)
391 Tag->setSelfClosing();
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000392 else if (!isHTMLEndTagForbidden(Tag->getTagName()))
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000393 HTMLOpenTags.push_back(Tag);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000394 return Tag;
395}
396
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000397HTMLEndTagComment *Sema::actOnHTMLEndTag(SourceLocation LocBegin,
398 SourceLocation LocEnd,
399 StringRef TagName) {
400 HTMLEndTagComment *HET =
401 new (Allocator) HTMLEndTagComment(LocBegin, LocEnd, TagName);
402 if (isHTMLEndTagForbidden(TagName)) {
403 Diag(HET->getLocation(), diag::warn_doc_html_end_forbidden)
404 << TagName << HET->getSourceRange();
405 return HET;
Dmitri Gribenko3d986982012-07-12 23:37:09 +0000406 }
407
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000408 bool FoundOpen = false;
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000409 for (SmallVectorImpl<HTMLStartTagComment *>::const_reverse_iterator
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000410 I = HTMLOpenTags.rbegin(), E = HTMLOpenTags.rend();
411 I != E; ++I) {
412 if ((*I)->getTagName() == TagName) {
413 FoundOpen = true;
414 break;
415 }
416 }
417 if (!FoundOpen) {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000418 Diag(HET->getLocation(), diag::warn_doc_html_end_unbalanced)
419 << HET->getSourceRange();
420 return HET;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000421 }
422
423 while (!HTMLOpenTags.empty()) {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000424 const HTMLStartTagComment *HST = HTMLOpenTags.back();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000425 HTMLOpenTags.pop_back();
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000426 StringRef LastNotClosedTagName = HST->getTagName();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000427 if (LastNotClosedTagName == TagName)
428 break;
429
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000430 if (isHTMLEndTagOptional(LastNotClosedTagName))
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000431 continue;
432
433 bool OpenLineInvalid;
434 const unsigned OpenLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000435 HST->getLocation(),
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000436 &OpenLineInvalid);
437 bool CloseLineInvalid;
438 const unsigned CloseLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000439 HET->getLocation(),
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000440 &CloseLineInvalid);
441
442 if (OpenLineInvalid || CloseLineInvalid || OpenLine == CloseLine)
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000443 Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
444 << HST->getTagName() << HET->getTagName()
445 << HST->getSourceRange() << HET->getSourceRange();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000446 else {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000447 Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
448 << HST->getTagName() << HET->getTagName()
449 << HST->getSourceRange();
450 Diag(HET->getLocation(), diag::note_doc_html_end_tag)
451 << HET->getSourceRange();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000452 }
453 }
454
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000455 return HET;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000456}
457
458FullComment *Sema::actOnFullComment(
459 ArrayRef<BlockContentComment *> Blocks) {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000460 return new (Allocator) FullComment(Blocks, ThisDeclInfo);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000461}
462
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000463void Sema::checkBlockCommandEmptyParagraph(BlockCommandComment *Command) {
464 ParagraphComment *Paragraph = Command->getParagraph();
465 if (Paragraph->isWhitespace()) {
466 SourceLocation DiagLoc;
Dmitri Gribenko0eaf69d2012-07-13 19:02:42 +0000467 if (Command->getNumArgs() > 0)
468 DiagLoc = Command->getArgRange(Command->getNumArgs() - 1).getEnd();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000469 if (!DiagLoc.isValid())
470 DiagLoc = Command->getCommandNameRange().getEnd();
471 Diag(DiagLoc, diag::warn_doc_block_command_empty_paragraph)
472 << Command->getCommandName()
473 << Command->getSourceRange();
474 }
475}
476
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +0000477void Sema::checkReturnsCommand(const BlockCommandComment *Command) {
478 if (!isReturnsCommand(Command->getCommandName()))
479 return;
480 if (isFunctionDecl()) {
481 if (ThisDeclInfo->ResultType->isVoidType()) {
482 unsigned DiagKind;
483 switch (ThisDeclInfo->ThisDecl->getKind()) {
484 default:
Dmitri Gribenko88815f32012-08-06 16:29:26 +0000485 if (ThisDeclInfo->IsObjCMethod)
486 DiagKind = 3;
487 else
488 DiagKind = 0;
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +0000489 break;
490 case Decl::CXXConstructor:
491 DiagKind = 1;
492 break;
493 case Decl::CXXDestructor:
494 DiagKind = 2;
495 break;
496 }
497 Diag(Command->getLocation(),
498 diag::warn_doc_returns_attached_to_a_void_function)
499 << Command->getCommandName()
500 << DiagKind
501 << Command->getSourceRange();
502 }
503 return;
504 }
505 Diag(Command->getLocation(),
506 diag::warn_doc_returns_not_attached_to_a_function_decl)
507 << Command->getCommandName()
508 << Command->getSourceRange();
509}
510
Dmitri Gribenko9443c572012-08-06 17:08:27 +0000511void Sema::checkBlockCommandDuplicate(const BlockCommandComment *Command) {
512 StringRef Name = Command->getCommandName();
513 const BlockCommandComment *PrevCommand = NULL;
514 if (isBriefCommand(Name)) {
515 if (!BriefCommand) {
516 BriefCommand = Command;
517 return;
518 }
519 PrevCommand = BriefCommand;
520 } else if (isReturnsCommand(Name)) {
521 if (!ReturnsCommand) {
522 ReturnsCommand = Command;
523 return;
524 }
525 PrevCommand = ReturnsCommand;
526 } else {
527 // We don't want to check this command for duplicates.
528 return;
529 }
530 Diag(Command->getLocation(), diag::warn_doc_block_command_duplicate)
531 << Name
532 << Command->getSourceRange();
533 if (Name == PrevCommand->getCommandName())
534 Diag(PrevCommand->getLocation(), diag::note_doc_block_command_previous)
535 << PrevCommand->getCommandName()
536 << Command->getSourceRange();
537 else
538 Diag(PrevCommand->getLocation(),
539 diag::note_doc_block_command_previous_alias)
540 << PrevCommand->getCommandName()
541 << Name;
542}
543
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000544bool Sema::isFunctionDecl() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000545 if (!ThisDeclInfo)
546 return false;
547 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko00c59f72012-07-24 20:58:46 +0000548 inspectThisDecl();
Dmitri Gribenkoaf19a6a2012-08-02 21:45:39 +0000549 return ThisDeclInfo->getKind() == DeclInfo::FunctionKind;
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000550}
551
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000552bool Sema::isTemplateDecl() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000553 if (!ThisDeclInfo)
554 return false;
555 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000556 inspectThisDecl();
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000557 return ThisDeclInfo->IsTemplateDecl;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000558}
559
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000560ArrayRef<const ParmVarDecl *> Sema::getParamVars() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000561 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko00c59f72012-07-24 20:58:46 +0000562 inspectThisDecl();
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000563 return ThisDeclInfo->ParamVars;
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000564}
565
566void Sema::inspectThisDecl() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000567 ThisDeclInfo->fill();
568 ParamVarDocs.resize(ThisDeclInfo->ParamVars.size(), NULL);
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000569}
570
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000571unsigned Sema::resolveParmVarReference(StringRef Name,
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000572 ArrayRef<const ParmVarDecl *> ParamVars) {
573 for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000574 const IdentifierInfo *II = ParamVars[i]->getIdentifier();
575 if (II && II->getName() == Name)
576 return i;
577 }
578 return ParamCommandComment::InvalidParamIndex;
579}
580
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000581namespace {
582class SimpleTypoCorrector {
583 StringRef Typo;
584 const unsigned MaxEditDistance;
585
586 const NamedDecl *BestDecl;
587 unsigned BestEditDistance;
588 unsigned BestIndex;
589 unsigned NextIndex;
590
591public:
592 SimpleTypoCorrector(StringRef Typo) :
593 Typo(Typo), MaxEditDistance((Typo.size() + 2) / 3),
594 BestDecl(NULL), BestEditDistance(MaxEditDistance + 1),
595 BestIndex(0), NextIndex(0)
596 { }
597
598 void addDecl(const NamedDecl *ND);
599
600 const NamedDecl *getBestDecl() const {
601 if (BestEditDistance > MaxEditDistance)
602 return NULL;
603
604 return BestDecl;
605 }
606
607 unsigned getBestDeclIndex() const {
608 assert(getBestDecl());
609 return BestIndex;
610 }
611};
612
613void SimpleTypoCorrector::addDecl(const NamedDecl *ND) {
614 unsigned CurrIndex = NextIndex++;
615
616 const IdentifierInfo *II = ND->getIdentifier();
617 if (!II)
618 return;
619
620 StringRef Name = II->getName();
621 unsigned MinPossibleEditDistance = abs((int)Name.size() - (int)Typo.size());
622 if (MinPossibleEditDistance > 0 &&
623 Typo.size() / MinPossibleEditDistance < 3)
624 return;
625
626 unsigned EditDistance = Typo.edit_distance(Name, true, MaxEditDistance);
627 if (EditDistance < BestEditDistance) {
628 BestEditDistance = EditDistance;
629 BestDecl = ND;
630 BestIndex = CurrIndex;
631 }
632}
633} // unnamed namespace
634
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000635unsigned Sema::correctTypoInParmVarReference(
636 StringRef Typo,
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000637 ArrayRef<const ParmVarDecl *> ParamVars) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000638 SimpleTypoCorrector Corrector(Typo);
639 for (unsigned i = 0, e = ParamVars.size(); i != e; ++i)
640 Corrector.addDecl(ParamVars[i]);
641 if (Corrector.getBestDecl())
642 return Corrector.getBestDeclIndex();
643 else
644 return ParamCommandComment::InvalidParamIndex;;
645}
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000646
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000647namespace {
648bool ResolveTParamReferenceHelper(
649 StringRef Name,
650 const TemplateParameterList *TemplateParameters,
651 SmallVectorImpl<unsigned> *Position) {
652 for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
653 const NamedDecl *Param = TemplateParameters->getParam(i);
654 const IdentifierInfo *II = Param->getIdentifier();
655 if (II && II->getName() == Name) {
656 Position->push_back(i);
657 return true;
658 }
659
660 if (const TemplateTemplateParmDecl *TTP =
661 dyn_cast<TemplateTemplateParmDecl>(Param)) {
662 Position->push_back(i);
663 if (ResolveTParamReferenceHelper(Name, TTP->getTemplateParameters(),
664 Position))
665 return true;
666 Position->pop_back();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000667 }
668 }
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000669 return false;
670}
671} // unnamed namespace
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000672
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000673bool Sema::resolveTParamReference(
674 StringRef Name,
675 const TemplateParameterList *TemplateParameters,
676 SmallVectorImpl<unsigned> *Position) {
677 Position->clear();
678 if (!TemplateParameters)
679 return false;
680
681 return ResolveTParamReferenceHelper(Name, TemplateParameters, Position);
682}
683
684namespace {
685void CorrectTypoInTParamReferenceHelper(
686 const TemplateParameterList *TemplateParameters,
687 SimpleTypoCorrector &Corrector) {
688 for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
689 const NamedDecl *Param = TemplateParameters->getParam(i);
690 Corrector.addDecl(Param);
691
692 if (const TemplateTemplateParmDecl *TTP =
693 dyn_cast<TemplateTemplateParmDecl>(Param))
694 CorrectTypoInTParamReferenceHelper(TTP->getTemplateParameters(),
695 Corrector);
696 }
697}
698} // unnamed namespace
699
700StringRef Sema::correctTypoInTParamReference(
701 StringRef Typo,
702 const TemplateParameterList *TemplateParameters) {
703 SimpleTypoCorrector Corrector(Typo);
704 CorrectTypoInTParamReferenceHelper(TemplateParameters, Corrector);
705 if (const NamedDecl *ND = Corrector.getBestDecl()) {
706 const IdentifierInfo *II = ND->getIdentifier();
707 assert(II && "SimpleTypoCorrector should not return this decl");
708 return II->getName();
709 }
710 return StringRef();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000711}
712
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000713// TODO: tablegen
714bool Sema::isBlockCommand(StringRef Name) {
Dmitri Gribenko9443c572012-08-06 17:08:27 +0000715 return isBriefCommand(Name) || isReturnsCommand(Name) ||
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +0000716 isParamCommand(Name) || isTParamCommand(Name) ||
717 llvm::StringSwitch<bool>(Name)
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000718 .Case("author", true)
719 .Case("authors", true)
720 .Case("pre", true)
721 .Case("post", true)
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +0000722 .Default(false);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000723}
724
725bool Sema::isParamCommand(StringRef Name) {
726 return llvm::StringSwitch<bool>(Name)
727 .Case("param", true)
728 .Case("arg", true)
729 .Default(false);
730}
731
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000732bool Sema::isTParamCommand(StringRef Name) {
733 return Name == "tparam";
734}
735
Dmitri Gribenko9443c572012-08-06 17:08:27 +0000736bool Sema::isBriefCommand(StringRef Name) {
737 return Name == "brief" || Name == "short";
738}
739
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +0000740bool Sema::isReturnsCommand(StringRef Name) {
741 return Name == "returns" || Name == "return" || Name == "result";
742}
743
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000744unsigned Sema::getBlockCommandNumArgs(StringRef Name) {
745 return llvm::StringSwitch<unsigned>(Name)
Dmitri Gribenko3d3d22c2012-07-18 00:44:55 +0000746 .Cases("brief", "short", 0)
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000747 .Case("pre", 0)
748 .Case("post", 0)
749 .Case("author", 0)
750 .Case("authors", 0)
751 .Default(0);
752}
753
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000754bool Sema::isInlineCommand(StringRef Name) const {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000755 return llvm::StringSwitch<bool>(Name)
Dmitri Gribenkoc48dd8e2012-07-19 00:21:03 +0000756 .Case("b", true)
757 .Cases("c", "p", true)
758 .Cases("a", "e", "em", true)
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000759 .Default(false);
760}
761
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000762InlineCommandComment::RenderKind
763Sema::getInlineCommandRenderKind(StringRef Name) const {
764 assert(isInlineCommand(Name));
765
766 return llvm::StringSwitch<InlineCommandComment::RenderKind>(Name)
767 .Case("b", InlineCommandComment::RenderBold)
768 .Cases("c", "p", InlineCommandComment::RenderMonospaced)
769 .Cases("a", "e", "em", InlineCommandComment::RenderEmphasized)
770 .Default(InlineCommandComment::RenderNormal);
771}
772
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000773bool Sema::isHTMLEndTagOptional(StringRef Name) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000774 return llvm::StringSwitch<bool>(Name)
Dmitri Gribenko3d986982012-07-12 23:37:09 +0000775 .Case("p", true)
776 .Case("li", true)
777 .Case("dt", true)
778 .Case("dd", true)
779 .Case("tr", true)
780 .Case("th", true)
781 .Case("td", true)
782 .Case("thead", true)
783 .Case("tfoot", true)
784 .Case("tbody", true)
785 .Case("colgroup", true)
786 .Default(false);
787}
788
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000789bool Sema::isHTMLEndTagForbidden(StringRef Name) {
Dmitri Gribenko3d986982012-07-12 23:37:09 +0000790 return llvm::StringSwitch<bool>(Name)
791 .Case("br", true)
792 .Case("hr", true)
793 .Case("img", true)
794 .Case("col", true)
795 .Default(false);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000796}
797
798} // end namespace comments
799} // end namespace clang
800