blob: c39ee573ef6f57120b455afcb794f890604f777a [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 Gribenkoa5ef44f2012-07-11 21:38:39 +000021Sema::Sema(llvm::BumpPtrAllocator &Allocator, const SourceManager &SourceMgr,
Dmitri Gribenkoaa580812012-08-09 00:03:17 +000022 DiagnosticsEngine &Diags, const CommandTraits &Traits) :
23 Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags), Traits(Traits),
Dmitri Gribenko9443c572012-08-06 17:08:27 +000024 ThisDeclInfo(NULL), BriefCommand(NULL), ReturnsCommand(NULL) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000025}
26
27void Sema::setDecl(const Decl *D) {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +000028 if (!D)
29 return;
30
31 ThisDeclInfo = new (Allocator) DeclInfo;
32 ThisDeclInfo->ThisDecl = D;
Dmitri Gribenko651f8ce2012-08-01 23:21:57 +000033 ThisDeclInfo->IsFilled = false;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000034}
35
36ParagraphComment *Sema::actOnParagraphComment(
37 ArrayRef<InlineContentComment *> Content) {
38 return new (Allocator) ParagraphComment(Content);
39}
40
41BlockCommandComment *Sema::actOnBlockCommandStart(SourceLocation LocBegin,
42 SourceLocation LocEnd,
43 StringRef Name) {
44 return new (Allocator) BlockCommandComment(LocBegin, LocEnd, Name);
45}
46
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +000047void Sema::actOnBlockCommandArgs(BlockCommandComment *Command,
48 ArrayRef<BlockCommandComment::Argument> Args) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000049 Command->setArgs(Args);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000050}
51
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +000052void Sema::actOnBlockCommandFinish(BlockCommandComment *Command,
53 ParagraphComment *Paragraph) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000054 Command->setParagraph(Paragraph);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000055 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenko9443c572012-08-06 17:08:27 +000056 checkBlockCommandDuplicate(Command);
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +000057 checkReturnsCommand(Command);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000058}
59
60ParamCommandComment *Sema::actOnParamCommandStart(SourceLocation LocBegin,
61 SourceLocation LocEnd,
62 StringRef Name) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000063 ParamCommandComment *Command =
64 new (Allocator) ParamCommandComment(LocBegin, LocEnd, Name);
65
Dmitri Gribenko8487c522012-07-23 17:40:30 +000066 if (!isFunctionDecl())
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000067 Diag(Command->getLocation(),
68 diag::warn_doc_param_not_attached_to_a_function_decl)
69 << Command->getCommandNameRange();
70
71 return Command;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000072}
73
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +000074void Sema::actOnParamCommandDirectionArg(ParamCommandComment *Command,
75 SourceLocation ArgLocBegin,
76 SourceLocation ArgLocEnd,
77 StringRef Arg) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000078 ParamCommandComment::PassDirection Direction;
79 std::string ArgLower = Arg.lower();
80 // TODO: optimize: lower Name first (need an API in SmallString for that),
81 // after that StringSwitch.
82 if (ArgLower == "[in]")
83 Direction = ParamCommandComment::In;
84 else if (ArgLower == "[out]")
85 Direction = ParamCommandComment::Out;
86 else if (ArgLower == "[in,out]" || ArgLower == "[out,in]")
87 Direction = ParamCommandComment::InOut;
88 else {
89 // Remove spaces.
90 std::string::iterator O = ArgLower.begin();
91 for (std::string::iterator I = ArgLower.begin(), E = ArgLower.end();
92 I != E; ++I) {
93 const char C = *I;
94 if (C != ' ' && C != '\n' && C != '\r' &&
95 C != '\t' && C != '\v' && C != '\f')
96 *O++ = C;
97 }
98 ArgLower.resize(O - ArgLower.begin());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000099
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000100 bool RemovingWhitespaceHelped = false;
101 if (ArgLower == "[in]") {
102 Direction = ParamCommandComment::In;
103 RemovingWhitespaceHelped = true;
104 } else if (ArgLower == "[out]") {
105 Direction = ParamCommandComment::Out;
106 RemovingWhitespaceHelped = true;
107 } else if (ArgLower == "[in,out]" || ArgLower == "[out,in]") {
108 Direction = ParamCommandComment::InOut;
109 RemovingWhitespaceHelped = true;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000110 } else {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000111 Direction = ParamCommandComment::In;
112 RemovingWhitespaceHelped = false;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000113 }
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000114
115 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
116 if (RemovingWhitespaceHelped)
117 Diag(ArgLocBegin, diag::warn_doc_param_spaces_in_direction)
118 << ArgRange
119 << FixItHint::CreateReplacement(
120 ArgRange,
121 ParamCommandComment::getDirectionAsString(Direction));
122 else
123 Diag(ArgLocBegin, diag::warn_doc_param_invalid_direction)
124 << ArgRange;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000125 }
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000126 Command->setDirection(Direction, /* Explicit = */ true);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000127}
128
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000129void Sema::actOnParamCommandParamNameArg(ParamCommandComment *Command,
130 SourceLocation ArgLocBegin,
131 SourceLocation ArgLocEnd,
132 StringRef Arg) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000133 // Parser will not feed us more arguments than needed.
Dmitri Gribenko0eaf69d2012-07-13 19:02:42 +0000134 assert(Command->getNumArgs() == 0);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000135
136 if (!Command->isDirectionExplicit()) {
137 // User didn't provide a direction argument.
138 Command->setDirection(ParamCommandComment::In, /* Explicit = */ false);
139 }
140 typedef BlockCommandComment::Argument Argument;
141 Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
142 ArgLocEnd),
143 Arg);
144 Command->setArgs(llvm::makeArrayRef(A, 1));
145
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000146 if (!isFunctionDecl()) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000147 // We already warned that this \\param is not attached to a function decl.
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000148 return;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000149 }
150
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000151 ArrayRef<const ParmVarDecl *> ParamVars = getParamVars();
152
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000153 // Check that referenced parameter name is in the function decl.
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000154 const unsigned ResolvedParamIndex = resolveParmVarReference(Arg, ParamVars);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000155 if (ResolvedParamIndex != ParamCommandComment::InvalidParamIndex) {
156 Command->setParamIndex(ResolvedParamIndex);
Dmitri Gribenko65822772012-07-24 21:44:16 +0000157 if (ParamVarDocs[ResolvedParamIndex]) {
158 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
159 Diag(ArgLocBegin, diag::warn_doc_param_duplicate)
160 << Arg << ArgRange;
161 ParamCommandComment *PrevCommand = ParamVarDocs[ResolvedParamIndex];
162 Diag(PrevCommand->getLocation(), diag::note_doc_param_previous)
163 << PrevCommand->getParamNameRange();
164 }
165 ParamVarDocs[ResolvedParamIndex] = Command;
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000166 return;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000167 }
168
169 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
170 Diag(ArgLocBegin, diag::warn_doc_param_not_found)
171 << Arg << ArgRange;
172
Dmitri Gribenkobbb7af32012-07-27 21:34:43 +0000173 // No parameters -- can't suggest a correction.
174 if (ParamVars.size() == 0)
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000175 return;
Dmitri Gribenkobbb7af32012-07-27 21:34:43 +0000176
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000177 unsigned CorrectedParamIndex = ParamCommandComment::InvalidParamIndex;
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000178 if (ParamVars.size() == 1) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000179 // If function has only one parameter then only that parameter
180 // can be documented.
181 CorrectedParamIndex = 0;
182 } else {
183 // Do typo correction.
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000184 CorrectedParamIndex = correctTypoInParmVarReference(Arg, ParamVars);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000185 }
186 if (CorrectedParamIndex != ParamCommandComment::InvalidParamIndex) {
187 const ParmVarDecl *CorrectedPVD = ParamVars[CorrectedParamIndex];
188 if (const IdentifierInfo *CorrectedII = CorrectedPVD->getIdentifier())
189 Diag(ArgLocBegin, diag::note_doc_param_name_suggestion)
190 << CorrectedII->getName()
191 << FixItHint::CreateReplacement(ArgRange, CorrectedII->getName());
192 }
193
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000194 return;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000195}
196
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000197void Sema::actOnParamCommandFinish(ParamCommandComment *Command,
198 ParagraphComment *Paragraph) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000199 Command->setParagraph(Paragraph);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000200 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000201}
202
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000203TParamCommandComment *Sema::actOnTParamCommandStart(SourceLocation LocBegin,
204 SourceLocation LocEnd,
205 StringRef Name) {
206 TParamCommandComment *Command =
207 new (Allocator) TParamCommandComment(LocBegin, LocEnd, Name);
208
Dmitri Gribenko04bf29e2012-08-06 21:31:15 +0000209 if (!isTemplateOrSpecialization())
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000210 Diag(Command->getLocation(),
211 diag::warn_doc_tparam_not_attached_to_a_template_decl)
212 << Command->getCommandNameRange();
213
214 return Command;
215}
216
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000217void Sema::actOnTParamCommandParamNameArg(TParamCommandComment *Command,
218 SourceLocation ArgLocBegin,
219 SourceLocation ArgLocEnd,
220 StringRef Arg) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000221 // Parser will not feed us more arguments than needed.
222 assert(Command->getNumArgs() == 0);
223
224 typedef BlockCommandComment::Argument Argument;
225 Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
226 ArgLocEnd),
227 Arg);
228 Command->setArgs(llvm::makeArrayRef(A, 1));
229
Dmitri Gribenko04bf29e2012-08-06 21:31:15 +0000230 if (!isTemplateOrSpecialization()) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000231 // We already warned that this \\tparam is not attached to a template decl.
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000232 return;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000233 }
234
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000235 const TemplateParameterList *TemplateParameters =
236 ThisDeclInfo->TemplateParameters;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000237 SmallVector<unsigned, 2> Position;
238 if (resolveTParamReference(Arg, TemplateParameters, &Position)) {
239 Command->setPosition(copyArray(llvm::makeArrayRef(Position)));
240 llvm::StringMap<TParamCommandComment *>::iterator PrevCommandIt =
241 TemplateParameterDocs.find(Arg);
242 if (PrevCommandIt != TemplateParameterDocs.end()) {
243 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
244 Diag(ArgLocBegin, diag::warn_doc_tparam_duplicate)
245 << Arg << ArgRange;
246 TParamCommandComment *PrevCommand = PrevCommandIt->second;
247 Diag(PrevCommand->getLocation(), diag::note_doc_tparam_previous)
248 << PrevCommand->getParamNameRange();
249 }
250 TemplateParameterDocs[Arg] = Command;
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000251 return;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000252 }
253
254 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
255 Diag(ArgLocBegin, diag::warn_doc_tparam_not_found)
256 << Arg << ArgRange;
257
258 if (!TemplateParameters || TemplateParameters->size() == 0)
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000259 return;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000260
261 StringRef CorrectedName;
262 if (TemplateParameters->size() == 1) {
263 const NamedDecl *Param = TemplateParameters->getParam(0);
264 const IdentifierInfo *II = Param->getIdentifier();
265 if (II)
266 CorrectedName = II->getName();
267 } else {
268 CorrectedName = correctTypoInTParamReference(Arg, TemplateParameters);
269 }
270
271 if (!CorrectedName.empty()) {
272 Diag(ArgLocBegin, diag::note_doc_tparam_name_suggestion)
273 << CorrectedName
274 << FixItHint::CreateReplacement(ArgRange, CorrectedName);
275 }
276
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000277 return;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000278}
279
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000280void Sema::actOnTParamCommandFinish(TParamCommandComment *Command,
281 ParagraphComment *Paragraph) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000282 Command->setParagraph(Paragraph);
283 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000284}
285
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000286InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
287 SourceLocation CommandLocEnd,
288 StringRef CommandName) {
289 ArrayRef<InlineCommandComment::Argument> Args;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000290 return new (Allocator) InlineCommandComment(
291 CommandLocBegin,
292 CommandLocEnd,
293 CommandName,
294 getInlineCommandRenderKind(CommandName),
295 Args);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000296}
297
298InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
299 SourceLocation CommandLocEnd,
300 StringRef CommandName,
301 SourceLocation ArgLocBegin,
302 SourceLocation ArgLocEnd,
303 StringRef Arg) {
304 typedef InlineCommandComment::Argument Argument;
305 Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
306 ArgLocEnd),
307 Arg);
308
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000309 return new (Allocator) InlineCommandComment(
310 CommandLocBegin,
311 CommandLocEnd,
312 CommandName,
313 getInlineCommandRenderKind(CommandName),
314 llvm::makeArrayRef(A, 1));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000315}
316
317InlineContentComment *Sema::actOnUnknownCommand(SourceLocation LocBegin,
318 SourceLocation LocEnd,
319 StringRef Name) {
320 ArrayRef<InlineCommandComment::Argument> Args;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000321 return new (Allocator) InlineCommandComment(
322 LocBegin, LocEnd, Name,
323 InlineCommandComment::RenderNormal,
324 Args);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000325}
326
327TextComment *Sema::actOnText(SourceLocation LocBegin,
328 SourceLocation LocEnd,
329 StringRef Text) {
330 return new (Allocator) TextComment(LocBegin, LocEnd, Text);
331}
332
333VerbatimBlockComment *Sema::actOnVerbatimBlockStart(SourceLocation Loc,
334 StringRef Name) {
335 return new (Allocator) VerbatimBlockComment(
336 Loc,
337 Loc.getLocWithOffset(1 + Name.size()),
338 Name);
339}
340
341VerbatimBlockLineComment *Sema::actOnVerbatimBlockLine(SourceLocation Loc,
342 StringRef Text) {
343 return new (Allocator) VerbatimBlockLineComment(Loc, Text);
344}
345
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000346void Sema::actOnVerbatimBlockFinish(
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000347 VerbatimBlockComment *Block,
348 SourceLocation CloseNameLocBegin,
349 StringRef CloseName,
350 ArrayRef<VerbatimBlockLineComment *> Lines) {
351 Block->setCloseName(CloseName, CloseNameLocBegin);
352 Block->setLines(Lines);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000353}
354
355VerbatimLineComment *Sema::actOnVerbatimLine(SourceLocation LocBegin,
356 StringRef Name,
357 SourceLocation TextBegin,
358 StringRef Text) {
359 return new (Allocator) VerbatimLineComment(
360 LocBegin,
361 TextBegin.getLocWithOffset(Text.size()),
362 Name,
363 TextBegin,
364 Text);
365}
366
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000367HTMLStartTagComment *Sema::actOnHTMLStartTagStart(SourceLocation LocBegin,
368 StringRef TagName) {
369 return new (Allocator) HTMLStartTagComment(LocBegin, TagName);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000370}
371
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000372void Sema::actOnHTMLStartTagFinish(
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000373 HTMLStartTagComment *Tag,
374 ArrayRef<HTMLStartTagComment::Attribute> Attrs,
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000375 SourceLocation GreaterLoc,
376 bool IsSelfClosing) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000377 Tag->setAttrs(Attrs);
378 Tag->setGreaterLoc(GreaterLoc);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000379 if (IsSelfClosing)
380 Tag->setSelfClosing();
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000381 else if (!isHTMLEndTagForbidden(Tag->getTagName()))
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000382 HTMLOpenTags.push_back(Tag);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000383}
384
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000385HTMLEndTagComment *Sema::actOnHTMLEndTag(SourceLocation LocBegin,
386 SourceLocation LocEnd,
387 StringRef TagName) {
388 HTMLEndTagComment *HET =
389 new (Allocator) HTMLEndTagComment(LocBegin, LocEnd, TagName);
390 if (isHTMLEndTagForbidden(TagName)) {
391 Diag(HET->getLocation(), diag::warn_doc_html_end_forbidden)
392 << TagName << HET->getSourceRange();
393 return HET;
Dmitri Gribenko3d986982012-07-12 23:37:09 +0000394 }
395
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000396 bool FoundOpen = false;
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000397 for (SmallVectorImpl<HTMLStartTagComment *>::const_reverse_iterator
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000398 I = HTMLOpenTags.rbegin(), E = HTMLOpenTags.rend();
399 I != E; ++I) {
400 if ((*I)->getTagName() == TagName) {
401 FoundOpen = true;
402 break;
403 }
404 }
405 if (!FoundOpen) {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000406 Diag(HET->getLocation(), diag::warn_doc_html_end_unbalanced)
407 << HET->getSourceRange();
408 return HET;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000409 }
410
411 while (!HTMLOpenTags.empty()) {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000412 const HTMLStartTagComment *HST = HTMLOpenTags.back();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000413 HTMLOpenTags.pop_back();
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000414 StringRef LastNotClosedTagName = HST->getTagName();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000415 if (LastNotClosedTagName == TagName)
416 break;
417
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000418 if (isHTMLEndTagOptional(LastNotClosedTagName))
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000419 continue;
420
421 bool OpenLineInvalid;
422 const unsigned OpenLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000423 HST->getLocation(),
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000424 &OpenLineInvalid);
425 bool CloseLineInvalid;
426 const unsigned CloseLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000427 HET->getLocation(),
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000428 &CloseLineInvalid);
429
430 if (OpenLineInvalid || CloseLineInvalid || OpenLine == CloseLine)
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000431 Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
432 << HST->getTagName() << HET->getTagName()
433 << HST->getSourceRange() << HET->getSourceRange();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000434 else {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000435 Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
436 << HST->getTagName() << HET->getTagName()
437 << HST->getSourceRange();
438 Diag(HET->getLocation(), diag::note_doc_html_end_tag)
439 << HET->getSourceRange();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000440 }
441 }
442
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000443 return HET;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000444}
445
446FullComment *Sema::actOnFullComment(
447 ArrayRef<BlockContentComment *> Blocks) {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000448 return new (Allocator) FullComment(Blocks, ThisDeclInfo);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000449}
450
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000451void Sema::checkBlockCommandEmptyParagraph(BlockCommandComment *Command) {
452 ParagraphComment *Paragraph = Command->getParagraph();
453 if (Paragraph->isWhitespace()) {
454 SourceLocation DiagLoc;
Dmitri Gribenko0eaf69d2012-07-13 19:02:42 +0000455 if (Command->getNumArgs() > 0)
456 DiagLoc = Command->getArgRange(Command->getNumArgs() - 1).getEnd();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000457 if (!DiagLoc.isValid())
458 DiagLoc = Command->getCommandNameRange().getEnd();
459 Diag(DiagLoc, diag::warn_doc_block_command_empty_paragraph)
460 << Command->getCommandName()
461 << Command->getSourceRange();
462 }
463}
464
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +0000465void Sema::checkReturnsCommand(const BlockCommandComment *Command) {
Dmitri Gribenkoaa580812012-08-09 00:03:17 +0000466 if (!Traits.isReturnsCommand(Command->getCommandName()))
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +0000467 return;
468 if (isFunctionDecl()) {
469 if (ThisDeclInfo->ResultType->isVoidType()) {
470 unsigned DiagKind;
471 switch (ThisDeclInfo->ThisDecl->getKind()) {
472 default:
Dmitri Gribenko88815f32012-08-06 16:29:26 +0000473 if (ThisDeclInfo->IsObjCMethod)
474 DiagKind = 3;
475 else
476 DiagKind = 0;
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +0000477 break;
478 case Decl::CXXConstructor:
479 DiagKind = 1;
480 break;
481 case Decl::CXXDestructor:
482 DiagKind = 2;
483 break;
484 }
485 Diag(Command->getLocation(),
486 diag::warn_doc_returns_attached_to_a_void_function)
487 << Command->getCommandName()
488 << DiagKind
489 << Command->getSourceRange();
490 }
491 return;
492 }
493 Diag(Command->getLocation(),
494 diag::warn_doc_returns_not_attached_to_a_function_decl)
495 << Command->getCommandName()
496 << Command->getSourceRange();
497}
498
Dmitri Gribenko9443c572012-08-06 17:08:27 +0000499void Sema::checkBlockCommandDuplicate(const BlockCommandComment *Command) {
500 StringRef Name = Command->getCommandName();
501 const BlockCommandComment *PrevCommand = NULL;
Dmitri Gribenkoaa580812012-08-09 00:03:17 +0000502 if (Traits.isBriefCommand(Name)) {
Dmitri Gribenko9443c572012-08-06 17:08:27 +0000503 if (!BriefCommand) {
504 BriefCommand = Command;
505 return;
506 }
507 PrevCommand = BriefCommand;
Dmitri Gribenkoaa580812012-08-09 00:03:17 +0000508 } else if (Traits.isReturnsCommand(Name)) {
Dmitri Gribenko9443c572012-08-06 17:08:27 +0000509 if (!ReturnsCommand) {
510 ReturnsCommand = Command;
511 return;
512 }
513 PrevCommand = ReturnsCommand;
514 } else {
515 // We don't want to check this command for duplicates.
516 return;
517 }
518 Diag(Command->getLocation(), diag::warn_doc_block_command_duplicate)
519 << Name
520 << Command->getSourceRange();
521 if (Name == PrevCommand->getCommandName())
522 Diag(PrevCommand->getLocation(), diag::note_doc_block_command_previous)
523 << PrevCommand->getCommandName()
524 << Command->getSourceRange();
525 else
526 Diag(PrevCommand->getLocation(),
527 diag::note_doc_block_command_previous_alias)
528 << PrevCommand->getCommandName()
529 << Name;
530}
531
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000532bool Sema::isFunctionDecl() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000533 if (!ThisDeclInfo)
534 return false;
535 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko00c59f72012-07-24 20:58:46 +0000536 inspectThisDecl();
Dmitri Gribenkoaf19a6a2012-08-02 21:45:39 +0000537 return ThisDeclInfo->getKind() == DeclInfo::FunctionKind;
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000538}
539
Dmitri Gribenko04bf29e2012-08-06 21:31:15 +0000540bool Sema::isTemplateOrSpecialization() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000541 if (!ThisDeclInfo)
542 return false;
543 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000544 inspectThisDecl();
Dmitri Gribenko04bf29e2012-08-06 21:31:15 +0000545 return ThisDeclInfo->getTemplateKind() != DeclInfo::NotTemplate;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000546}
547
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000548ArrayRef<const ParmVarDecl *> Sema::getParamVars() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000549 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko00c59f72012-07-24 20:58:46 +0000550 inspectThisDecl();
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000551 return ThisDeclInfo->ParamVars;
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000552}
553
554void Sema::inspectThisDecl() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000555 ThisDeclInfo->fill();
556 ParamVarDocs.resize(ThisDeclInfo->ParamVars.size(), NULL);
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000557}
558
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000559unsigned Sema::resolveParmVarReference(StringRef Name,
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000560 ArrayRef<const ParmVarDecl *> ParamVars) {
561 for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000562 const IdentifierInfo *II = ParamVars[i]->getIdentifier();
563 if (II && II->getName() == Name)
564 return i;
565 }
566 return ParamCommandComment::InvalidParamIndex;
567}
568
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000569namespace {
570class SimpleTypoCorrector {
571 StringRef Typo;
572 const unsigned MaxEditDistance;
573
574 const NamedDecl *BestDecl;
575 unsigned BestEditDistance;
576 unsigned BestIndex;
577 unsigned NextIndex;
578
579public:
580 SimpleTypoCorrector(StringRef Typo) :
581 Typo(Typo), MaxEditDistance((Typo.size() + 2) / 3),
582 BestDecl(NULL), BestEditDistance(MaxEditDistance + 1),
583 BestIndex(0), NextIndex(0)
584 { }
585
586 void addDecl(const NamedDecl *ND);
587
588 const NamedDecl *getBestDecl() const {
589 if (BestEditDistance > MaxEditDistance)
590 return NULL;
591
592 return BestDecl;
593 }
594
595 unsigned getBestDeclIndex() const {
596 assert(getBestDecl());
597 return BestIndex;
598 }
599};
600
601void SimpleTypoCorrector::addDecl(const NamedDecl *ND) {
602 unsigned CurrIndex = NextIndex++;
603
604 const IdentifierInfo *II = ND->getIdentifier();
605 if (!II)
606 return;
607
608 StringRef Name = II->getName();
609 unsigned MinPossibleEditDistance = abs((int)Name.size() - (int)Typo.size());
610 if (MinPossibleEditDistance > 0 &&
611 Typo.size() / MinPossibleEditDistance < 3)
612 return;
613
614 unsigned EditDistance = Typo.edit_distance(Name, true, MaxEditDistance);
615 if (EditDistance < BestEditDistance) {
616 BestEditDistance = EditDistance;
617 BestDecl = ND;
618 BestIndex = CurrIndex;
619 }
620}
621} // unnamed namespace
622
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000623unsigned Sema::correctTypoInParmVarReference(
624 StringRef Typo,
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000625 ArrayRef<const ParmVarDecl *> ParamVars) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000626 SimpleTypoCorrector Corrector(Typo);
627 for (unsigned i = 0, e = ParamVars.size(); i != e; ++i)
628 Corrector.addDecl(ParamVars[i]);
629 if (Corrector.getBestDecl())
630 return Corrector.getBestDeclIndex();
631 else
632 return ParamCommandComment::InvalidParamIndex;;
633}
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000634
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000635namespace {
636bool ResolveTParamReferenceHelper(
637 StringRef Name,
638 const TemplateParameterList *TemplateParameters,
639 SmallVectorImpl<unsigned> *Position) {
640 for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
641 const NamedDecl *Param = TemplateParameters->getParam(i);
642 const IdentifierInfo *II = Param->getIdentifier();
643 if (II && II->getName() == Name) {
644 Position->push_back(i);
645 return true;
646 }
647
648 if (const TemplateTemplateParmDecl *TTP =
649 dyn_cast<TemplateTemplateParmDecl>(Param)) {
650 Position->push_back(i);
651 if (ResolveTParamReferenceHelper(Name, TTP->getTemplateParameters(),
652 Position))
653 return true;
654 Position->pop_back();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000655 }
656 }
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000657 return false;
658}
659} // unnamed namespace
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000660
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000661bool Sema::resolveTParamReference(
662 StringRef Name,
663 const TemplateParameterList *TemplateParameters,
664 SmallVectorImpl<unsigned> *Position) {
665 Position->clear();
666 if (!TemplateParameters)
667 return false;
668
669 return ResolveTParamReferenceHelper(Name, TemplateParameters, Position);
670}
671
672namespace {
673void CorrectTypoInTParamReferenceHelper(
674 const TemplateParameterList *TemplateParameters,
675 SimpleTypoCorrector &Corrector) {
676 for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
677 const NamedDecl *Param = TemplateParameters->getParam(i);
678 Corrector.addDecl(Param);
679
680 if (const TemplateTemplateParmDecl *TTP =
681 dyn_cast<TemplateTemplateParmDecl>(Param))
682 CorrectTypoInTParamReferenceHelper(TTP->getTemplateParameters(),
683 Corrector);
684 }
685}
686} // unnamed namespace
687
688StringRef Sema::correctTypoInTParamReference(
689 StringRef Typo,
690 const TemplateParameterList *TemplateParameters) {
691 SimpleTypoCorrector Corrector(Typo);
692 CorrectTypoInTParamReferenceHelper(TemplateParameters, Corrector);
693 if (const NamedDecl *ND = Corrector.getBestDecl()) {
694 const IdentifierInfo *II = ND->getIdentifier();
695 assert(II && "SimpleTypoCorrector should not return this decl");
696 return II->getName();
697 }
698 return StringRef();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000699}
700
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000701InlineCommandComment::RenderKind
702Sema::getInlineCommandRenderKind(StringRef Name) const {
Dmitri Gribenkoaa580812012-08-09 00:03:17 +0000703 assert(Traits.isInlineCommand(Name));
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000704
705 return llvm::StringSwitch<InlineCommandComment::RenderKind>(Name)
706 .Case("b", InlineCommandComment::RenderBold)
707 .Cases("c", "p", InlineCommandComment::RenderMonospaced)
708 .Cases("a", "e", "em", InlineCommandComment::RenderEmphasized)
709 .Default(InlineCommandComment::RenderNormal);
710}
711
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000712bool Sema::isHTMLEndTagOptional(StringRef Name) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000713 return llvm::StringSwitch<bool>(Name)
Dmitri Gribenko3d986982012-07-12 23:37:09 +0000714 .Case("p", true)
715 .Case("li", true)
716 .Case("dt", true)
717 .Case("dd", true)
718 .Case("tr", true)
719 .Case("th", true)
720 .Case("td", true)
721 .Case("thead", true)
722 .Case("tfoot", true)
723 .Case("tbody", true)
724 .Case("colgroup", true)
725 .Default(false);
726}
727
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000728bool Sema::isHTMLEndTagForbidden(StringRef Name) {
Dmitri Gribenko3d986982012-07-12 23:37:09 +0000729 return llvm::StringSwitch<bool>(Name)
730 .Case("br", true)
731 .Case("hr", true)
732 .Case("img", true)
733 .Case("col", true)
734 .Default(false);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000735}
736
737} // end namespace comments
738} // end namespace clang
739