blob: 978c748b7416da8347e9a74265091aaac46fb816 [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
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +000046void Sema::actOnBlockCommandArgs(BlockCommandComment *Command,
47 ArrayRef<BlockCommandComment::Argument> Args) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000048 Command->setArgs(Args);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000049}
50
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +000051void Sema::actOnBlockCommandFinish(BlockCommandComment *Command,
52 ParagraphComment *Paragraph) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000053 Command->setParagraph(Paragraph);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000054 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenko9443c572012-08-06 17:08:27 +000055 checkBlockCommandDuplicate(Command);
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +000056 checkReturnsCommand(Command);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000057}
58
59ParamCommandComment *Sema::actOnParamCommandStart(SourceLocation LocBegin,
60 SourceLocation LocEnd,
61 StringRef Name) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000062 ParamCommandComment *Command =
63 new (Allocator) ParamCommandComment(LocBegin, LocEnd, Name);
64
Dmitri Gribenko8487c522012-07-23 17:40:30 +000065 if (!isFunctionDecl())
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000066 Diag(Command->getLocation(),
67 diag::warn_doc_param_not_attached_to_a_function_decl)
68 << Command->getCommandNameRange();
69
70 return Command;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000071}
72
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +000073void Sema::actOnParamCommandDirectionArg(ParamCommandComment *Command,
74 SourceLocation ArgLocBegin,
75 SourceLocation ArgLocEnd,
76 StringRef Arg) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000077 ParamCommandComment::PassDirection Direction;
78 std::string ArgLower = Arg.lower();
79 // TODO: optimize: lower Name first (need an API in SmallString for that),
80 // after that StringSwitch.
81 if (ArgLower == "[in]")
82 Direction = ParamCommandComment::In;
83 else if (ArgLower == "[out]")
84 Direction = ParamCommandComment::Out;
85 else if (ArgLower == "[in,out]" || ArgLower == "[out,in]")
86 Direction = ParamCommandComment::InOut;
87 else {
88 // Remove spaces.
89 std::string::iterator O = ArgLower.begin();
90 for (std::string::iterator I = ArgLower.begin(), E = ArgLower.end();
91 I != E; ++I) {
92 const char C = *I;
93 if (C != ' ' && C != '\n' && C != '\r' &&
94 C != '\t' && C != '\v' && C != '\f')
95 *O++ = C;
96 }
97 ArgLower.resize(O - ArgLower.begin());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000098
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000099 bool RemovingWhitespaceHelped = false;
100 if (ArgLower == "[in]") {
101 Direction = ParamCommandComment::In;
102 RemovingWhitespaceHelped = true;
103 } else if (ArgLower == "[out]") {
104 Direction = ParamCommandComment::Out;
105 RemovingWhitespaceHelped = true;
106 } else if (ArgLower == "[in,out]" || ArgLower == "[out,in]") {
107 Direction = ParamCommandComment::InOut;
108 RemovingWhitespaceHelped = true;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000109 } else {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000110 Direction = ParamCommandComment::In;
111 RemovingWhitespaceHelped = false;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000112 }
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000113
114 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
115 if (RemovingWhitespaceHelped)
116 Diag(ArgLocBegin, diag::warn_doc_param_spaces_in_direction)
117 << ArgRange
118 << FixItHint::CreateReplacement(
119 ArgRange,
120 ParamCommandComment::getDirectionAsString(Direction));
121 else
122 Diag(ArgLocBegin, diag::warn_doc_param_invalid_direction)
123 << ArgRange;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000124 }
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000125 Command->setDirection(Direction, /* Explicit = */ true);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000126}
127
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000128void Sema::actOnParamCommandParamNameArg(ParamCommandComment *Command,
129 SourceLocation ArgLocBegin,
130 SourceLocation ArgLocEnd,
131 StringRef Arg) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000132 // Parser will not feed us more arguments than needed.
Dmitri Gribenko0eaf69d2012-07-13 19:02:42 +0000133 assert(Command->getNumArgs() == 0);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000134
135 if (!Command->isDirectionExplicit()) {
136 // User didn't provide a direction argument.
137 Command->setDirection(ParamCommandComment::In, /* Explicit = */ false);
138 }
139 typedef BlockCommandComment::Argument Argument;
140 Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
141 ArgLocEnd),
142 Arg);
143 Command->setArgs(llvm::makeArrayRef(A, 1));
144
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000145 if (!isFunctionDecl()) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000146 // We already warned that this \\param is not attached to a function decl.
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000147 return;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000148 }
149
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000150 ArrayRef<const ParmVarDecl *> ParamVars = getParamVars();
151
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000152 // Check that referenced parameter name is in the function decl.
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000153 const unsigned ResolvedParamIndex = resolveParmVarReference(Arg, ParamVars);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000154 if (ResolvedParamIndex != ParamCommandComment::InvalidParamIndex) {
155 Command->setParamIndex(ResolvedParamIndex);
Dmitri Gribenko65822772012-07-24 21:44:16 +0000156 if (ParamVarDocs[ResolvedParamIndex]) {
157 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
158 Diag(ArgLocBegin, diag::warn_doc_param_duplicate)
159 << Arg << ArgRange;
160 ParamCommandComment *PrevCommand = ParamVarDocs[ResolvedParamIndex];
161 Diag(PrevCommand->getLocation(), diag::note_doc_param_previous)
162 << PrevCommand->getParamNameRange();
163 }
164 ParamVarDocs[ResolvedParamIndex] = Command;
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000165 return;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000166 }
167
168 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
169 Diag(ArgLocBegin, diag::warn_doc_param_not_found)
170 << Arg << ArgRange;
171
Dmitri Gribenkobbb7af32012-07-27 21:34:43 +0000172 // No parameters -- can't suggest a correction.
173 if (ParamVars.size() == 0)
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000174 return;
Dmitri Gribenkobbb7af32012-07-27 21:34:43 +0000175
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000176 unsigned CorrectedParamIndex = ParamCommandComment::InvalidParamIndex;
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000177 if (ParamVars.size() == 1) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000178 // If function has only one parameter then only that parameter
179 // can be documented.
180 CorrectedParamIndex = 0;
181 } else {
182 // Do typo correction.
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000183 CorrectedParamIndex = correctTypoInParmVarReference(Arg, ParamVars);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000184 }
185 if (CorrectedParamIndex != ParamCommandComment::InvalidParamIndex) {
186 const ParmVarDecl *CorrectedPVD = ParamVars[CorrectedParamIndex];
187 if (const IdentifierInfo *CorrectedII = CorrectedPVD->getIdentifier())
188 Diag(ArgLocBegin, diag::note_doc_param_name_suggestion)
189 << CorrectedII->getName()
190 << FixItHint::CreateReplacement(ArgRange, CorrectedII->getName());
191 }
192
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000193 return;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000194}
195
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000196void Sema::actOnParamCommandFinish(ParamCommandComment *Command,
197 ParagraphComment *Paragraph) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000198 Command->setParagraph(Paragraph);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000199 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000200}
201
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000202TParamCommandComment *Sema::actOnTParamCommandStart(SourceLocation LocBegin,
203 SourceLocation LocEnd,
204 StringRef Name) {
205 TParamCommandComment *Command =
206 new (Allocator) TParamCommandComment(LocBegin, LocEnd, Name);
207
Dmitri Gribenko04bf29e2012-08-06 21:31:15 +0000208 if (!isTemplateOrSpecialization())
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000209 Diag(Command->getLocation(),
210 diag::warn_doc_tparam_not_attached_to_a_template_decl)
211 << Command->getCommandNameRange();
212
213 return Command;
214}
215
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000216void Sema::actOnTParamCommandParamNameArg(TParamCommandComment *Command,
217 SourceLocation ArgLocBegin,
218 SourceLocation ArgLocEnd,
219 StringRef Arg) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000220 // Parser will not feed us more arguments than needed.
221 assert(Command->getNumArgs() == 0);
222
223 typedef BlockCommandComment::Argument Argument;
224 Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
225 ArgLocEnd),
226 Arg);
227 Command->setArgs(llvm::makeArrayRef(A, 1));
228
Dmitri Gribenko04bf29e2012-08-06 21:31:15 +0000229 if (!isTemplateOrSpecialization()) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000230 // We already warned that this \\tparam is not attached to a template decl.
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000231 return;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000232 }
233
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000234 const TemplateParameterList *TemplateParameters =
235 ThisDeclInfo->TemplateParameters;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000236 SmallVector<unsigned, 2> Position;
237 if (resolveTParamReference(Arg, TemplateParameters, &Position)) {
238 Command->setPosition(copyArray(llvm::makeArrayRef(Position)));
239 llvm::StringMap<TParamCommandComment *>::iterator PrevCommandIt =
240 TemplateParameterDocs.find(Arg);
241 if (PrevCommandIt != TemplateParameterDocs.end()) {
242 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
243 Diag(ArgLocBegin, diag::warn_doc_tparam_duplicate)
244 << Arg << ArgRange;
245 TParamCommandComment *PrevCommand = PrevCommandIt->second;
246 Diag(PrevCommand->getLocation(), diag::note_doc_tparam_previous)
247 << PrevCommand->getParamNameRange();
248 }
249 TemplateParameterDocs[Arg] = Command;
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000250 return;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000251 }
252
253 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
254 Diag(ArgLocBegin, diag::warn_doc_tparam_not_found)
255 << Arg << ArgRange;
256
257 if (!TemplateParameters || TemplateParameters->size() == 0)
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000258 return;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000259
260 StringRef CorrectedName;
261 if (TemplateParameters->size() == 1) {
262 const NamedDecl *Param = TemplateParameters->getParam(0);
263 const IdentifierInfo *II = Param->getIdentifier();
264 if (II)
265 CorrectedName = II->getName();
266 } else {
267 CorrectedName = correctTypoInTParamReference(Arg, TemplateParameters);
268 }
269
270 if (!CorrectedName.empty()) {
271 Diag(ArgLocBegin, diag::note_doc_tparam_name_suggestion)
272 << CorrectedName
273 << FixItHint::CreateReplacement(ArgRange, CorrectedName);
274 }
275
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000276 return;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000277}
278
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000279void Sema::actOnTParamCommandFinish(TParamCommandComment *Command,
280 ParagraphComment *Paragraph) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000281 Command->setParagraph(Paragraph);
282 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000283}
284
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000285InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
286 SourceLocation CommandLocEnd,
287 StringRef CommandName) {
288 ArrayRef<InlineCommandComment::Argument> Args;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000289 return new (Allocator) InlineCommandComment(
290 CommandLocBegin,
291 CommandLocEnd,
292 CommandName,
293 getInlineCommandRenderKind(CommandName),
294 Args);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000295}
296
297InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
298 SourceLocation CommandLocEnd,
299 StringRef CommandName,
300 SourceLocation ArgLocBegin,
301 SourceLocation ArgLocEnd,
302 StringRef Arg) {
303 typedef InlineCommandComment::Argument Argument;
304 Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
305 ArgLocEnd),
306 Arg);
307
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000308 return new (Allocator) InlineCommandComment(
309 CommandLocBegin,
310 CommandLocEnd,
311 CommandName,
312 getInlineCommandRenderKind(CommandName),
313 llvm::makeArrayRef(A, 1));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000314}
315
316InlineContentComment *Sema::actOnUnknownCommand(SourceLocation LocBegin,
317 SourceLocation LocEnd,
318 StringRef Name) {
319 ArrayRef<InlineCommandComment::Argument> Args;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000320 return new (Allocator) InlineCommandComment(
321 LocBegin, LocEnd, Name,
322 InlineCommandComment::RenderNormal,
323 Args);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000324}
325
326TextComment *Sema::actOnText(SourceLocation LocBegin,
327 SourceLocation LocEnd,
328 StringRef Text) {
329 return new (Allocator) TextComment(LocBegin, LocEnd, Text);
330}
331
332VerbatimBlockComment *Sema::actOnVerbatimBlockStart(SourceLocation Loc,
333 StringRef Name) {
334 return new (Allocator) VerbatimBlockComment(
335 Loc,
336 Loc.getLocWithOffset(1 + Name.size()),
337 Name);
338}
339
340VerbatimBlockLineComment *Sema::actOnVerbatimBlockLine(SourceLocation Loc,
341 StringRef Text) {
342 return new (Allocator) VerbatimBlockLineComment(Loc, Text);
343}
344
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000345void Sema::actOnVerbatimBlockFinish(
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000346 VerbatimBlockComment *Block,
347 SourceLocation CloseNameLocBegin,
348 StringRef CloseName,
349 ArrayRef<VerbatimBlockLineComment *> Lines) {
350 Block->setCloseName(CloseName, CloseNameLocBegin);
351 Block->setLines(Lines);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000352}
353
354VerbatimLineComment *Sema::actOnVerbatimLine(SourceLocation LocBegin,
355 StringRef Name,
356 SourceLocation TextBegin,
357 StringRef Text) {
358 return new (Allocator) VerbatimLineComment(
359 LocBegin,
360 TextBegin.getLocWithOffset(Text.size()),
361 Name,
362 TextBegin,
363 Text);
364}
365
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000366HTMLStartTagComment *Sema::actOnHTMLStartTagStart(SourceLocation LocBegin,
367 StringRef TagName) {
368 return new (Allocator) HTMLStartTagComment(LocBegin, TagName);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000369}
370
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000371void Sema::actOnHTMLStartTagFinish(
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000372 HTMLStartTagComment *Tag,
373 ArrayRef<HTMLStartTagComment::Attribute> Attrs,
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000374 SourceLocation GreaterLoc,
375 bool IsSelfClosing) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000376 Tag->setAttrs(Attrs);
377 Tag->setGreaterLoc(GreaterLoc);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000378 if (IsSelfClosing)
379 Tag->setSelfClosing();
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000380 else if (!isHTMLEndTagForbidden(Tag->getTagName()))
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000381 HTMLOpenTags.push_back(Tag);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000382}
383
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000384HTMLEndTagComment *Sema::actOnHTMLEndTag(SourceLocation LocBegin,
385 SourceLocation LocEnd,
386 StringRef TagName) {
387 HTMLEndTagComment *HET =
388 new (Allocator) HTMLEndTagComment(LocBegin, LocEnd, TagName);
389 if (isHTMLEndTagForbidden(TagName)) {
390 Diag(HET->getLocation(), diag::warn_doc_html_end_forbidden)
391 << TagName << HET->getSourceRange();
392 return HET;
Dmitri Gribenko3d986982012-07-12 23:37:09 +0000393 }
394
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000395 bool FoundOpen = false;
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000396 for (SmallVectorImpl<HTMLStartTagComment *>::const_reverse_iterator
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000397 I = HTMLOpenTags.rbegin(), E = HTMLOpenTags.rend();
398 I != E; ++I) {
399 if ((*I)->getTagName() == TagName) {
400 FoundOpen = true;
401 break;
402 }
403 }
404 if (!FoundOpen) {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000405 Diag(HET->getLocation(), diag::warn_doc_html_end_unbalanced)
406 << HET->getSourceRange();
407 return HET;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000408 }
409
410 while (!HTMLOpenTags.empty()) {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000411 const HTMLStartTagComment *HST = HTMLOpenTags.back();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000412 HTMLOpenTags.pop_back();
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000413 StringRef LastNotClosedTagName = HST->getTagName();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000414 if (LastNotClosedTagName == TagName)
415 break;
416
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000417 if (isHTMLEndTagOptional(LastNotClosedTagName))
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000418 continue;
419
420 bool OpenLineInvalid;
421 const unsigned OpenLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000422 HST->getLocation(),
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000423 &OpenLineInvalid);
424 bool CloseLineInvalid;
425 const unsigned CloseLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000426 HET->getLocation(),
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000427 &CloseLineInvalid);
428
429 if (OpenLineInvalid || CloseLineInvalid || OpenLine == CloseLine)
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000430 Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
431 << HST->getTagName() << HET->getTagName()
432 << HST->getSourceRange() << HET->getSourceRange();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000433 else {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000434 Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
435 << HST->getTagName() << HET->getTagName()
436 << HST->getSourceRange();
437 Diag(HET->getLocation(), diag::note_doc_html_end_tag)
438 << HET->getSourceRange();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000439 }
440 }
441
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000442 return HET;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000443}
444
445FullComment *Sema::actOnFullComment(
446 ArrayRef<BlockContentComment *> Blocks) {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000447 return new (Allocator) FullComment(Blocks, ThisDeclInfo);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000448}
449
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000450void Sema::checkBlockCommandEmptyParagraph(BlockCommandComment *Command) {
451 ParagraphComment *Paragraph = Command->getParagraph();
452 if (Paragraph->isWhitespace()) {
453 SourceLocation DiagLoc;
Dmitri Gribenko0eaf69d2012-07-13 19:02:42 +0000454 if (Command->getNumArgs() > 0)
455 DiagLoc = Command->getArgRange(Command->getNumArgs() - 1).getEnd();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000456 if (!DiagLoc.isValid())
457 DiagLoc = Command->getCommandNameRange().getEnd();
458 Diag(DiagLoc, diag::warn_doc_block_command_empty_paragraph)
459 << Command->getCommandName()
460 << Command->getSourceRange();
461 }
462}
463
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +0000464void Sema::checkReturnsCommand(const BlockCommandComment *Command) {
465 if (!isReturnsCommand(Command->getCommandName()))
466 return;
467 if (isFunctionDecl()) {
468 if (ThisDeclInfo->ResultType->isVoidType()) {
469 unsigned DiagKind;
470 switch (ThisDeclInfo->ThisDecl->getKind()) {
471 default:
Dmitri Gribenko88815f32012-08-06 16:29:26 +0000472 if (ThisDeclInfo->IsObjCMethod)
473 DiagKind = 3;
474 else
475 DiagKind = 0;
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +0000476 break;
477 case Decl::CXXConstructor:
478 DiagKind = 1;
479 break;
480 case Decl::CXXDestructor:
481 DiagKind = 2;
482 break;
483 }
484 Diag(Command->getLocation(),
485 diag::warn_doc_returns_attached_to_a_void_function)
486 << Command->getCommandName()
487 << DiagKind
488 << Command->getSourceRange();
489 }
490 return;
491 }
492 Diag(Command->getLocation(),
493 diag::warn_doc_returns_not_attached_to_a_function_decl)
494 << Command->getCommandName()
495 << Command->getSourceRange();
496}
497
Dmitri Gribenko9443c572012-08-06 17:08:27 +0000498void Sema::checkBlockCommandDuplicate(const BlockCommandComment *Command) {
499 StringRef Name = Command->getCommandName();
500 const BlockCommandComment *PrevCommand = NULL;
501 if (isBriefCommand(Name)) {
502 if (!BriefCommand) {
503 BriefCommand = Command;
504 return;
505 }
506 PrevCommand = BriefCommand;
507 } else if (isReturnsCommand(Name)) {
508 if (!ReturnsCommand) {
509 ReturnsCommand = Command;
510 return;
511 }
512 PrevCommand = ReturnsCommand;
513 } else {
514 // We don't want to check this command for duplicates.
515 return;
516 }
517 Diag(Command->getLocation(), diag::warn_doc_block_command_duplicate)
518 << Name
519 << Command->getSourceRange();
520 if (Name == PrevCommand->getCommandName())
521 Diag(PrevCommand->getLocation(), diag::note_doc_block_command_previous)
522 << PrevCommand->getCommandName()
523 << Command->getSourceRange();
524 else
525 Diag(PrevCommand->getLocation(),
526 diag::note_doc_block_command_previous_alias)
527 << PrevCommand->getCommandName()
528 << Name;
529}
530
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000531bool Sema::isFunctionDecl() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000532 if (!ThisDeclInfo)
533 return false;
534 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko00c59f72012-07-24 20:58:46 +0000535 inspectThisDecl();
Dmitri Gribenkoaf19a6a2012-08-02 21:45:39 +0000536 return ThisDeclInfo->getKind() == DeclInfo::FunctionKind;
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000537}
538
Dmitri Gribenko04bf29e2012-08-06 21:31:15 +0000539bool Sema::isTemplateOrSpecialization() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000540 if (!ThisDeclInfo)
541 return false;
542 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000543 inspectThisDecl();
Dmitri Gribenko04bf29e2012-08-06 21:31:15 +0000544 return ThisDeclInfo->getTemplateKind() != DeclInfo::NotTemplate;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000545}
546
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000547ArrayRef<const ParmVarDecl *> Sema::getParamVars() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000548 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko00c59f72012-07-24 20:58:46 +0000549 inspectThisDecl();
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000550 return ThisDeclInfo->ParamVars;
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000551}
552
553void Sema::inspectThisDecl() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000554 ThisDeclInfo->fill();
555 ParamVarDocs.resize(ThisDeclInfo->ParamVars.size(), NULL);
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000556}
557
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000558unsigned Sema::resolveParmVarReference(StringRef Name,
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000559 ArrayRef<const ParmVarDecl *> ParamVars) {
560 for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000561 const IdentifierInfo *II = ParamVars[i]->getIdentifier();
562 if (II && II->getName() == Name)
563 return i;
564 }
565 return ParamCommandComment::InvalidParamIndex;
566}
567
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000568namespace {
569class SimpleTypoCorrector {
570 StringRef Typo;
571 const unsigned MaxEditDistance;
572
573 const NamedDecl *BestDecl;
574 unsigned BestEditDistance;
575 unsigned BestIndex;
576 unsigned NextIndex;
577
578public:
579 SimpleTypoCorrector(StringRef Typo) :
580 Typo(Typo), MaxEditDistance((Typo.size() + 2) / 3),
581 BestDecl(NULL), BestEditDistance(MaxEditDistance + 1),
582 BestIndex(0), NextIndex(0)
583 { }
584
585 void addDecl(const NamedDecl *ND);
586
587 const NamedDecl *getBestDecl() const {
588 if (BestEditDistance > MaxEditDistance)
589 return NULL;
590
591 return BestDecl;
592 }
593
594 unsigned getBestDeclIndex() const {
595 assert(getBestDecl());
596 return BestIndex;
597 }
598};
599
600void SimpleTypoCorrector::addDecl(const NamedDecl *ND) {
601 unsigned CurrIndex = NextIndex++;
602
603 const IdentifierInfo *II = ND->getIdentifier();
604 if (!II)
605 return;
606
607 StringRef Name = II->getName();
608 unsigned MinPossibleEditDistance = abs((int)Name.size() - (int)Typo.size());
609 if (MinPossibleEditDistance > 0 &&
610 Typo.size() / MinPossibleEditDistance < 3)
611 return;
612
613 unsigned EditDistance = Typo.edit_distance(Name, true, MaxEditDistance);
614 if (EditDistance < BestEditDistance) {
615 BestEditDistance = EditDistance;
616 BestDecl = ND;
617 BestIndex = CurrIndex;
618 }
619}
620} // unnamed namespace
621
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000622unsigned Sema::correctTypoInParmVarReference(
623 StringRef Typo,
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000624 ArrayRef<const ParmVarDecl *> ParamVars) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000625 SimpleTypoCorrector Corrector(Typo);
626 for (unsigned i = 0, e = ParamVars.size(); i != e; ++i)
627 Corrector.addDecl(ParamVars[i]);
628 if (Corrector.getBestDecl())
629 return Corrector.getBestDeclIndex();
630 else
631 return ParamCommandComment::InvalidParamIndex;;
632}
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000633
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000634namespace {
635bool ResolveTParamReferenceHelper(
636 StringRef Name,
637 const TemplateParameterList *TemplateParameters,
638 SmallVectorImpl<unsigned> *Position) {
639 for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
640 const NamedDecl *Param = TemplateParameters->getParam(i);
641 const IdentifierInfo *II = Param->getIdentifier();
642 if (II && II->getName() == Name) {
643 Position->push_back(i);
644 return true;
645 }
646
647 if (const TemplateTemplateParmDecl *TTP =
648 dyn_cast<TemplateTemplateParmDecl>(Param)) {
649 Position->push_back(i);
650 if (ResolveTParamReferenceHelper(Name, TTP->getTemplateParameters(),
651 Position))
652 return true;
653 Position->pop_back();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000654 }
655 }
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000656 return false;
657}
658} // unnamed namespace
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000659
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000660bool Sema::resolveTParamReference(
661 StringRef Name,
662 const TemplateParameterList *TemplateParameters,
663 SmallVectorImpl<unsigned> *Position) {
664 Position->clear();
665 if (!TemplateParameters)
666 return false;
667
668 return ResolveTParamReferenceHelper(Name, TemplateParameters, Position);
669}
670
671namespace {
672void CorrectTypoInTParamReferenceHelper(
673 const TemplateParameterList *TemplateParameters,
674 SimpleTypoCorrector &Corrector) {
675 for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
676 const NamedDecl *Param = TemplateParameters->getParam(i);
677 Corrector.addDecl(Param);
678
679 if (const TemplateTemplateParmDecl *TTP =
680 dyn_cast<TemplateTemplateParmDecl>(Param))
681 CorrectTypoInTParamReferenceHelper(TTP->getTemplateParameters(),
682 Corrector);
683 }
684}
685} // unnamed namespace
686
687StringRef Sema::correctTypoInTParamReference(
688 StringRef Typo,
689 const TemplateParameterList *TemplateParameters) {
690 SimpleTypoCorrector Corrector(Typo);
691 CorrectTypoInTParamReferenceHelper(TemplateParameters, Corrector);
692 if (const NamedDecl *ND = Corrector.getBestDecl()) {
693 const IdentifierInfo *II = ND->getIdentifier();
694 assert(II && "SimpleTypoCorrector should not return this decl");
695 return II->getName();
696 }
697 return StringRef();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000698}
699
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000700// TODO: tablegen
701bool Sema::isBlockCommand(StringRef Name) {
Dmitri Gribenko9443c572012-08-06 17:08:27 +0000702 return isBriefCommand(Name) || isReturnsCommand(Name) ||
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +0000703 isParamCommand(Name) || isTParamCommand(Name) ||
704 llvm::StringSwitch<bool>(Name)
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000705 .Case("author", true)
706 .Case("authors", true)
707 .Case("pre", true)
708 .Case("post", true)
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +0000709 .Default(false);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000710}
711
712bool Sema::isParamCommand(StringRef Name) {
713 return llvm::StringSwitch<bool>(Name)
714 .Case("param", true)
715 .Case("arg", true)
716 .Default(false);
717}
718
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000719bool Sema::isTParamCommand(StringRef Name) {
720 return Name == "tparam";
721}
722
Dmitri Gribenko9443c572012-08-06 17:08:27 +0000723bool Sema::isBriefCommand(StringRef Name) {
724 return Name == "brief" || Name == "short";
725}
726
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +0000727bool Sema::isReturnsCommand(StringRef Name) {
728 return Name == "returns" || Name == "return" || Name == "result";
729}
730
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000731unsigned Sema::getBlockCommandNumArgs(StringRef Name) {
732 return llvm::StringSwitch<unsigned>(Name)
Dmitri Gribenko3d3d22c2012-07-18 00:44:55 +0000733 .Cases("brief", "short", 0)
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000734 .Case("pre", 0)
735 .Case("post", 0)
736 .Case("author", 0)
737 .Case("authors", 0)
738 .Default(0);
739}
740
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000741bool Sema::isInlineCommand(StringRef Name) const {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000742 return llvm::StringSwitch<bool>(Name)
Dmitri Gribenkoc48dd8e2012-07-19 00:21:03 +0000743 .Case("b", true)
744 .Cases("c", "p", true)
745 .Cases("a", "e", "em", true)
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000746 .Default(false);
747}
748
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000749InlineCommandComment::RenderKind
750Sema::getInlineCommandRenderKind(StringRef Name) const {
751 assert(isInlineCommand(Name));
752
753 return llvm::StringSwitch<InlineCommandComment::RenderKind>(Name)
754 .Case("b", InlineCommandComment::RenderBold)
755 .Cases("c", "p", InlineCommandComment::RenderMonospaced)
756 .Cases("a", "e", "em", InlineCommandComment::RenderEmphasized)
757 .Default(InlineCommandComment::RenderNormal);
758}
759
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000760bool Sema::isHTMLEndTagOptional(StringRef Name) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000761 return llvm::StringSwitch<bool>(Name)
Dmitri Gribenko3d986982012-07-12 23:37:09 +0000762 .Case("p", true)
763 .Case("li", true)
764 .Case("dt", true)
765 .Case("dd", true)
766 .Case("tr", true)
767 .Case("th", true)
768 .Case("td", true)
769 .Case("thead", true)
770 .Case("tfoot", true)
771 .Case("tbody", true)
772 .Case("colgroup", true)
773 .Default(false);
774}
775
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000776bool Sema::isHTMLEndTagForbidden(StringRef Name) {
Dmitri Gribenko3d986982012-07-12 23:37:09 +0000777 return llvm::StringSwitch<bool>(Name)
778 .Case("br", true)
779 .Case("hr", true)
780 .Case("img", true)
781 .Case("col", true)
782 .Default(false);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000783}
784
785} // end namespace comments
786} // end namespace clang
787