blob: 770d5bba244f8a7f92266b5f1aabf84a891f3e78 [file] [log] [blame]
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +00001//===--- CommentSema.cpp - Doxygen comment semantic analysis --------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "clang/AST/CommentSema.h"
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000011#include "clang/AST/CommentDiagnostic.h"
12#include "clang/AST/Decl.h"
Dmitri Gribenko96b09862012-07-31 22:37:06 +000013#include "clang/AST/DeclTemplate.h"
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000014#include "clang/Basic/SourceManager.h"
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000015#include "llvm/ADT/StringSwitch.h"
16
17namespace clang {
18namespace comments {
19
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000020Sema::Sema(llvm::BumpPtrAllocator &Allocator, const SourceManager &SourceMgr,
21 DiagnosticsEngine &Diags) :
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +000022 Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags),
23 ThisDeclInfo(NULL) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000024}
25
26void Sema::setDecl(const Decl *D) {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +000027 if (!D)
28 return;
29
30 ThisDeclInfo = new (Allocator) DeclInfo;
31 ThisDeclInfo->ThisDecl = D;
Dmitri 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 Gribenko89ab7d02012-08-03 21:15:32 +000058 checkReturnsCommand(Command);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000059 return Command;
60}
61
62ParamCommandComment *Sema::actOnParamCommandStart(SourceLocation LocBegin,
63 SourceLocation LocEnd,
64 StringRef Name) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000065 ParamCommandComment *Command =
66 new (Allocator) ParamCommandComment(LocBegin, LocEnd, Name);
67
Dmitri Gribenko8487c522012-07-23 17:40:30 +000068 if (!isFunctionDecl())
Dmitri Gribenkoa5ef44f2012-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 Gribenko8d3ba232012-07-06 00:28:32 +000074}
75
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000076ParamCommandComment *Sema::actOnParamCommandDirectionArg(
77 ParamCommandComment *Command,
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000078 SourceLocation ArgLocBegin,
79 SourceLocation ArgLocEnd,
Dmitri Gribenkoa5ef44f2012-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 Gribenko8d3ba232012-07-06 00:28:32 +0000102
Dmitri Gribenkoa5ef44f2012-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 Gribenko8d3ba232012-07-06 00:28:32 +0000113 } else {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000114 Direction = ParamCommandComment::In;
115 RemovingWhitespaceHelped = false;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000116 }
Dmitri Gribenkoa5ef44f2012-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 Gribenko8d3ba232012-07-06 00:28:32 +0000128 }
Dmitri Gribenkoa5ef44f2012-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 Gribenko0eaf69d2012-07-13 19:02:42 +0000139 assert(Command->getNumArgs() == 0);
Dmitri Gribenkoa5ef44f2012-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 Gribenko8487c522012-07-23 17:40:30 +0000151 if (!isFunctionDecl()) {
Dmitri Gribenkoa5ef44f2012-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 Gribenko8487c522012-07-23 17:40:30 +0000156 ArrayRef<const ParmVarDecl *> ParamVars = getParamVars();
157
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000158 // Check that referenced parameter name is in the function decl.
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000159 const unsigned ResolvedParamIndex = resolveParmVarReference(Arg, ParamVars);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000160 if (ResolvedParamIndex != ParamCommandComment::InvalidParamIndex) {
161 Command->setParamIndex(ResolvedParamIndex);
Dmitri Gribenko65822772012-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 Gribenkoa5ef44f2012-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 Gribenkobbb7af32012-07-27 21:34:43 +0000178 // No parameters -- can't suggest a correction.
179 if (ParamVars.size() == 0)
180 return Command;
181
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000182 unsigned CorrectedParamIndex = ParamCommandComment::InvalidParamIndex;
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000183 if (ParamVars.size() == 1) {
Dmitri Gribenkoa5ef44f2012-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 Gribenko8487c522012-07-23 17:40:30 +0000189 CorrectedParamIndex = correctTypoInParmVarReference(Arg, ParamVars);
Dmitri Gribenkoa5ef44f2012-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 Gribenko8d3ba232012-07-06 00:28:32 +0000199 return Command;
200}
201
202ParamCommandComment *Sema::actOnParamCommandFinish(ParamCommandComment *Command,
203 ParagraphComment *Paragraph) {
204 Command->setParagraph(Paragraph);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000205 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000206 return Command;
207}
208
Dmitri Gribenko96b09862012-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 Gribenko1ca7ecc2012-08-01 23:08:09 +0000242 const TemplateParameterList *TemplateParameters =
243 ThisDeclInfo->TemplateParameters;
Dmitri Gribenko96b09862012-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 Gribenko8d3ba232012-07-06 00:28:32 +0000295InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
296 SourceLocation CommandLocEnd,
297 StringRef CommandName) {
298 ArrayRef<InlineCommandComment::Argument> Args;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000299 return new (Allocator) InlineCommandComment(
300 CommandLocBegin,
301 CommandLocEnd,
302 CommandName,
303 getInlineCommandRenderKind(CommandName),
304 Args);
Dmitri Gribenko8d3ba232012-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 Gribenko2d66a502012-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 Gribenko8d3ba232012-07-06 00:28:32 +0000324}
325
326InlineContentComment *Sema::actOnUnknownCommand(SourceLocation LocBegin,
327 SourceLocation LocEnd,
328 StringRef Name) {
329 ArrayRef<InlineCommandComment::Argument> Args;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000330 return new (Allocator) InlineCommandComment(
331 LocBegin, LocEnd, Name,
332 InlineCommandComment::RenderNormal,
333 Args);
Dmitri Gribenko8d3ba232012-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 Gribenko3f38bf22012-07-13 00:44:24 +0000377HTMLStartTagComment *Sema::actOnHTMLStartTagStart(SourceLocation LocBegin,
378 StringRef TagName) {
379 return new (Allocator) HTMLStartTagComment(LocBegin, TagName);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000380}
381
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000382HTMLStartTagComment *Sema::actOnHTMLStartTagFinish(
383 HTMLStartTagComment *Tag,
384 ArrayRef<HTMLStartTagComment::Attribute> Attrs,
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000385 SourceLocation GreaterLoc,
386 bool IsSelfClosing) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000387 Tag->setAttrs(Attrs);
388 Tag->setGreaterLoc(GreaterLoc);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000389 if (IsSelfClosing)
390 Tag->setSelfClosing();
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000391 else if (!isHTMLEndTagForbidden(Tag->getTagName()))
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000392 HTMLOpenTags.push_back(Tag);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000393 return Tag;
394}
395
Dmitri Gribenko3f38bf22012-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 Gribenko3d986982012-07-12 23:37:09 +0000405 }
406
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000407 bool FoundOpen = false;
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000408 for (SmallVectorImpl<HTMLStartTagComment *>::const_reverse_iterator
Dmitri Gribenkoa5ef44f2012-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 Gribenko3f38bf22012-07-13 00:44:24 +0000417 Diag(HET->getLocation(), diag::warn_doc_html_end_unbalanced)
418 << HET->getSourceRange();
419 return HET;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000420 }
421
422 while (!HTMLOpenTags.empty()) {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000423 const HTMLStartTagComment *HST = HTMLOpenTags.back();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000424 HTMLOpenTags.pop_back();
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000425 StringRef LastNotClosedTagName = HST->getTagName();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000426 if (LastNotClosedTagName == TagName)
427 break;
428
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000429 if (isHTMLEndTagOptional(LastNotClosedTagName))
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000430 continue;
431
432 bool OpenLineInvalid;
433 const unsigned OpenLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000434 HST->getLocation(),
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000435 &OpenLineInvalid);
436 bool CloseLineInvalid;
437 const unsigned CloseLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000438 HET->getLocation(),
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000439 &CloseLineInvalid);
440
441 if (OpenLineInvalid || CloseLineInvalid || OpenLine == CloseLine)
Dmitri Gribenko3f38bf22012-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 Gribenkoa5ef44f2012-07-11 21:38:39 +0000445 else {
Dmitri Gribenko3f38bf22012-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 Gribenkoa5ef44f2012-07-11 21:38:39 +0000451 }
452 }
453
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000454 return HET;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000455}
456
457FullComment *Sema::actOnFullComment(
458 ArrayRef<BlockContentComment *> Blocks) {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000459 return new (Allocator) FullComment(Blocks, ThisDeclInfo);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000460}
461
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000462void Sema::checkBlockCommandEmptyParagraph(BlockCommandComment *Command) {
463 ParagraphComment *Paragraph = Command->getParagraph();
464 if (Paragraph->isWhitespace()) {
465 SourceLocation DiagLoc;
Dmitri Gribenko0eaf69d2012-07-13 19:02:42 +0000466 if (Command->getNumArgs() > 0)
467 DiagLoc = Command->getArgRange(Command->getNumArgs() - 1).getEnd();
Dmitri Gribenkoa5ef44f2012-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 Gribenko89ab7d02012-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:
Dmitri Gribenko88815f32012-08-06 16:29:26 +0000484 if (ThisDeclInfo->IsObjCMethod)
485 DiagKind = 3;
486 else
487 DiagKind = 0;
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +0000488 break;
489 case Decl::CXXConstructor:
490 DiagKind = 1;
491 break;
492 case Decl::CXXDestructor:
493 DiagKind = 2;
494 break;
495 }
496 Diag(Command->getLocation(),
497 diag::warn_doc_returns_attached_to_a_void_function)
498 << Command->getCommandName()
499 << DiagKind
500 << Command->getSourceRange();
501 }
502 return;
503 }
504 Diag(Command->getLocation(),
505 diag::warn_doc_returns_not_attached_to_a_function_decl)
506 << Command->getCommandName()
507 << Command->getSourceRange();
508}
509
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000510bool Sema::isFunctionDecl() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000511 if (!ThisDeclInfo)
512 return false;
513 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko00c59f72012-07-24 20:58:46 +0000514 inspectThisDecl();
Dmitri Gribenkoaf19a6a2012-08-02 21:45:39 +0000515 return ThisDeclInfo->getKind() == DeclInfo::FunctionKind;
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000516}
517
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000518bool Sema::isTemplateDecl() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000519 if (!ThisDeclInfo)
520 return false;
521 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000522 inspectThisDecl();
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000523 return ThisDeclInfo->IsTemplateDecl;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000524}
525
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000526ArrayRef<const ParmVarDecl *> Sema::getParamVars() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000527 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko00c59f72012-07-24 20:58:46 +0000528 inspectThisDecl();
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000529 return ThisDeclInfo->ParamVars;
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000530}
531
532void Sema::inspectThisDecl() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000533 ThisDeclInfo->fill();
534 ParamVarDocs.resize(ThisDeclInfo->ParamVars.size(), NULL);
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000535}
536
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000537unsigned Sema::resolveParmVarReference(StringRef Name,
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000538 ArrayRef<const ParmVarDecl *> ParamVars) {
539 for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000540 const IdentifierInfo *II = ParamVars[i]->getIdentifier();
541 if (II && II->getName() == Name)
542 return i;
543 }
544 return ParamCommandComment::InvalidParamIndex;
545}
546
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000547namespace {
548class SimpleTypoCorrector {
549 StringRef Typo;
550 const unsigned MaxEditDistance;
551
552 const NamedDecl *BestDecl;
553 unsigned BestEditDistance;
554 unsigned BestIndex;
555 unsigned NextIndex;
556
557public:
558 SimpleTypoCorrector(StringRef Typo) :
559 Typo(Typo), MaxEditDistance((Typo.size() + 2) / 3),
560 BestDecl(NULL), BestEditDistance(MaxEditDistance + 1),
561 BestIndex(0), NextIndex(0)
562 { }
563
564 void addDecl(const NamedDecl *ND);
565
566 const NamedDecl *getBestDecl() const {
567 if (BestEditDistance > MaxEditDistance)
568 return NULL;
569
570 return BestDecl;
571 }
572
573 unsigned getBestDeclIndex() const {
574 assert(getBestDecl());
575 return BestIndex;
576 }
577};
578
579void SimpleTypoCorrector::addDecl(const NamedDecl *ND) {
580 unsigned CurrIndex = NextIndex++;
581
582 const IdentifierInfo *II = ND->getIdentifier();
583 if (!II)
584 return;
585
586 StringRef Name = II->getName();
587 unsigned MinPossibleEditDistance = abs((int)Name.size() - (int)Typo.size());
588 if (MinPossibleEditDistance > 0 &&
589 Typo.size() / MinPossibleEditDistance < 3)
590 return;
591
592 unsigned EditDistance = Typo.edit_distance(Name, true, MaxEditDistance);
593 if (EditDistance < BestEditDistance) {
594 BestEditDistance = EditDistance;
595 BestDecl = ND;
596 BestIndex = CurrIndex;
597 }
598}
599} // unnamed namespace
600
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000601unsigned Sema::correctTypoInParmVarReference(
602 StringRef Typo,
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000603 ArrayRef<const ParmVarDecl *> ParamVars) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000604 SimpleTypoCorrector Corrector(Typo);
605 for (unsigned i = 0, e = ParamVars.size(); i != e; ++i)
606 Corrector.addDecl(ParamVars[i]);
607 if (Corrector.getBestDecl())
608 return Corrector.getBestDeclIndex();
609 else
610 return ParamCommandComment::InvalidParamIndex;;
611}
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000612
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000613namespace {
614bool ResolveTParamReferenceHelper(
615 StringRef Name,
616 const TemplateParameterList *TemplateParameters,
617 SmallVectorImpl<unsigned> *Position) {
618 for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
619 const NamedDecl *Param = TemplateParameters->getParam(i);
620 const IdentifierInfo *II = Param->getIdentifier();
621 if (II && II->getName() == Name) {
622 Position->push_back(i);
623 return true;
624 }
625
626 if (const TemplateTemplateParmDecl *TTP =
627 dyn_cast<TemplateTemplateParmDecl>(Param)) {
628 Position->push_back(i);
629 if (ResolveTParamReferenceHelper(Name, TTP->getTemplateParameters(),
630 Position))
631 return true;
632 Position->pop_back();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000633 }
634 }
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000635 return false;
636}
637} // unnamed namespace
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000638
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000639bool Sema::resolveTParamReference(
640 StringRef Name,
641 const TemplateParameterList *TemplateParameters,
642 SmallVectorImpl<unsigned> *Position) {
643 Position->clear();
644 if (!TemplateParameters)
645 return false;
646
647 return ResolveTParamReferenceHelper(Name, TemplateParameters, Position);
648}
649
650namespace {
651void CorrectTypoInTParamReferenceHelper(
652 const TemplateParameterList *TemplateParameters,
653 SimpleTypoCorrector &Corrector) {
654 for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
655 const NamedDecl *Param = TemplateParameters->getParam(i);
656 Corrector.addDecl(Param);
657
658 if (const TemplateTemplateParmDecl *TTP =
659 dyn_cast<TemplateTemplateParmDecl>(Param))
660 CorrectTypoInTParamReferenceHelper(TTP->getTemplateParameters(),
661 Corrector);
662 }
663}
664} // unnamed namespace
665
666StringRef Sema::correctTypoInTParamReference(
667 StringRef Typo,
668 const TemplateParameterList *TemplateParameters) {
669 SimpleTypoCorrector Corrector(Typo);
670 CorrectTypoInTParamReferenceHelper(TemplateParameters, Corrector);
671 if (const NamedDecl *ND = Corrector.getBestDecl()) {
672 const IdentifierInfo *II = ND->getIdentifier();
673 assert(II && "SimpleTypoCorrector should not return this decl");
674 return II->getName();
675 }
676 return StringRef();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000677}
678
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000679// TODO: tablegen
680bool Sema::isBlockCommand(StringRef Name) {
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +0000681 return isReturnsCommand(Name) ||
682 isParamCommand(Name) || isTParamCommand(Name) ||
683 llvm::StringSwitch<bool>(Name)
Dmitri Gribenko3d3d22c2012-07-18 00:44:55 +0000684 .Cases("brief", "short", true)
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000685 .Case("author", true)
686 .Case("authors", true)
687 .Case("pre", true)
688 .Case("post", true)
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +0000689 .Default(false);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000690}
691
692bool Sema::isParamCommand(StringRef Name) {
693 return llvm::StringSwitch<bool>(Name)
694 .Case("param", true)
695 .Case("arg", true)
696 .Default(false);
697}
698
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000699bool Sema::isTParamCommand(StringRef Name) {
700 return Name == "tparam";
701}
702
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +0000703bool Sema::isReturnsCommand(StringRef Name) {
704 return Name == "returns" || Name == "return" || Name == "result";
705}
706
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000707unsigned Sema::getBlockCommandNumArgs(StringRef Name) {
708 return llvm::StringSwitch<unsigned>(Name)
Dmitri Gribenko3d3d22c2012-07-18 00:44:55 +0000709 .Cases("brief", "short", 0)
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000710 .Case("pre", 0)
711 .Case("post", 0)
712 .Case("author", 0)
713 .Case("authors", 0)
714 .Default(0);
715}
716
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000717bool Sema::isInlineCommand(StringRef Name) const {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000718 return llvm::StringSwitch<bool>(Name)
Dmitri Gribenkoc48dd8e2012-07-19 00:21:03 +0000719 .Case("b", true)
720 .Cases("c", "p", true)
721 .Cases("a", "e", "em", true)
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000722 .Default(false);
723}
724
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000725InlineCommandComment::RenderKind
726Sema::getInlineCommandRenderKind(StringRef Name) const {
727 assert(isInlineCommand(Name));
728
729 return llvm::StringSwitch<InlineCommandComment::RenderKind>(Name)
730 .Case("b", InlineCommandComment::RenderBold)
731 .Cases("c", "p", InlineCommandComment::RenderMonospaced)
732 .Cases("a", "e", "em", InlineCommandComment::RenderEmphasized)
733 .Default(InlineCommandComment::RenderNormal);
734}
735
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000736bool Sema::isHTMLEndTagOptional(StringRef Name) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000737 return llvm::StringSwitch<bool>(Name)
Dmitri Gribenko3d986982012-07-12 23:37:09 +0000738 .Case("p", true)
739 .Case("li", true)
740 .Case("dt", true)
741 .Case("dd", true)
742 .Case("tr", true)
743 .Case("th", true)
744 .Case("td", true)
745 .Case("thead", true)
746 .Case("tfoot", true)
747 .Case("tbody", true)
748 .Case("colgroup", true)
749 .Default(false);
750}
751
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000752bool Sema::isHTMLEndTagForbidden(StringRef Name) {
Dmitri Gribenko3d986982012-07-12 23:37:09 +0000753 return llvm::StringSwitch<bool>(Name)
754 .Case("br", true)
755 .Case("hr", true)
756 .Case("img", true)
757 .Case("col", true)
758 .Default(false);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000759}
760
761} // end namespace comments
762} // end namespace clang
763