blob: cbfbc4eb24dcea419709a41a866e787746b861e8 [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 Gribenko8d3ba232012-07-06 00:28:32 +000058 return Command;
59}
60
61ParamCommandComment *Sema::actOnParamCommandStart(SourceLocation LocBegin,
62 SourceLocation LocEnd,
63 StringRef Name) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000064 ParamCommandComment *Command =
65 new (Allocator) ParamCommandComment(LocBegin, LocEnd, Name);
66
Dmitri Gribenko8487c522012-07-23 17:40:30 +000067 if (!isFunctionDecl())
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000068 Diag(Command->getLocation(),
69 diag::warn_doc_param_not_attached_to_a_function_decl)
70 << Command->getCommandNameRange();
71
72 return Command;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000073}
74
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000075ParamCommandComment *Sema::actOnParamCommandDirectionArg(
76 ParamCommandComment *Command,
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000077 SourceLocation ArgLocBegin,
78 SourceLocation ArgLocEnd,
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000079 StringRef Arg) {
80 ParamCommandComment::PassDirection Direction;
81 std::string ArgLower = Arg.lower();
82 // TODO: optimize: lower Name first (need an API in SmallString for that),
83 // after that StringSwitch.
84 if (ArgLower == "[in]")
85 Direction = ParamCommandComment::In;
86 else if (ArgLower == "[out]")
87 Direction = ParamCommandComment::Out;
88 else if (ArgLower == "[in,out]" || ArgLower == "[out,in]")
89 Direction = ParamCommandComment::InOut;
90 else {
91 // Remove spaces.
92 std::string::iterator O = ArgLower.begin();
93 for (std::string::iterator I = ArgLower.begin(), E = ArgLower.end();
94 I != E; ++I) {
95 const char C = *I;
96 if (C != ' ' && C != '\n' && C != '\r' &&
97 C != '\t' && C != '\v' && C != '\f')
98 *O++ = C;
99 }
100 ArgLower.resize(O - ArgLower.begin());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000101
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000102 bool RemovingWhitespaceHelped = false;
103 if (ArgLower == "[in]") {
104 Direction = ParamCommandComment::In;
105 RemovingWhitespaceHelped = true;
106 } else if (ArgLower == "[out]") {
107 Direction = ParamCommandComment::Out;
108 RemovingWhitespaceHelped = true;
109 } else if (ArgLower == "[in,out]" || ArgLower == "[out,in]") {
110 Direction = ParamCommandComment::InOut;
111 RemovingWhitespaceHelped = true;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000112 } else {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000113 Direction = ParamCommandComment::In;
114 RemovingWhitespaceHelped = false;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000115 }
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000116
117 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
118 if (RemovingWhitespaceHelped)
119 Diag(ArgLocBegin, diag::warn_doc_param_spaces_in_direction)
120 << ArgRange
121 << FixItHint::CreateReplacement(
122 ArgRange,
123 ParamCommandComment::getDirectionAsString(Direction));
124 else
125 Diag(ArgLocBegin, diag::warn_doc_param_invalid_direction)
126 << ArgRange;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000127 }
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000128 Command->setDirection(Direction, /* Explicit = */ true);
129 return Command;
130}
131
132ParamCommandComment *Sema::actOnParamCommandParamNameArg(
133 ParamCommandComment *Command,
134 SourceLocation ArgLocBegin,
135 SourceLocation ArgLocEnd,
136 StringRef Arg) {
137 // Parser will not feed us more arguments than needed.
Dmitri Gribenko0eaf69d2012-07-13 19:02:42 +0000138 assert(Command->getNumArgs() == 0);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000139
140 if (!Command->isDirectionExplicit()) {
141 // User didn't provide a direction argument.
142 Command->setDirection(ParamCommandComment::In, /* Explicit = */ false);
143 }
144 typedef BlockCommandComment::Argument Argument;
145 Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
146 ArgLocEnd),
147 Arg);
148 Command->setArgs(llvm::makeArrayRef(A, 1));
149
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000150 if (!isFunctionDecl()) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000151 // We already warned that this \\param is not attached to a function decl.
152 return Command;
153 }
154
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000155 ArrayRef<const ParmVarDecl *> ParamVars = getParamVars();
156
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000157 // Check that referenced parameter name is in the function decl.
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000158 const unsigned ResolvedParamIndex = resolveParmVarReference(Arg, ParamVars);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000159 if (ResolvedParamIndex != ParamCommandComment::InvalidParamIndex) {
160 Command->setParamIndex(ResolvedParamIndex);
Dmitri Gribenko65822772012-07-24 21:44:16 +0000161 if (ParamVarDocs[ResolvedParamIndex]) {
162 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
163 Diag(ArgLocBegin, diag::warn_doc_param_duplicate)
164 << Arg << ArgRange;
165 ParamCommandComment *PrevCommand = ParamVarDocs[ResolvedParamIndex];
166 Diag(PrevCommand->getLocation(), diag::note_doc_param_previous)
167 << PrevCommand->getParamNameRange();
168 }
169 ParamVarDocs[ResolvedParamIndex] = Command;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000170 return Command;
171 }
172
173 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
174 Diag(ArgLocBegin, diag::warn_doc_param_not_found)
175 << Arg << ArgRange;
176
Dmitri Gribenkobbb7af32012-07-27 21:34:43 +0000177 // No parameters -- can't suggest a correction.
178 if (ParamVars.size() == 0)
179 return Command;
180
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000181 unsigned CorrectedParamIndex = ParamCommandComment::InvalidParamIndex;
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000182 if (ParamVars.size() == 1) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000183 // If function has only one parameter then only that parameter
184 // can be documented.
185 CorrectedParamIndex = 0;
186 } else {
187 // Do typo correction.
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000188 CorrectedParamIndex = correctTypoInParmVarReference(Arg, ParamVars);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000189 }
190 if (CorrectedParamIndex != ParamCommandComment::InvalidParamIndex) {
191 const ParmVarDecl *CorrectedPVD = ParamVars[CorrectedParamIndex];
192 if (const IdentifierInfo *CorrectedII = CorrectedPVD->getIdentifier())
193 Diag(ArgLocBegin, diag::note_doc_param_name_suggestion)
194 << CorrectedII->getName()
195 << FixItHint::CreateReplacement(ArgRange, CorrectedII->getName());
196 }
197
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000198 return Command;
199}
200
201ParamCommandComment *Sema::actOnParamCommandFinish(ParamCommandComment *Command,
202 ParagraphComment *Paragraph) {
203 Command->setParagraph(Paragraph);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000204 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000205 return Command;
206}
207
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000208TParamCommandComment *Sema::actOnTParamCommandStart(SourceLocation LocBegin,
209 SourceLocation LocEnd,
210 StringRef Name) {
211 TParamCommandComment *Command =
212 new (Allocator) TParamCommandComment(LocBegin, LocEnd, Name);
213
214 if (!isTemplateDecl())
215 Diag(Command->getLocation(),
216 diag::warn_doc_tparam_not_attached_to_a_template_decl)
217 << Command->getCommandNameRange();
218
219 return Command;
220}
221
222TParamCommandComment *Sema::actOnTParamCommandParamNameArg(
223 TParamCommandComment *Command,
224 SourceLocation ArgLocBegin,
225 SourceLocation ArgLocEnd,
226 StringRef Arg) {
227 // Parser will not feed us more arguments than needed.
228 assert(Command->getNumArgs() == 0);
229
230 typedef BlockCommandComment::Argument Argument;
231 Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
232 ArgLocEnd),
233 Arg);
234 Command->setArgs(llvm::makeArrayRef(A, 1));
235
236 if (!isTemplateDecl()) {
237 // We already warned that this \\tparam is not attached to a template decl.
238 return Command;
239 }
240
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000241 const TemplateParameterList *TemplateParameters =
242 ThisDeclInfo->TemplateParameters;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000243 SmallVector<unsigned, 2> Position;
244 if (resolveTParamReference(Arg, TemplateParameters, &Position)) {
245 Command->setPosition(copyArray(llvm::makeArrayRef(Position)));
246 llvm::StringMap<TParamCommandComment *>::iterator PrevCommandIt =
247 TemplateParameterDocs.find(Arg);
248 if (PrevCommandIt != TemplateParameterDocs.end()) {
249 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
250 Diag(ArgLocBegin, diag::warn_doc_tparam_duplicate)
251 << Arg << ArgRange;
252 TParamCommandComment *PrevCommand = PrevCommandIt->second;
253 Diag(PrevCommand->getLocation(), diag::note_doc_tparam_previous)
254 << PrevCommand->getParamNameRange();
255 }
256 TemplateParameterDocs[Arg] = Command;
257 return Command;
258 }
259
260 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
261 Diag(ArgLocBegin, diag::warn_doc_tparam_not_found)
262 << Arg << ArgRange;
263
264 if (!TemplateParameters || TemplateParameters->size() == 0)
265 return Command;
266
267 StringRef CorrectedName;
268 if (TemplateParameters->size() == 1) {
269 const NamedDecl *Param = TemplateParameters->getParam(0);
270 const IdentifierInfo *II = Param->getIdentifier();
271 if (II)
272 CorrectedName = II->getName();
273 } else {
274 CorrectedName = correctTypoInTParamReference(Arg, TemplateParameters);
275 }
276
277 if (!CorrectedName.empty()) {
278 Diag(ArgLocBegin, diag::note_doc_tparam_name_suggestion)
279 << CorrectedName
280 << FixItHint::CreateReplacement(ArgRange, CorrectedName);
281 }
282
283 return Command;
284}
285
286TParamCommandComment *Sema::actOnTParamCommandFinish(
287 TParamCommandComment *Command,
288 ParagraphComment *Paragraph) {
289 Command->setParagraph(Paragraph);
290 checkBlockCommandEmptyParagraph(Command);
291 return Command;
292}
293
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000294InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
295 SourceLocation CommandLocEnd,
296 StringRef CommandName) {
297 ArrayRef<InlineCommandComment::Argument> Args;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000298 return new (Allocator) InlineCommandComment(
299 CommandLocBegin,
300 CommandLocEnd,
301 CommandName,
302 getInlineCommandRenderKind(CommandName),
303 Args);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000304}
305
306InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
307 SourceLocation CommandLocEnd,
308 StringRef CommandName,
309 SourceLocation ArgLocBegin,
310 SourceLocation ArgLocEnd,
311 StringRef Arg) {
312 typedef InlineCommandComment::Argument Argument;
313 Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
314 ArgLocEnd),
315 Arg);
316
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000317 return new (Allocator) InlineCommandComment(
318 CommandLocBegin,
319 CommandLocEnd,
320 CommandName,
321 getInlineCommandRenderKind(CommandName),
322 llvm::makeArrayRef(A, 1));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000323}
324
325InlineContentComment *Sema::actOnUnknownCommand(SourceLocation LocBegin,
326 SourceLocation LocEnd,
327 StringRef Name) {
328 ArrayRef<InlineCommandComment::Argument> Args;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000329 return new (Allocator) InlineCommandComment(
330 LocBegin, LocEnd, Name,
331 InlineCommandComment::RenderNormal,
332 Args);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000333}
334
335TextComment *Sema::actOnText(SourceLocation LocBegin,
336 SourceLocation LocEnd,
337 StringRef Text) {
338 return new (Allocator) TextComment(LocBegin, LocEnd, Text);
339}
340
341VerbatimBlockComment *Sema::actOnVerbatimBlockStart(SourceLocation Loc,
342 StringRef Name) {
343 return new (Allocator) VerbatimBlockComment(
344 Loc,
345 Loc.getLocWithOffset(1 + Name.size()),
346 Name);
347}
348
349VerbatimBlockLineComment *Sema::actOnVerbatimBlockLine(SourceLocation Loc,
350 StringRef Text) {
351 return new (Allocator) VerbatimBlockLineComment(Loc, Text);
352}
353
354VerbatimBlockComment *Sema::actOnVerbatimBlockFinish(
355 VerbatimBlockComment *Block,
356 SourceLocation CloseNameLocBegin,
357 StringRef CloseName,
358 ArrayRef<VerbatimBlockLineComment *> Lines) {
359 Block->setCloseName(CloseName, CloseNameLocBegin);
360 Block->setLines(Lines);
361 return Block;
362}
363
364VerbatimLineComment *Sema::actOnVerbatimLine(SourceLocation LocBegin,
365 StringRef Name,
366 SourceLocation TextBegin,
367 StringRef Text) {
368 return new (Allocator) VerbatimLineComment(
369 LocBegin,
370 TextBegin.getLocWithOffset(Text.size()),
371 Name,
372 TextBegin,
373 Text);
374}
375
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000376HTMLStartTagComment *Sema::actOnHTMLStartTagStart(SourceLocation LocBegin,
377 StringRef TagName) {
378 return new (Allocator) HTMLStartTagComment(LocBegin, TagName);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000379}
380
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000381HTMLStartTagComment *Sema::actOnHTMLStartTagFinish(
382 HTMLStartTagComment *Tag,
383 ArrayRef<HTMLStartTagComment::Attribute> Attrs,
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000384 SourceLocation GreaterLoc,
385 bool IsSelfClosing) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000386 Tag->setAttrs(Attrs);
387 Tag->setGreaterLoc(GreaterLoc);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000388 if (IsSelfClosing)
389 Tag->setSelfClosing();
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000390 else if (!isHTMLEndTagForbidden(Tag->getTagName()))
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000391 HTMLOpenTags.push_back(Tag);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000392 return Tag;
393}
394
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000395HTMLEndTagComment *Sema::actOnHTMLEndTag(SourceLocation LocBegin,
396 SourceLocation LocEnd,
397 StringRef TagName) {
398 HTMLEndTagComment *HET =
399 new (Allocator) HTMLEndTagComment(LocBegin, LocEnd, TagName);
400 if (isHTMLEndTagForbidden(TagName)) {
401 Diag(HET->getLocation(), diag::warn_doc_html_end_forbidden)
402 << TagName << HET->getSourceRange();
403 return HET;
Dmitri Gribenko3d986982012-07-12 23:37:09 +0000404 }
405
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000406 bool FoundOpen = false;
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000407 for (SmallVectorImpl<HTMLStartTagComment *>::const_reverse_iterator
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000408 I = HTMLOpenTags.rbegin(), E = HTMLOpenTags.rend();
409 I != E; ++I) {
410 if ((*I)->getTagName() == TagName) {
411 FoundOpen = true;
412 break;
413 }
414 }
415 if (!FoundOpen) {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000416 Diag(HET->getLocation(), diag::warn_doc_html_end_unbalanced)
417 << HET->getSourceRange();
418 return HET;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000419 }
420
421 while (!HTMLOpenTags.empty()) {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000422 const HTMLStartTagComment *HST = HTMLOpenTags.back();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000423 HTMLOpenTags.pop_back();
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000424 StringRef LastNotClosedTagName = HST->getTagName();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000425 if (LastNotClosedTagName == TagName)
426 break;
427
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000428 if (isHTMLEndTagOptional(LastNotClosedTagName))
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000429 continue;
430
431 bool OpenLineInvalid;
432 const unsigned OpenLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000433 HST->getLocation(),
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000434 &OpenLineInvalid);
435 bool CloseLineInvalid;
436 const unsigned CloseLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000437 HET->getLocation(),
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000438 &CloseLineInvalid);
439
440 if (OpenLineInvalid || CloseLineInvalid || OpenLine == CloseLine)
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000441 Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
442 << HST->getTagName() << HET->getTagName()
443 << HST->getSourceRange() << HET->getSourceRange();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000444 else {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000445 Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
446 << HST->getTagName() << HET->getTagName()
447 << HST->getSourceRange();
448 Diag(HET->getLocation(), diag::note_doc_html_end_tag)
449 << HET->getSourceRange();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000450 }
451 }
452
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000453 return HET;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000454}
455
456FullComment *Sema::actOnFullComment(
457 ArrayRef<BlockContentComment *> Blocks) {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000458 return new (Allocator) FullComment(Blocks, ThisDeclInfo);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000459}
460
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000461void Sema::checkBlockCommandEmptyParagraph(BlockCommandComment *Command) {
462 ParagraphComment *Paragraph = Command->getParagraph();
463 if (Paragraph->isWhitespace()) {
464 SourceLocation DiagLoc;
Dmitri Gribenko0eaf69d2012-07-13 19:02:42 +0000465 if (Command->getNumArgs() > 0)
466 DiagLoc = Command->getArgRange(Command->getNumArgs() - 1).getEnd();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000467 if (!DiagLoc.isValid())
468 DiagLoc = Command->getCommandNameRange().getEnd();
469 Diag(DiagLoc, diag::warn_doc_block_command_empty_paragraph)
470 << Command->getCommandName()
471 << Command->getSourceRange();
472 }
473}
474
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000475bool Sema::isFunctionDecl() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000476 if (!ThisDeclInfo)
477 return false;
478 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko00c59f72012-07-24 20:58:46 +0000479 inspectThisDecl();
Dmitri Gribenkoaf19a6a2012-08-02 21:45:39 +0000480 return ThisDeclInfo->getKind() == DeclInfo::FunctionKind;
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000481}
482
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000483bool Sema::isTemplateDecl() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000484 if (!ThisDeclInfo)
485 return false;
486 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000487 inspectThisDecl();
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000488 return ThisDeclInfo->IsTemplateDecl;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000489}
490
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000491ArrayRef<const ParmVarDecl *> Sema::getParamVars() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000492 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko00c59f72012-07-24 20:58:46 +0000493 inspectThisDecl();
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000494 return ThisDeclInfo->ParamVars;
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000495}
496
497void Sema::inspectThisDecl() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000498 ThisDeclInfo->fill();
499 ParamVarDocs.resize(ThisDeclInfo->ParamVars.size(), NULL);
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000500}
501
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000502unsigned Sema::resolveParmVarReference(StringRef Name,
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000503 ArrayRef<const ParmVarDecl *> ParamVars) {
504 for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000505 const IdentifierInfo *II = ParamVars[i]->getIdentifier();
506 if (II && II->getName() == Name)
507 return i;
508 }
509 return ParamCommandComment::InvalidParamIndex;
510}
511
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000512namespace {
513class SimpleTypoCorrector {
514 StringRef Typo;
515 const unsigned MaxEditDistance;
516
517 const NamedDecl *BestDecl;
518 unsigned BestEditDistance;
519 unsigned BestIndex;
520 unsigned NextIndex;
521
522public:
523 SimpleTypoCorrector(StringRef Typo) :
524 Typo(Typo), MaxEditDistance((Typo.size() + 2) / 3),
525 BestDecl(NULL), BestEditDistance(MaxEditDistance + 1),
526 BestIndex(0), NextIndex(0)
527 { }
528
529 void addDecl(const NamedDecl *ND);
530
531 const NamedDecl *getBestDecl() const {
532 if (BestEditDistance > MaxEditDistance)
533 return NULL;
534
535 return BestDecl;
536 }
537
538 unsigned getBestDeclIndex() const {
539 assert(getBestDecl());
540 return BestIndex;
541 }
542};
543
544void SimpleTypoCorrector::addDecl(const NamedDecl *ND) {
545 unsigned CurrIndex = NextIndex++;
546
547 const IdentifierInfo *II = ND->getIdentifier();
548 if (!II)
549 return;
550
551 StringRef Name = II->getName();
552 unsigned MinPossibleEditDistance = abs((int)Name.size() - (int)Typo.size());
553 if (MinPossibleEditDistance > 0 &&
554 Typo.size() / MinPossibleEditDistance < 3)
555 return;
556
557 unsigned EditDistance = Typo.edit_distance(Name, true, MaxEditDistance);
558 if (EditDistance < BestEditDistance) {
559 BestEditDistance = EditDistance;
560 BestDecl = ND;
561 BestIndex = CurrIndex;
562 }
563}
564} // unnamed namespace
565
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000566unsigned Sema::correctTypoInParmVarReference(
567 StringRef Typo,
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000568 ArrayRef<const ParmVarDecl *> ParamVars) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000569 SimpleTypoCorrector Corrector(Typo);
570 for (unsigned i = 0, e = ParamVars.size(); i != e; ++i)
571 Corrector.addDecl(ParamVars[i]);
572 if (Corrector.getBestDecl())
573 return Corrector.getBestDeclIndex();
574 else
575 return ParamCommandComment::InvalidParamIndex;;
576}
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000577
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000578namespace {
579bool ResolveTParamReferenceHelper(
580 StringRef Name,
581 const TemplateParameterList *TemplateParameters,
582 SmallVectorImpl<unsigned> *Position) {
583 for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
584 const NamedDecl *Param = TemplateParameters->getParam(i);
585 const IdentifierInfo *II = Param->getIdentifier();
586 if (II && II->getName() == Name) {
587 Position->push_back(i);
588 return true;
589 }
590
591 if (const TemplateTemplateParmDecl *TTP =
592 dyn_cast<TemplateTemplateParmDecl>(Param)) {
593 Position->push_back(i);
594 if (ResolveTParamReferenceHelper(Name, TTP->getTemplateParameters(),
595 Position))
596 return true;
597 Position->pop_back();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000598 }
599 }
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000600 return false;
601}
602} // unnamed namespace
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000603
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000604bool Sema::resolveTParamReference(
605 StringRef Name,
606 const TemplateParameterList *TemplateParameters,
607 SmallVectorImpl<unsigned> *Position) {
608 Position->clear();
609 if (!TemplateParameters)
610 return false;
611
612 return ResolveTParamReferenceHelper(Name, TemplateParameters, Position);
613}
614
615namespace {
616void CorrectTypoInTParamReferenceHelper(
617 const TemplateParameterList *TemplateParameters,
618 SimpleTypoCorrector &Corrector) {
619 for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
620 const NamedDecl *Param = TemplateParameters->getParam(i);
621 Corrector.addDecl(Param);
622
623 if (const TemplateTemplateParmDecl *TTP =
624 dyn_cast<TemplateTemplateParmDecl>(Param))
625 CorrectTypoInTParamReferenceHelper(TTP->getTemplateParameters(),
626 Corrector);
627 }
628}
629} // unnamed namespace
630
631StringRef Sema::correctTypoInTParamReference(
632 StringRef Typo,
633 const TemplateParameterList *TemplateParameters) {
634 SimpleTypoCorrector Corrector(Typo);
635 CorrectTypoInTParamReferenceHelper(TemplateParameters, Corrector);
636 if (const NamedDecl *ND = Corrector.getBestDecl()) {
637 const IdentifierInfo *II = ND->getIdentifier();
638 assert(II && "SimpleTypoCorrector should not return this decl");
639 return II->getName();
640 }
641 return StringRef();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000642}
643
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000644// TODO: tablegen
645bool Sema::isBlockCommand(StringRef Name) {
646 return llvm::StringSwitch<bool>(Name)
Dmitri Gribenko3d3d22c2012-07-18 00:44:55 +0000647 .Cases("brief", "short", true)
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000648 .Case("result", true)
649 .Case("return", true)
650 .Case("returns", true)
651 .Case("author", true)
652 .Case("authors", true)
653 .Case("pre", true)
654 .Case("post", true)
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000655 .Default(false) || isParamCommand(Name) || isTParamCommand(Name);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000656}
657
658bool Sema::isParamCommand(StringRef Name) {
659 return llvm::StringSwitch<bool>(Name)
660 .Case("param", true)
661 .Case("arg", true)
662 .Default(false);
663}
664
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000665bool Sema::isTParamCommand(StringRef Name) {
666 return Name == "tparam";
667}
668
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000669unsigned Sema::getBlockCommandNumArgs(StringRef Name) {
670 return llvm::StringSwitch<unsigned>(Name)
Dmitri Gribenko3d3d22c2012-07-18 00:44:55 +0000671 .Cases("brief", "short", 0)
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000672 .Case("pre", 0)
673 .Case("post", 0)
674 .Case("author", 0)
675 .Case("authors", 0)
676 .Default(0);
677}
678
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000679bool Sema::isInlineCommand(StringRef Name) const {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000680 return llvm::StringSwitch<bool>(Name)
Dmitri Gribenkoc48dd8e2012-07-19 00:21:03 +0000681 .Case("b", true)
682 .Cases("c", "p", true)
683 .Cases("a", "e", "em", true)
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000684 .Default(false);
685}
686
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000687InlineCommandComment::RenderKind
688Sema::getInlineCommandRenderKind(StringRef Name) const {
689 assert(isInlineCommand(Name));
690
691 return llvm::StringSwitch<InlineCommandComment::RenderKind>(Name)
692 .Case("b", InlineCommandComment::RenderBold)
693 .Cases("c", "p", InlineCommandComment::RenderMonospaced)
694 .Cases("a", "e", "em", InlineCommandComment::RenderEmphasized)
695 .Default(InlineCommandComment::RenderNormal);
696}
697
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000698bool Sema::isHTMLEndTagOptional(StringRef Name) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000699 return llvm::StringSwitch<bool>(Name)
Dmitri Gribenko3d986982012-07-12 23:37:09 +0000700 .Case("p", true)
701 .Case("li", true)
702 .Case("dt", true)
703 .Case("dd", true)
704 .Case("tr", true)
705 .Case("th", true)
706 .Case("td", true)
707 .Case("thead", true)
708 .Case("tfoot", true)
709 .Case("tbody", true)
710 .Case("colgroup", true)
711 .Default(false);
712}
713
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000714bool Sema::isHTMLEndTagForbidden(StringRef Name) {
Dmitri Gribenko3d986982012-07-12 23:37:09 +0000715 return llvm::StringSwitch<bool>(Name)
716 .Case("br", true)
717 .Case("hr", true)
718 .Case("img", true)
719 .Case("col", true)
720 .Default(false);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000721}
722
723} // end namespace comments
724} // end namespace clang
725