blob: c8422508d38174df7470c88bacec4d418af04eb4 [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:
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 Gribenko8487c522012-07-23 17:40:30 +0000507bool Sema::isFunctionDecl() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000508 if (!ThisDeclInfo)
509 return false;
510 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko00c59f72012-07-24 20:58:46 +0000511 inspectThisDecl();
Dmitri Gribenkoaf19a6a2012-08-02 21:45:39 +0000512 return ThisDeclInfo->getKind() == DeclInfo::FunctionKind;
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000513}
514
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000515bool Sema::isTemplateDecl() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000516 if (!ThisDeclInfo)
517 return false;
518 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000519 inspectThisDecl();
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000520 return ThisDeclInfo->IsTemplateDecl;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000521}
522
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000523ArrayRef<const ParmVarDecl *> Sema::getParamVars() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000524 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko00c59f72012-07-24 20:58:46 +0000525 inspectThisDecl();
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000526 return ThisDeclInfo->ParamVars;
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000527}
528
529void Sema::inspectThisDecl() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000530 ThisDeclInfo->fill();
531 ParamVarDocs.resize(ThisDeclInfo->ParamVars.size(), NULL);
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000532}
533
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000534unsigned Sema::resolveParmVarReference(StringRef Name,
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000535 ArrayRef<const ParmVarDecl *> ParamVars) {
536 for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) {
Dmitri Gribenkoa5ef44f2012-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 Gribenko96b09862012-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 Gribenkoa5ef44f2012-07-11 21:38:39 +0000598unsigned Sema::correctTypoInParmVarReference(
599 StringRef Typo,
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000600 ArrayRef<const ParmVarDecl *> ParamVars) {
Dmitri Gribenko96b09862012-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 Gribenkoa5ef44f2012-07-11 21:38:39 +0000609
Dmitri Gribenko96b09862012-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 Gribenkoa5ef44f2012-07-11 21:38:39 +0000630 }
631 }
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000632 return false;
633}
634} // unnamed namespace
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000635
Dmitri Gribenko96b09862012-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 Gribenkoa5ef44f2012-07-11 21:38:39 +0000674}
675
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000676// TODO: tablegen
677bool Sema::isBlockCommand(StringRef Name) {
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +0000678 return isReturnsCommand(Name) ||
679 isParamCommand(Name) || isTParamCommand(Name) ||
680 llvm::StringSwitch<bool>(Name)
Dmitri Gribenko3d3d22c2012-07-18 00:44:55 +0000681 .Cases("brief", "short", true)
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000682 .Case("author", true)
683 .Case("authors", true)
684 .Case("pre", true)
685 .Case("post", true)
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +0000686 .Default(false);
Dmitri Gribenko8d3ba232012-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 Gribenko96b09862012-07-31 22:37:06 +0000696bool Sema::isTParamCommand(StringRef Name) {
697 return Name == "tparam";
698}
699
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +0000700bool Sema::isReturnsCommand(StringRef Name) {
701 return Name == "returns" || Name == "return" || Name == "result";
702}
703
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000704unsigned Sema::getBlockCommandNumArgs(StringRef Name) {
705 return llvm::StringSwitch<unsigned>(Name)
Dmitri Gribenko3d3d22c2012-07-18 00:44:55 +0000706 .Cases("brief", "short", 0)
Dmitri Gribenko8d3ba232012-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 Gribenko2d66a502012-07-23 16:43:01 +0000714bool Sema::isInlineCommand(StringRef Name) const {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000715 return llvm::StringSwitch<bool>(Name)
Dmitri Gribenkoc48dd8e2012-07-19 00:21:03 +0000716 .Case("b", true)
717 .Cases("c", "p", true)
718 .Cases("a", "e", "em", true)
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000719 .Default(false);
720}
721
Dmitri Gribenko2d66a502012-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 Gribenko3f38bf22012-07-13 00:44:24 +0000733bool Sema::isHTMLEndTagOptional(StringRef Name) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000734 return llvm::StringSwitch<bool>(Name)
Dmitri Gribenko3d986982012-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 Gribenko3f38bf22012-07-13 00:44:24 +0000749bool Sema::isHTMLEndTagForbidden(StringRef Name) {
Dmitri Gribenko3d986982012-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 Gribenko8d3ba232012-07-06 00:28:32 +0000756}
757
758} // end namespace comments
759} // end namespace clang
760