blob: 978c748b7416da8347e9a74265091aaac46fb816 [file] [log] [blame]
Dmitri Gribenkoec925312012-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 Gribenkof26054f2012-07-11 21:38:39 +000011#include "clang/AST/CommentDiagnostic.h"
12#include "clang/AST/Decl.h"
Dmitri Gribenko34df2202012-07-31 22:37:06 +000013#include "clang/AST/DeclTemplate.h"
Dmitri Gribenkof26054f2012-07-11 21:38:39 +000014#include "clang/Basic/SourceManager.h"
Dmitri Gribenkoec925312012-07-06 00:28:32 +000015#include "llvm/ADT/StringSwitch.h"
16
17namespace clang {
18namespace comments {
19
Dmitri Gribenkof26054f2012-07-11 21:38:39 +000020Sema::Sema(llvm::BumpPtrAllocator &Allocator, const SourceManager &SourceMgr,
21 DiagnosticsEngine &Diags) :
Dmitri Gribenko527ab212012-08-01 23:08:09 +000022 Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags),
Dmitri Gribenko5ec0c752012-08-06 17:08:27 +000023 ThisDeclInfo(NULL), BriefCommand(NULL), ReturnsCommand(NULL) {
Dmitri Gribenkof26054f2012-07-11 21:38:39 +000024}
25
26void Sema::setDecl(const Decl *D) {
Dmitri Gribenko527ab212012-08-01 23:08:09 +000027 if (!D)
28 return;
29
30 ThisDeclInfo = new (Allocator) DeclInfo;
31 ThisDeclInfo->ThisDecl = D;
Dmitri Gribenkoe6213dd2012-08-01 23:21:57 +000032 ThisDeclInfo->IsFilled = false;
Dmitri Gribenkoec925312012-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 Gribenkoa9770ad2012-08-06 19:03:12 +000046void Sema::actOnBlockCommandArgs(BlockCommandComment *Command,
47 ArrayRef<BlockCommandComment::Argument> Args) {
Dmitri Gribenkoec925312012-07-06 00:28:32 +000048 Command->setArgs(Args);
Dmitri Gribenkoec925312012-07-06 00:28:32 +000049}
50
Dmitri Gribenkoa9770ad2012-08-06 19:03:12 +000051void Sema::actOnBlockCommandFinish(BlockCommandComment *Command,
52 ParagraphComment *Paragraph) {
Dmitri Gribenkoec925312012-07-06 00:28:32 +000053 Command->setParagraph(Paragraph);
Dmitri Gribenkof26054f2012-07-11 21:38:39 +000054 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenko5ec0c752012-08-06 17:08:27 +000055 checkBlockCommandDuplicate(Command);
Dmitri Gribenko64305832012-08-03 21:15:32 +000056 checkReturnsCommand(Command);
Dmitri Gribenkoec925312012-07-06 00:28:32 +000057}
58
59ParamCommandComment *Sema::actOnParamCommandStart(SourceLocation LocBegin,
60 SourceLocation LocEnd,
61 StringRef Name) {
Dmitri Gribenkof26054f2012-07-11 21:38:39 +000062 ParamCommandComment *Command =
63 new (Allocator) ParamCommandComment(LocBegin, LocEnd, Name);
64
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +000065 if (!isFunctionDecl())
Dmitri Gribenkof26054f2012-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 Gribenkoec925312012-07-06 00:28:32 +000071}
72
Dmitri Gribenkoa9770ad2012-08-06 19:03:12 +000073void Sema::actOnParamCommandDirectionArg(ParamCommandComment *Command,
74 SourceLocation ArgLocBegin,
75 SourceLocation ArgLocEnd,
76 StringRef Arg) {
Dmitri Gribenkof26054f2012-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 Gribenkoec925312012-07-06 00:28:32 +000098
Dmitri Gribenkof26054f2012-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 Gribenkoec925312012-07-06 00:28:32 +0000109 } else {
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000110 Direction = ParamCommandComment::In;
111 RemovingWhitespaceHelped = false;
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000112 }
Dmitri Gribenkof26054f2012-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 Gribenkoec925312012-07-06 00:28:32 +0000124 }
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000125 Command->setDirection(Direction, /* Explicit = */ true);
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000126}
127
Dmitri Gribenkoa9770ad2012-08-06 19:03:12 +0000128void Sema::actOnParamCommandParamNameArg(ParamCommandComment *Command,
129 SourceLocation ArgLocBegin,
130 SourceLocation ArgLocEnd,
131 StringRef Arg) {
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000132 // Parser will not feed us more arguments than needed.
Dmitri Gribenko619e75e2012-07-13 19:02:42 +0000133 assert(Command->getNumArgs() == 0);
Dmitri Gribenkof26054f2012-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 Gribenko4b7f5fe2012-07-23 17:40:30 +0000145 if (!isFunctionDecl()) {
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000146 // We already warned that this \\param is not attached to a function decl.
Dmitri Gribenkoa9770ad2012-08-06 19:03:12 +0000147 return;
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000148 }
149
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000150 ArrayRef<const ParmVarDecl *> ParamVars = getParamVars();
151
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000152 // Check that referenced parameter name is in the function decl.
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000153 const unsigned ResolvedParamIndex = resolveParmVarReference(Arg, ParamVars);
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000154 if (ResolvedParamIndex != ParamCommandComment::InvalidParamIndex) {
155 Command->setParamIndex(ResolvedParamIndex);
Dmitri Gribenko8f0f1b02012-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 Gribenkoa9770ad2012-08-06 19:03:12 +0000165 return;
Dmitri Gribenkof26054f2012-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 Gribenkof24990a2012-07-27 21:34:43 +0000172 // No parameters -- can't suggest a correction.
173 if (ParamVars.size() == 0)
Dmitri Gribenkoa9770ad2012-08-06 19:03:12 +0000174 return;
Dmitri Gribenkof24990a2012-07-27 21:34:43 +0000175
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000176 unsigned CorrectedParamIndex = ParamCommandComment::InvalidParamIndex;
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000177 if (ParamVars.size() == 1) {
Dmitri Gribenkof26054f2012-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 Gribenko4b7f5fe2012-07-23 17:40:30 +0000183 CorrectedParamIndex = correctTypoInParmVarReference(Arg, ParamVars);
Dmitri Gribenkof26054f2012-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 Gribenkoa9770ad2012-08-06 19:03:12 +0000193 return;
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000194}
195
Dmitri Gribenkoa9770ad2012-08-06 19:03:12 +0000196void Sema::actOnParamCommandFinish(ParamCommandComment *Command,
197 ParagraphComment *Paragraph) {
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000198 Command->setParagraph(Paragraph);
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000199 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000200}
201
Dmitri Gribenko34df2202012-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 Gribenko8e5d5f12012-08-06 21:31:15 +0000208 if (!isTemplateOrSpecialization())
Dmitri Gribenko34df2202012-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 Gribenkoa9770ad2012-08-06 19:03:12 +0000216void Sema::actOnTParamCommandParamNameArg(TParamCommandComment *Command,
217 SourceLocation ArgLocBegin,
218 SourceLocation ArgLocEnd,
219 StringRef Arg) {
Dmitri Gribenko34df2202012-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 Gribenko8e5d5f12012-08-06 21:31:15 +0000229 if (!isTemplateOrSpecialization()) {
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000230 // We already warned that this \\tparam is not attached to a template decl.
Dmitri Gribenkoa9770ad2012-08-06 19:03:12 +0000231 return;
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000232 }
233
Dmitri Gribenko527ab212012-08-01 23:08:09 +0000234 const TemplateParameterList *TemplateParameters =
235 ThisDeclInfo->TemplateParameters;
Dmitri Gribenko34df2202012-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 Gribenkoa9770ad2012-08-06 19:03:12 +0000250 return;
Dmitri Gribenko34df2202012-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 Gribenkoa9770ad2012-08-06 19:03:12 +0000258 return;
Dmitri Gribenko34df2202012-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 Gribenkoa9770ad2012-08-06 19:03:12 +0000276 return;
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000277}
278
Dmitri Gribenkoa9770ad2012-08-06 19:03:12 +0000279void Sema::actOnTParamCommandFinish(TParamCommandComment *Command,
280 ParagraphComment *Paragraph) {
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000281 Command->setParagraph(Paragraph);
282 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000283}
284
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000285InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
286 SourceLocation CommandLocEnd,
287 StringRef CommandName) {
288 ArrayRef<InlineCommandComment::Argument> Args;
Dmitri Gribenkod73e4ce2012-07-23 16:43:01 +0000289 return new (Allocator) InlineCommandComment(
290 CommandLocBegin,
291 CommandLocEnd,
292 CommandName,
293 getInlineCommandRenderKind(CommandName),
294 Args);
Dmitri Gribenkoec925312012-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 Gribenkod73e4ce2012-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 Gribenkoec925312012-07-06 00:28:32 +0000314}
315
316InlineContentComment *Sema::actOnUnknownCommand(SourceLocation LocBegin,
317 SourceLocation LocEnd,
318 StringRef Name) {
319 ArrayRef<InlineCommandComment::Argument> Args;
Dmitri Gribenkod73e4ce2012-07-23 16:43:01 +0000320 return new (Allocator) InlineCommandComment(
321 LocBegin, LocEnd, Name,
322 InlineCommandComment::RenderNormal,
323 Args);
Dmitri Gribenkoec925312012-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 Gribenkoa9770ad2012-08-06 19:03:12 +0000345void Sema::actOnVerbatimBlockFinish(
Dmitri Gribenkoec925312012-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 Gribenkoec925312012-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 Gribenkoe00ffc72012-07-13 00:44:24 +0000366HTMLStartTagComment *Sema::actOnHTMLStartTagStart(SourceLocation LocBegin,
367 StringRef TagName) {
368 return new (Allocator) HTMLStartTagComment(LocBegin, TagName);
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000369}
370
Dmitri Gribenkoa9770ad2012-08-06 19:03:12 +0000371void Sema::actOnHTMLStartTagFinish(
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000372 HTMLStartTagComment *Tag,
373 ArrayRef<HTMLStartTagComment::Attribute> Attrs,
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000374 SourceLocation GreaterLoc,
375 bool IsSelfClosing) {
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000376 Tag->setAttrs(Attrs);
377 Tag->setGreaterLoc(GreaterLoc);
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000378 if (IsSelfClosing)
379 Tag->setSelfClosing();
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000380 else if (!isHTMLEndTagForbidden(Tag->getTagName()))
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000381 HTMLOpenTags.push_back(Tag);
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000382}
383
Dmitri Gribenkoe00ffc72012-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 Gribenko9460fbf2012-07-12 23:37:09 +0000393 }
394
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000395 bool FoundOpen = false;
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000396 for (SmallVectorImpl<HTMLStartTagComment *>::const_reverse_iterator
Dmitri Gribenkof26054f2012-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 Gribenkoe00ffc72012-07-13 00:44:24 +0000405 Diag(HET->getLocation(), diag::warn_doc_html_end_unbalanced)
406 << HET->getSourceRange();
407 return HET;
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000408 }
409
410 while (!HTMLOpenTags.empty()) {
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000411 const HTMLStartTagComment *HST = HTMLOpenTags.back();
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000412 HTMLOpenTags.pop_back();
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000413 StringRef LastNotClosedTagName = HST->getTagName();
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000414 if (LastNotClosedTagName == TagName)
415 break;
416
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000417 if (isHTMLEndTagOptional(LastNotClosedTagName))
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000418 continue;
419
420 bool OpenLineInvalid;
421 const unsigned OpenLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000422 HST->getLocation(),
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000423 &OpenLineInvalid);
424 bool CloseLineInvalid;
425 const unsigned CloseLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000426 HET->getLocation(),
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000427 &CloseLineInvalid);
428
429 if (OpenLineInvalid || CloseLineInvalid || OpenLine == CloseLine)
Dmitri Gribenkoe00ffc72012-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 Gribenkof26054f2012-07-11 21:38:39 +0000433 else {
Dmitri Gribenkoe00ffc72012-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 Gribenkof26054f2012-07-11 21:38:39 +0000439 }
440 }
441
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000442 return HET;
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000443}
444
445FullComment *Sema::actOnFullComment(
446 ArrayRef<BlockContentComment *> Blocks) {
Dmitri Gribenko527ab212012-08-01 23:08:09 +0000447 return new (Allocator) FullComment(Blocks, ThisDeclInfo);
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000448}
449
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000450void Sema::checkBlockCommandEmptyParagraph(BlockCommandComment *Command) {
451 ParagraphComment *Paragraph = Command->getParagraph();
452 if (Paragraph->isWhitespace()) {
453 SourceLocation DiagLoc;
Dmitri Gribenko619e75e2012-07-13 19:02:42 +0000454 if (Command->getNumArgs() > 0)
455 DiagLoc = Command->getArgRange(Command->getNumArgs() - 1).getEnd();
Dmitri Gribenkof26054f2012-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 Gribenko64305832012-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 Gribenko558babc2012-08-06 16:29:26 +0000472 if (ThisDeclInfo->IsObjCMethod)
473 DiagKind = 3;
474 else
475 DiagKind = 0;
Dmitri Gribenko64305832012-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 Gribenko5ec0c752012-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 Gribenko4b7f5fe2012-07-23 17:40:30 +0000531bool Sema::isFunctionDecl() {
Dmitri Gribenko527ab212012-08-01 23:08:09 +0000532 if (!ThisDeclInfo)
533 return false;
534 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko52cb2182012-07-24 20:58:46 +0000535 inspectThisDecl();
Dmitri Gribenko37a7faf2012-08-02 21:45:39 +0000536 return ThisDeclInfo->getKind() == DeclInfo::FunctionKind;
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000537}
538
Dmitri Gribenko8e5d5f12012-08-06 21:31:15 +0000539bool Sema::isTemplateOrSpecialization() {
Dmitri Gribenko527ab212012-08-01 23:08:09 +0000540 if (!ThisDeclInfo)
541 return false;
542 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000543 inspectThisDecl();
Dmitri Gribenko8e5d5f12012-08-06 21:31:15 +0000544 return ThisDeclInfo->getTemplateKind() != DeclInfo::NotTemplate;
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000545}
546
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000547ArrayRef<const ParmVarDecl *> Sema::getParamVars() {
Dmitri Gribenko527ab212012-08-01 23:08:09 +0000548 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko52cb2182012-07-24 20:58:46 +0000549 inspectThisDecl();
Dmitri Gribenko527ab212012-08-01 23:08:09 +0000550 return ThisDeclInfo->ParamVars;
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000551}
552
553void Sema::inspectThisDecl() {
Dmitri Gribenko527ab212012-08-01 23:08:09 +0000554 ThisDeclInfo->fill();
555 ParamVarDocs.resize(ThisDeclInfo->ParamVars.size(), NULL);
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000556}
557
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000558unsigned Sema::resolveParmVarReference(StringRef Name,
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000559 ArrayRef<const ParmVarDecl *> ParamVars) {
560 for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) {
Dmitri Gribenkof26054f2012-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 Gribenko34df2202012-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 Gribenkof26054f2012-07-11 21:38:39 +0000622unsigned Sema::correctTypoInParmVarReference(
623 StringRef Typo,
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000624 ArrayRef<const ParmVarDecl *> ParamVars) {
Dmitri Gribenko34df2202012-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 Gribenkof26054f2012-07-11 21:38:39 +0000633
Dmitri Gribenko34df2202012-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 Gribenkof26054f2012-07-11 21:38:39 +0000654 }
655 }
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000656 return false;
657}
658} // unnamed namespace
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000659
Dmitri Gribenko34df2202012-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 Gribenkof26054f2012-07-11 21:38:39 +0000698}
699
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000700// TODO: tablegen
701bool Sema::isBlockCommand(StringRef Name) {
Dmitri Gribenko5ec0c752012-08-06 17:08:27 +0000702 return isBriefCommand(Name) || isReturnsCommand(Name) ||
Dmitri Gribenko64305832012-08-03 21:15:32 +0000703 isParamCommand(Name) || isTParamCommand(Name) ||
704 llvm::StringSwitch<bool>(Name)
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000705 .Case("author", true)
706 .Case("authors", true)
707 .Case("pre", true)
708 .Case("post", true)
Dmitri Gribenko64305832012-08-03 21:15:32 +0000709 .Default(false);
Dmitri Gribenkoec925312012-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 Gribenko34df2202012-07-31 22:37:06 +0000719bool Sema::isTParamCommand(StringRef Name) {
720 return Name == "tparam";
721}
722
Dmitri Gribenko5ec0c752012-08-06 17:08:27 +0000723bool Sema::isBriefCommand(StringRef Name) {
724 return Name == "brief" || Name == "short";
725}
726
Dmitri Gribenko64305832012-08-03 21:15:32 +0000727bool Sema::isReturnsCommand(StringRef Name) {
728 return Name == "returns" || Name == "return" || Name == "result";
729}
730
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000731unsigned Sema::getBlockCommandNumArgs(StringRef Name) {
732 return llvm::StringSwitch<unsigned>(Name)
Dmitri Gribenko31005192012-07-18 00:44:55 +0000733 .Cases("brief", "short", 0)
Dmitri Gribenkoec925312012-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 Gribenkod73e4ce2012-07-23 16:43:01 +0000741bool Sema::isInlineCommand(StringRef Name) const {
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000742 return llvm::StringSwitch<bool>(Name)
Dmitri Gribenkoe82ae812012-07-19 00:21:03 +0000743 .Case("b", true)
744 .Cases("c", "p", true)
745 .Cases("a", "e", "em", true)
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000746 .Default(false);
747}
748
Dmitri Gribenkod73e4ce2012-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 Gribenkoe00ffc72012-07-13 00:44:24 +0000760bool Sema::isHTMLEndTagOptional(StringRef Name) {
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000761 return llvm::StringSwitch<bool>(Name)
Dmitri Gribenko9460fbf2012-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 Gribenkoe00ffc72012-07-13 00:44:24 +0000776bool Sema::isHTMLEndTagForbidden(StringRef Name) {
Dmitri Gribenko9460fbf2012-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 Gribenkoec925312012-07-06 00:28:32 +0000783}
784
785} // end namespace comments
786} // end namespace clang
787