blob: 4f9f1f241c8cbbba0d6293a9ef73b66c2650aedf [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"
Dmitri Gribenkoaa580812012-08-09 00:03:17 +000012#include "clang/AST/CommentCommandTraits.h"
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000013#include "clang/AST/Decl.h"
Dmitri Gribenko96b09862012-07-31 22:37:06 +000014#include "clang/AST/DeclTemplate.h"
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000015#include "clang/Basic/SourceManager.h"
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000016#include "llvm/ADT/StringSwitch.h"
17
18namespace clang {
19namespace comments {
20
Dmitri Gribenkoc24a76e2012-08-31 02:21:44 +000021namespace {
22#include "clang/AST/CommentHTMLTagsProperties.inc"
23} // unnamed namespace
24
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000025Sema::Sema(llvm::BumpPtrAllocator &Allocator, const SourceManager &SourceMgr,
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +000026 DiagnosticsEngine &Diags, CommandTraits &Traits) :
Dmitri Gribenkoaa580812012-08-09 00:03:17 +000027 Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags), Traits(Traits),
Dmitri Gribenko9443c572012-08-06 17:08:27 +000028 ThisDeclInfo(NULL), BriefCommand(NULL), ReturnsCommand(NULL) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000029}
30
31void Sema::setDecl(const Decl *D) {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +000032 if (!D)
33 return;
34
35 ThisDeclInfo = new (Allocator) DeclInfo;
36 ThisDeclInfo->ThisDecl = D;
Dmitri Gribenko651f8ce2012-08-01 23:21:57 +000037 ThisDeclInfo->IsFilled = false;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000038}
39
40ParagraphComment *Sema::actOnParagraphComment(
41 ArrayRef<InlineContentComment *> Content) {
42 return new (Allocator) ParagraphComment(Content);
43}
44
45BlockCommandComment *Sema::actOnBlockCommandStart(SourceLocation LocBegin,
46 SourceLocation LocEnd,
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +000047 unsigned CommandID) {
48 return new (Allocator) BlockCommandComment(LocBegin, LocEnd, CommandID);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000049}
50
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +000051void Sema::actOnBlockCommandArgs(BlockCommandComment *Command,
52 ArrayRef<BlockCommandComment::Argument> Args) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000053 Command->setArgs(Args);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000054}
55
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +000056void Sema::actOnBlockCommandFinish(BlockCommandComment *Command,
57 ParagraphComment *Paragraph) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000058 Command->setParagraph(Paragraph);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000059 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenko9443c572012-08-06 17:08:27 +000060 checkBlockCommandDuplicate(Command);
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +000061 checkReturnsCommand(Command);
Dmitri Gribenko0bd98382012-09-22 21:47:50 +000062 checkDeprecatedCommand(Command);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000063}
64
65ParamCommandComment *Sema::actOnParamCommandStart(SourceLocation LocBegin,
66 SourceLocation LocEnd,
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +000067 unsigned CommandID) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000068 ParamCommandComment *Command =
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +000069 new (Allocator) ParamCommandComment(LocBegin, LocEnd, CommandID);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000070
Dmitri Gribenko8487c522012-07-23 17:40:30 +000071 if (!isFunctionDecl())
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000072 Diag(Command->getLocation(),
73 diag::warn_doc_param_not_attached_to_a_function_decl)
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +000074 << Command->getCommandNameRange(Traits);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000075
76 return Command;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000077}
78
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +000079void Sema::actOnParamCommandDirectionArg(ParamCommandComment *Command,
80 SourceLocation ArgLocBegin,
81 SourceLocation ArgLocEnd,
82 StringRef Arg) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000083 ParamCommandComment::PassDirection Direction;
84 std::string ArgLower = Arg.lower();
85 // TODO: optimize: lower Name first (need an API in SmallString for that),
86 // after that StringSwitch.
87 if (ArgLower == "[in]")
88 Direction = ParamCommandComment::In;
89 else if (ArgLower == "[out]")
90 Direction = ParamCommandComment::Out;
91 else if (ArgLower == "[in,out]" || ArgLower == "[out,in]")
92 Direction = ParamCommandComment::InOut;
93 else {
94 // Remove spaces.
95 std::string::iterator O = ArgLower.begin();
96 for (std::string::iterator I = ArgLower.begin(), E = ArgLower.end();
97 I != E; ++I) {
98 const char C = *I;
99 if (C != ' ' && C != '\n' && C != '\r' &&
100 C != '\t' && C != '\v' && C != '\f')
101 *O++ = C;
102 }
103 ArgLower.resize(O - ArgLower.begin());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000104
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000105 bool RemovingWhitespaceHelped = false;
106 if (ArgLower == "[in]") {
107 Direction = ParamCommandComment::In;
108 RemovingWhitespaceHelped = true;
109 } else if (ArgLower == "[out]") {
110 Direction = ParamCommandComment::Out;
111 RemovingWhitespaceHelped = true;
112 } else if (ArgLower == "[in,out]" || ArgLower == "[out,in]") {
113 Direction = ParamCommandComment::InOut;
114 RemovingWhitespaceHelped = true;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000115 } else {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000116 Direction = ParamCommandComment::In;
117 RemovingWhitespaceHelped = false;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000118 }
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000119
120 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
121 if (RemovingWhitespaceHelped)
122 Diag(ArgLocBegin, diag::warn_doc_param_spaces_in_direction)
123 << ArgRange
124 << FixItHint::CreateReplacement(
125 ArgRange,
126 ParamCommandComment::getDirectionAsString(Direction));
127 else
128 Diag(ArgLocBegin, diag::warn_doc_param_invalid_direction)
129 << ArgRange;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000130 }
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000131 Command->setDirection(Direction, /* Explicit = */ true);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000132}
133
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000134void Sema::actOnParamCommandParamNameArg(ParamCommandComment *Command,
135 SourceLocation ArgLocBegin,
136 SourceLocation ArgLocEnd,
137 StringRef Arg) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000138 // Parser will not feed us more arguments than needed.
Dmitri Gribenko0eaf69d2012-07-13 19:02:42 +0000139 assert(Command->getNumArgs() == 0);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000140
141 if (!Command->isDirectionExplicit()) {
142 // User didn't provide a direction argument.
143 Command->setDirection(ParamCommandComment::In, /* Explicit = */ false);
144 }
145 typedef BlockCommandComment::Argument Argument;
146 Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
147 ArgLocEnd),
148 Arg);
149 Command->setArgs(llvm::makeArrayRef(A, 1));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000150}
151
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000152void Sema::actOnParamCommandFinish(ParamCommandComment *Command,
153 ParagraphComment *Paragraph) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000154 Command->setParagraph(Paragraph);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000155 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000156}
157
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000158TParamCommandComment *Sema::actOnTParamCommandStart(SourceLocation LocBegin,
159 SourceLocation LocEnd,
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000160 unsigned CommandID) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000161 TParamCommandComment *Command =
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000162 new (Allocator) TParamCommandComment(LocBegin, LocEnd, CommandID);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000163
Dmitri Gribenko04bf29e2012-08-06 21:31:15 +0000164 if (!isTemplateOrSpecialization())
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000165 Diag(Command->getLocation(),
166 diag::warn_doc_tparam_not_attached_to_a_template_decl)
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000167 << Command->getCommandNameRange(Traits);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000168
169 return Command;
170}
171
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000172void Sema::actOnTParamCommandParamNameArg(TParamCommandComment *Command,
173 SourceLocation ArgLocBegin,
174 SourceLocation ArgLocEnd,
175 StringRef Arg) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000176 // Parser will not feed us more arguments than needed.
177 assert(Command->getNumArgs() == 0);
178
179 typedef BlockCommandComment::Argument Argument;
180 Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
181 ArgLocEnd),
182 Arg);
183 Command->setArgs(llvm::makeArrayRef(A, 1));
184
Dmitri Gribenko04bf29e2012-08-06 21:31:15 +0000185 if (!isTemplateOrSpecialization()) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000186 // We already warned that this \\tparam is not attached to a template decl.
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000187 return;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000188 }
189
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000190 const TemplateParameterList *TemplateParameters =
191 ThisDeclInfo->TemplateParameters;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000192 SmallVector<unsigned, 2> Position;
193 if (resolveTParamReference(Arg, TemplateParameters, &Position)) {
194 Command->setPosition(copyArray(llvm::makeArrayRef(Position)));
195 llvm::StringMap<TParamCommandComment *>::iterator PrevCommandIt =
196 TemplateParameterDocs.find(Arg);
197 if (PrevCommandIt != TemplateParameterDocs.end()) {
198 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
199 Diag(ArgLocBegin, diag::warn_doc_tparam_duplicate)
200 << Arg << ArgRange;
201 TParamCommandComment *PrevCommand = PrevCommandIt->second;
202 Diag(PrevCommand->getLocation(), diag::note_doc_tparam_previous)
203 << PrevCommand->getParamNameRange();
204 }
205 TemplateParameterDocs[Arg] = Command;
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000206 return;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000207 }
208
209 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
210 Diag(ArgLocBegin, diag::warn_doc_tparam_not_found)
211 << Arg << ArgRange;
212
213 if (!TemplateParameters || TemplateParameters->size() == 0)
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000214 return;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000215
216 StringRef CorrectedName;
217 if (TemplateParameters->size() == 1) {
218 const NamedDecl *Param = TemplateParameters->getParam(0);
219 const IdentifierInfo *II = Param->getIdentifier();
220 if (II)
221 CorrectedName = II->getName();
222 } else {
223 CorrectedName = correctTypoInTParamReference(Arg, TemplateParameters);
224 }
225
226 if (!CorrectedName.empty()) {
227 Diag(ArgLocBegin, diag::note_doc_tparam_name_suggestion)
228 << CorrectedName
229 << FixItHint::CreateReplacement(ArgRange, CorrectedName);
230 }
231
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000232 return;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000233}
234
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000235void Sema::actOnTParamCommandFinish(TParamCommandComment *Command,
236 ParagraphComment *Paragraph) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000237 Command->setParagraph(Paragraph);
238 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000239}
240
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000241InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
242 SourceLocation CommandLocEnd,
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000243 unsigned CommandID) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000244 ArrayRef<InlineCommandComment::Argument> Args;
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000245 StringRef CommandName = Traits.getCommandInfo(CommandID)->Name;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000246 return new (Allocator) InlineCommandComment(
247 CommandLocBegin,
248 CommandLocEnd,
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000249 CommandID,
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000250 getInlineCommandRenderKind(CommandName),
251 Args);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000252}
253
254InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
255 SourceLocation CommandLocEnd,
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000256 unsigned CommandID,
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000257 SourceLocation ArgLocBegin,
258 SourceLocation ArgLocEnd,
259 StringRef Arg) {
260 typedef InlineCommandComment::Argument Argument;
261 Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
262 ArgLocEnd),
263 Arg);
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000264 StringRef CommandName = Traits.getCommandInfo(CommandID)->Name;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000265
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000266 return new (Allocator) InlineCommandComment(
267 CommandLocBegin,
268 CommandLocEnd,
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000269 CommandID,
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000270 getInlineCommandRenderKind(CommandName),
271 llvm::makeArrayRef(A, 1));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000272}
273
274InlineContentComment *Sema::actOnUnknownCommand(SourceLocation LocBegin,
275 SourceLocation LocEnd,
Dmitri Gribenkob0b8a962012-09-11 19:22:03 +0000276 StringRef CommandName) {
277 unsigned CommandID = Traits.registerUnknownCommand(CommandName)->getID();
278 return actOnUnknownCommand(LocBegin, LocEnd, CommandID);
279}
280
281InlineContentComment *Sema::actOnUnknownCommand(SourceLocation LocBegin,
282 SourceLocation LocEnd,
283 unsigned CommandID) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000284 ArrayRef<InlineCommandComment::Argument> Args;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000285 return new (Allocator) InlineCommandComment(
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000286 LocBegin, LocEnd, CommandID,
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000287 InlineCommandComment::RenderNormal,
288 Args);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000289}
290
291TextComment *Sema::actOnText(SourceLocation LocBegin,
292 SourceLocation LocEnd,
293 StringRef Text) {
294 return new (Allocator) TextComment(LocBegin, LocEnd, Text);
295}
296
297VerbatimBlockComment *Sema::actOnVerbatimBlockStart(SourceLocation Loc,
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000298 unsigned CommandID) {
299 StringRef CommandName = Traits.getCommandInfo(CommandID)->Name;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000300 return new (Allocator) VerbatimBlockComment(
301 Loc,
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000302 Loc.getLocWithOffset(1 + CommandName.size()),
303 CommandID);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000304}
305
306VerbatimBlockLineComment *Sema::actOnVerbatimBlockLine(SourceLocation Loc,
307 StringRef Text) {
308 return new (Allocator) VerbatimBlockLineComment(Loc, Text);
309}
310
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000311void Sema::actOnVerbatimBlockFinish(
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000312 VerbatimBlockComment *Block,
313 SourceLocation CloseNameLocBegin,
314 StringRef CloseName,
315 ArrayRef<VerbatimBlockLineComment *> Lines) {
316 Block->setCloseName(CloseName, CloseNameLocBegin);
317 Block->setLines(Lines);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000318}
319
320VerbatimLineComment *Sema::actOnVerbatimLine(SourceLocation LocBegin,
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000321 unsigned CommandID,
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000322 SourceLocation TextBegin,
323 StringRef Text) {
324 return new (Allocator) VerbatimLineComment(
325 LocBegin,
326 TextBegin.getLocWithOffset(Text.size()),
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000327 CommandID,
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000328 TextBegin,
329 Text);
330}
331
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000332HTMLStartTagComment *Sema::actOnHTMLStartTagStart(SourceLocation LocBegin,
333 StringRef TagName) {
334 return new (Allocator) HTMLStartTagComment(LocBegin, TagName);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000335}
336
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000337void Sema::actOnHTMLStartTagFinish(
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000338 HTMLStartTagComment *Tag,
339 ArrayRef<HTMLStartTagComment::Attribute> Attrs,
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000340 SourceLocation GreaterLoc,
341 bool IsSelfClosing) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000342 Tag->setAttrs(Attrs);
343 Tag->setGreaterLoc(GreaterLoc);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000344 if (IsSelfClosing)
345 Tag->setSelfClosing();
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000346 else if (!isHTMLEndTagForbidden(Tag->getTagName()))
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000347 HTMLOpenTags.push_back(Tag);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000348}
349
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000350HTMLEndTagComment *Sema::actOnHTMLEndTag(SourceLocation LocBegin,
351 SourceLocation LocEnd,
352 StringRef TagName) {
353 HTMLEndTagComment *HET =
354 new (Allocator) HTMLEndTagComment(LocBegin, LocEnd, TagName);
355 if (isHTMLEndTagForbidden(TagName)) {
356 Diag(HET->getLocation(), diag::warn_doc_html_end_forbidden)
357 << TagName << HET->getSourceRange();
358 return HET;
Dmitri Gribenko3d986982012-07-12 23:37:09 +0000359 }
360
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000361 bool FoundOpen = false;
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000362 for (SmallVectorImpl<HTMLStartTagComment *>::const_reverse_iterator
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000363 I = HTMLOpenTags.rbegin(), E = HTMLOpenTags.rend();
364 I != E; ++I) {
365 if ((*I)->getTagName() == TagName) {
366 FoundOpen = true;
367 break;
368 }
369 }
370 if (!FoundOpen) {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000371 Diag(HET->getLocation(), diag::warn_doc_html_end_unbalanced)
372 << HET->getSourceRange();
373 return HET;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000374 }
375
376 while (!HTMLOpenTags.empty()) {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000377 const HTMLStartTagComment *HST = HTMLOpenTags.back();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000378 HTMLOpenTags.pop_back();
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000379 StringRef LastNotClosedTagName = HST->getTagName();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000380 if (LastNotClosedTagName == TagName)
381 break;
382
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000383 if (isHTMLEndTagOptional(LastNotClosedTagName))
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000384 continue;
385
386 bool OpenLineInvalid;
387 const unsigned OpenLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000388 HST->getLocation(),
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000389 &OpenLineInvalid);
390 bool CloseLineInvalid;
391 const unsigned CloseLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000392 HET->getLocation(),
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000393 &CloseLineInvalid);
394
395 if (OpenLineInvalid || CloseLineInvalid || OpenLine == CloseLine)
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000396 Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
397 << HST->getTagName() << HET->getTagName()
398 << HST->getSourceRange() << HET->getSourceRange();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000399 else {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000400 Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
401 << HST->getTagName() << HET->getTagName()
402 << HST->getSourceRange();
403 Diag(HET->getLocation(), diag::note_doc_html_end_tag)
404 << HET->getSourceRange();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000405 }
406 }
407
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000408 return HET;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000409}
410
411FullComment *Sema::actOnFullComment(
412 ArrayRef<BlockContentComment *> Blocks) {
Dmitri Gribenko9edd2c82012-08-24 17:45:39 +0000413 FullComment *FC = new (Allocator) FullComment(Blocks, ThisDeclInfo);
414 resolveParamCommandIndexes(FC);
415 return FC;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000416}
417
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000418void Sema::checkBlockCommandEmptyParagraph(BlockCommandComment *Command) {
Dmitri Gribenkoabcf0dc2012-09-13 20:36:01 +0000419 if (Traits.getCommandInfo(Command->getCommandID())->IsEmptyParagraphAllowed)
420 return;
421
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000422 ParagraphComment *Paragraph = Command->getParagraph();
423 if (Paragraph->isWhitespace()) {
424 SourceLocation DiagLoc;
Dmitri Gribenko0eaf69d2012-07-13 19:02:42 +0000425 if (Command->getNumArgs() > 0)
426 DiagLoc = Command->getArgRange(Command->getNumArgs() - 1).getEnd();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000427 if (!DiagLoc.isValid())
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000428 DiagLoc = Command->getCommandNameRange(Traits).getEnd();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000429 Diag(DiagLoc, diag::warn_doc_block_command_empty_paragraph)
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000430 << Command->getCommandName(Traits)
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000431 << Command->getSourceRange();
432 }
433}
434
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +0000435void Sema::checkReturnsCommand(const BlockCommandComment *Command) {
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000436 if (!Traits.getCommandInfo(Command->getCommandID())->IsReturnsCommand)
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +0000437 return;
438 if (isFunctionDecl()) {
439 if (ThisDeclInfo->ResultType->isVoidType()) {
440 unsigned DiagKind;
441 switch (ThisDeclInfo->ThisDecl->getKind()) {
442 default:
Dmitri Gribenko88815f32012-08-06 16:29:26 +0000443 if (ThisDeclInfo->IsObjCMethod)
444 DiagKind = 3;
445 else
446 DiagKind = 0;
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +0000447 break;
448 case Decl::CXXConstructor:
449 DiagKind = 1;
450 break;
451 case Decl::CXXDestructor:
452 DiagKind = 2;
453 break;
454 }
455 Diag(Command->getLocation(),
456 diag::warn_doc_returns_attached_to_a_void_function)
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000457 << Command->getCommandName(Traits)
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +0000458 << DiagKind
459 << Command->getSourceRange();
460 }
461 return;
462 }
463 Diag(Command->getLocation(),
464 diag::warn_doc_returns_not_attached_to_a_function_decl)
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000465 << Command->getCommandName(Traits)
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +0000466 << Command->getSourceRange();
467}
468
Dmitri Gribenko9443c572012-08-06 17:08:27 +0000469void Sema::checkBlockCommandDuplicate(const BlockCommandComment *Command) {
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000470 const CommandInfo *Info = Traits.getCommandInfo(Command->getCommandID());
Dmitri Gribenko9443c572012-08-06 17:08:27 +0000471 const BlockCommandComment *PrevCommand = NULL;
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000472 if (Info->IsBriefCommand) {
Dmitri Gribenko9443c572012-08-06 17:08:27 +0000473 if (!BriefCommand) {
474 BriefCommand = Command;
475 return;
476 }
477 PrevCommand = BriefCommand;
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000478 } else if (Info->IsReturnsCommand) {
Dmitri Gribenko9443c572012-08-06 17:08:27 +0000479 if (!ReturnsCommand) {
480 ReturnsCommand = Command;
481 return;
482 }
483 PrevCommand = ReturnsCommand;
484 } else {
485 // We don't want to check this command for duplicates.
486 return;
487 }
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000488 StringRef CommandName = Command->getCommandName(Traits);
489 StringRef PrevCommandName = PrevCommand->getCommandName(Traits);
Dmitri Gribenko9443c572012-08-06 17:08:27 +0000490 Diag(Command->getLocation(), diag::warn_doc_block_command_duplicate)
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000491 << CommandName
Dmitri Gribenko9443c572012-08-06 17:08:27 +0000492 << Command->getSourceRange();
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000493 if (CommandName == PrevCommandName)
Dmitri Gribenko9443c572012-08-06 17:08:27 +0000494 Diag(PrevCommand->getLocation(), diag::note_doc_block_command_previous)
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000495 << PrevCommandName
496 << PrevCommand->getSourceRange();
Dmitri Gribenko9443c572012-08-06 17:08:27 +0000497 else
498 Diag(PrevCommand->getLocation(),
499 diag::note_doc_block_command_previous_alias)
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000500 << PrevCommandName
501 << CommandName;
Dmitri Gribenko9443c572012-08-06 17:08:27 +0000502}
503
Dmitri Gribenko0bd98382012-09-22 21:47:50 +0000504void Sema::checkDeprecatedCommand(const BlockCommandComment *Command) {
505 if (!Traits.getCommandInfo(Command->getCommandID())->IsDeprecatedCommand)
506 return;
507
508 const Decl *D = ThisDeclInfo->ThisDecl;
509 if (!D)
510 return;
511
512 if (D->hasAttr<DeprecatedAttr>() ||
513 D->hasAttr<AvailabilityAttr>() ||
514 D->hasAttr<UnavailableAttr>())
515 return;
516
517 Diag(Command->getLocation(),
518 diag::warn_doc_deprecated_not_sync)
519 << Command->getSourceRange();
520
521 // Try to emit a fixit with a deprecation attribute.
522 if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
523 // Don't emit a Fix-It for non-member function definitions. GCC does not
524 // accept attributes on them.
525 const DeclContext *Ctx = FD->getDeclContext();
526 if ((!Ctx || !Ctx->isRecord()) &&
527 FD->doesThisDeclarationHaveABody())
528 return;
529
530 Diag(FD->getLocEnd(),
531 diag::note_add_deprecation_attr)
532 << FixItHint::CreateInsertion(FD->getLocEnd().getLocWithOffset(1),
533 " __attribute__((deprecated))");
534 }
535}
536
Dmitri Gribenko9edd2c82012-08-24 17:45:39 +0000537void Sema::resolveParamCommandIndexes(const FullComment *FC) {
538 if (!isFunctionDecl()) {
539 // We already warned that \\param commands are not attached to a function
540 // decl.
541 return;
542 }
543
544 llvm::SmallVector<ParamCommandComment *, 8> UnresolvedParamCommands;
545
546 // Comment AST nodes that correspond to \c ParamVars for which we have
547 // found a \\param command or NULL if no documentation was found so far.
548 llvm::SmallVector<ParamCommandComment *, 8> ParamVarDocs;
549
550 ArrayRef<const ParmVarDecl *> ParamVars = getParamVars();
551 ParamVarDocs.resize(ParamVars.size(), NULL);
552
553 // First pass over all \\param commands: resolve all parameter names.
554 for (Comment::child_iterator I = FC->child_begin(), E = FC->child_end();
555 I != E; ++I) {
556 ParamCommandComment *PCC = dyn_cast<ParamCommandComment>(*I);
557 if (!PCC || !PCC->hasParamName())
558 continue;
559 StringRef ParamName = PCC->getParamName();
560
561 // Check that referenced parameter name is in the function decl.
562 const unsigned ResolvedParamIndex = resolveParmVarReference(ParamName,
563 ParamVars);
564 if (ResolvedParamIndex == ParamCommandComment::InvalidParamIndex) {
565 UnresolvedParamCommands.push_back(PCC);
566 continue;
567 }
568 PCC->setParamIndex(ResolvedParamIndex);
569 if (ParamVarDocs[ResolvedParamIndex]) {
570 SourceRange ArgRange = PCC->getParamNameRange();
571 Diag(ArgRange.getBegin(), diag::warn_doc_param_duplicate)
572 << ParamName << ArgRange;
573 ParamCommandComment *PrevCommand = ParamVarDocs[ResolvedParamIndex];
574 Diag(PrevCommand->getLocation(), diag::note_doc_param_previous)
575 << PrevCommand->getParamNameRange();
576 }
577 ParamVarDocs[ResolvedParamIndex] = PCC;
578 }
579
580 // Find parameter declarations that have no corresponding \\param.
581 llvm::SmallVector<const ParmVarDecl *, 8> OrphanedParamDecls;
582 for (unsigned i = 0, e = ParamVarDocs.size(); i != e; ++i) {
583 if (!ParamVarDocs[i])
584 OrphanedParamDecls.push_back(ParamVars[i]);
585 }
586
587 // Second pass over unresolved \\param commands: do typo correction.
588 // Suggest corrections from a set of parameter declarations that have no
589 // corresponding \\param.
590 for (unsigned i = 0, e = UnresolvedParamCommands.size(); i != e; ++i) {
591 const ParamCommandComment *PCC = UnresolvedParamCommands[i];
592
593 SourceRange ArgRange = PCC->getParamNameRange();
594 StringRef ParamName = PCC->getParamName();
595 Diag(ArgRange.getBegin(), diag::warn_doc_param_not_found)
596 << ParamName << ArgRange;
597
598 // All parameters documented -- can't suggest a correction.
599 if (OrphanedParamDecls.size() == 0)
600 continue;
601
602 unsigned CorrectedParamIndex = ParamCommandComment::InvalidParamIndex;
603 if (OrphanedParamDecls.size() == 1) {
604 // If one parameter is not documented then that parameter is the only
605 // possible suggestion.
606 CorrectedParamIndex = 0;
607 } else {
608 // Do typo correction.
609 CorrectedParamIndex = correctTypoInParmVarReference(ParamName,
610 OrphanedParamDecls);
611 }
612 if (CorrectedParamIndex != ParamCommandComment::InvalidParamIndex) {
613 const ParmVarDecl *CorrectedPVD = OrphanedParamDecls[CorrectedParamIndex];
614 if (const IdentifierInfo *CorrectedII = CorrectedPVD->getIdentifier())
615 Diag(ArgRange.getBegin(), diag::note_doc_param_name_suggestion)
616 << CorrectedII->getName()
617 << FixItHint::CreateReplacement(ArgRange, CorrectedII->getName());
618 }
619 }
620}
621
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000622bool Sema::isFunctionDecl() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000623 if (!ThisDeclInfo)
624 return false;
625 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko00c59f72012-07-24 20:58:46 +0000626 inspectThisDecl();
Dmitri Gribenkoaf19a6a2012-08-02 21:45:39 +0000627 return ThisDeclInfo->getKind() == DeclInfo::FunctionKind;
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000628}
629
Dmitri Gribenko04bf29e2012-08-06 21:31:15 +0000630bool Sema::isTemplateOrSpecialization() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000631 if (!ThisDeclInfo)
632 return false;
633 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000634 inspectThisDecl();
Dmitri Gribenko04bf29e2012-08-06 21:31:15 +0000635 return ThisDeclInfo->getTemplateKind() != DeclInfo::NotTemplate;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000636}
637
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000638ArrayRef<const ParmVarDecl *> Sema::getParamVars() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000639 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko00c59f72012-07-24 20:58:46 +0000640 inspectThisDecl();
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000641 return ThisDeclInfo->ParamVars;
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000642}
643
644void Sema::inspectThisDecl() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000645 ThisDeclInfo->fill();
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000646}
647
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000648unsigned Sema::resolveParmVarReference(StringRef Name,
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000649 ArrayRef<const ParmVarDecl *> ParamVars) {
650 for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000651 const IdentifierInfo *II = ParamVars[i]->getIdentifier();
652 if (II && II->getName() == Name)
653 return i;
654 }
655 return ParamCommandComment::InvalidParamIndex;
656}
657
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000658namespace {
659class SimpleTypoCorrector {
660 StringRef Typo;
661 const unsigned MaxEditDistance;
662
663 const NamedDecl *BestDecl;
664 unsigned BestEditDistance;
665 unsigned BestIndex;
666 unsigned NextIndex;
667
668public:
669 SimpleTypoCorrector(StringRef Typo) :
670 Typo(Typo), MaxEditDistance((Typo.size() + 2) / 3),
671 BestDecl(NULL), BestEditDistance(MaxEditDistance + 1),
672 BestIndex(0), NextIndex(0)
673 { }
674
675 void addDecl(const NamedDecl *ND);
676
677 const NamedDecl *getBestDecl() const {
678 if (BestEditDistance > MaxEditDistance)
679 return NULL;
680
681 return BestDecl;
682 }
683
684 unsigned getBestDeclIndex() const {
685 assert(getBestDecl());
686 return BestIndex;
687 }
688};
689
690void SimpleTypoCorrector::addDecl(const NamedDecl *ND) {
691 unsigned CurrIndex = NextIndex++;
692
693 const IdentifierInfo *II = ND->getIdentifier();
694 if (!II)
695 return;
696
697 StringRef Name = II->getName();
698 unsigned MinPossibleEditDistance = abs((int)Name.size() - (int)Typo.size());
699 if (MinPossibleEditDistance > 0 &&
700 Typo.size() / MinPossibleEditDistance < 3)
701 return;
702
703 unsigned EditDistance = Typo.edit_distance(Name, true, MaxEditDistance);
704 if (EditDistance < BestEditDistance) {
705 BestEditDistance = EditDistance;
706 BestDecl = ND;
707 BestIndex = CurrIndex;
708 }
709}
710} // unnamed namespace
711
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000712unsigned Sema::correctTypoInParmVarReference(
713 StringRef Typo,
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000714 ArrayRef<const ParmVarDecl *> ParamVars) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000715 SimpleTypoCorrector Corrector(Typo);
716 for (unsigned i = 0, e = ParamVars.size(); i != e; ++i)
717 Corrector.addDecl(ParamVars[i]);
718 if (Corrector.getBestDecl())
719 return Corrector.getBestDeclIndex();
720 else
Dmitri Gribenko1ad23d62012-09-10 21:20:09 +0000721 return ParamCommandComment::InvalidParamIndex;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000722}
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000723
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000724namespace {
725bool ResolveTParamReferenceHelper(
726 StringRef Name,
727 const TemplateParameterList *TemplateParameters,
728 SmallVectorImpl<unsigned> *Position) {
729 for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
730 const NamedDecl *Param = TemplateParameters->getParam(i);
731 const IdentifierInfo *II = Param->getIdentifier();
732 if (II && II->getName() == Name) {
733 Position->push_back(i);
734 return true;
735 }
736
737 if (const TemplateTemplateParmDecl *TTP =
738 dyn_cast<TemplateTemplateParmDecl>(Param)) {
739 Position->push_back(i);
740 if (ResolveTParamReferenceHelper(Name, TTP->getTemplateParameters(),
741 Position))
742 return true;
743 Position->pop_back();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000744 }
745 }
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000746 return false;
747}
748} // unnamed namespace
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000749
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000750bool Sema::resolveTParamReference(
751 StringRef Name,
752 const TemplateParameterList *TemplateParameters,
753 SmallVectorImpl<unsigned> *Position) {
754 Position->clear();
755 if (!TemplateParameters)
756 return false;
757
758 return ResolveTParamReferenceHelper(Name, TemplateParameters, Position);
759}
760
761namespace {
762void CorrectTypoInTParamReferenceHelper(
763 const TemplateParameterList *TemplateParameters,
764 SimpleTypoCorrector &Corrector) {
765 for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
766 const NamedDecl *Param = TemplateParameters->getParam(i);
767 Corrector.addDecl(Param);
768
769 if (const TemplateTemplateParmDecl *TTP =
770 dyn_cast<TemplateTemplateParmDecl>(Param))
771 CorrectTypoInTParamReferenceHelper(TTP->getTemplateParameters(),
772 Corrector);
773 }
774}
775} // unnamed namespace
776
777StringRef Sema::correctTypoInTParamReference(
778 StringRef Typo,
779 const TemplateParameterList *TemplateParameters) {
780 SimpleTypoCorrector Corrector(Typo);
781 CorrectTypoInTParamReferenceHelper(TemplateParameters, Corrector);
782 if (const NamedDecl *ND = Corrector.getBestDecl()) {
783 const IdentifierInfo *II = ND->getIdentifier();
784 assert(II && "SimpleTypoCorrector should not return this decl");
785 return II->getName();
786 }
787 return StringRef();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000788}
789
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000790InlineCommandComment::RenderKind
791Sema::getInlineCommandRenderKind(StringRef Name) const {
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000792 assert(Traits.getCommandInfo(Name)->IsInlineCommand);
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000793
794 return llvm::StringSwitch<InlineCommandComment::RenderKind>(Name)
795 .Case("b", InlineCommandComment::RenderBold)
796 .Cases("c", "p", InlineCommandComment::RenderMonospaced)
797 .Cases("a", "e", "em", InlineCommandComment::RenderEmphasized)
798 .Default(InlineCommandComment::RenderNormal);
799}
800
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000801} // end namespace comments
802} // end namespace clang
803