blob: c39ee573ef6f57120b455afcb794f890604f777a [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"
Dmitri Gribenkoca7f80a2012-08-09 00:03:17 +000012#include "clang/AST/CommentCommandTraits.h"
Dmitri Gribenkof26054f2012-07-11 21:38:39 +000013#include "clang/AST/Decl.h"
Dmitri Gribenko34df2202012-07-31 22:37:06 +000014#include "clang/AST/DeclTemplate.h"
Dmitri Gribenkof26054f2012-07-11 21:38:39 +000015#include "clang/Basic/SourceManager.h"
Dmitri Gribenkoec925312012-07-06 00:28:32 +000016#include "llvm/ADT/StringSwitch.h"
17
18namespace clang {
19namespace comments {
20
Dmitri Gribenkof26054f2012-07-11 21:38:39 +000021Sema::Sema(llvm::BumpPtrAllocator &Allocator, const SourceManager &SourceMgr,
Dmitri Gribenkoca7f80a2012-08-09 00:03:17 +000022 DiagnosticsEngine &Diags, const CommandTraits &Traits) :
23 Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags), Traits(Traits),
Dmitri Gribenko5ec0c752012-08-06 17:08:27 +000024 ThisDeclInfo(NULL), BriefCommand(NULL), ReturnsCommand(NULL) {
Dmitri Gribenkof26054f2012-07-11 21:38:39 +000025}
26
27void Sema::setDecl(const Decl *D) {
Dmitri Gribenko527ab212012-08-01 23:08:09 +000028 if (!D)
29 return;
30
31 ThisDeclInfo = new (Allocator) DeclInfo;
32 ThisDeclInfo->ThisDecl = D;
Dmitri Gribenkoe6213dd2012-08-01 23:21:57 +000033 ThisDeclInfo->IsFilled = false;
Dmitri Gribenkoec925312012-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 Gribenkoa9770ad2012-08-06 19:03:12 +000047void Sema::actOnBlockCommandArgs(BlockCommandComment *Command,
48 ArrayRef<BlockCommandComment::Argument> Args) {
Dmitri Gribenkoec925312012-07-06 00:28:32 +000049 Command->setArgs(Args);
Dmitri Gribenkoec925312012-07-06 00:28:32 +000050}
51
Dmitri Gribenkoa9770ad2012-08-06 19:03:12 +000052void Sema::actOnBlockCommandFinish(BlockCommandComment *Command,
53 ParagraphComment *Paragraph) {
Dmitri Gribenkoec925312012-07-06 00:28:32 +000054 Command->setParagraph(Paragraph);
Dmitri Gribenkof26054f2012-07-11 21:38:39 +000055 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenko5ec0c752012-08-06 17:08:27 +000056 checkBlockCommandDuplicate(Command);
Dmitri Gribenko64305832012-08-03 21:15:32 +000057 checkReturnsCommand(Command);
Dmitri Gribenkoec925312012-07-06 00:28:32 +000058}
59
60ParamCommandComment *Sema::actOnParamCommandStart(SourceLocation LocBegin,
61 SourceLocation LocEnd,
62 StringRef Name) {
Dmitri Gribenkof26054f2012-07-11 21:38:39 +000063 ParamCommandComment *Command =
64 new (Allocator) ParamCommandComment(LocBegin, LocEnd, Name);
65
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +000066 if (!isFunctionDecl())
Dmitri Gribenkof26054f2012-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 Gribenkoec925312012-07-06 00:28:32 +000072}
73
Dmitri Gribenkoa9770ad2012-08-06 19:03:12 +000074void Sema::actOnParamCommandDirectionArg(ParamCommandComment *Command,
75 SourceLocation ArgLocBegin,
76 SourceLocation ArgLocEnd,
77 StringRef Arg) {
Dmitri Gribenkof26054f2012-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 Gribenkoec925312012-07-06 00:28:32 +000099
Dmitri Gribenkof26054f2012-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 Gribenkoec925312012-07-06 00:28:32 +0000110 } else {
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000111 Direction = ParamCommandComment::In;
112 RemovingWhitespaceHelped = false;
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000113 }
Dmitri Gribenkof26054f2012-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 Gribenkoec925312012-07-06 00:28:32 +0000125 }
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000126 Command->setDirection(Direction, /* Explicit = */ true);
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000127}
128
Dmitri Gribenkoa9770ad2012-08-06 19:03:12 +0000129void Sema::actOnParamCommandParamNameArg(ParamCommandComment *Command,
130 SourceLocation ArgLocBegin,
131 SourceLocation ArgLocEnd,
132 StringRef Arg) {
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000133 // Parser will not feed us more arguments than needed.
Dmitri Gribenko619e75e2012-07-13 19:02:42 +0000134 assert(Command->getNumArgs() == 0);
Dmitri Gribenkof26054f2012-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 Gribenko4b7f5fe2012-07-23 17:40:30 +0000146 if (!isFunctionDecl()) {
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000147 // We already warned that this \\param is not attached to a function decl.
Dmitri Gribenkoa9770ad2012-08-06 19:03:12 +0000148 return;
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000149 }
150
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000151 ArrayRef<const ParmVarDecl *> ParamVars = getParamVars();
152
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000153 // Check that referenced parameter name is in the function decl.
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000154 const unsigned ResolvedParamIndex = resolveParmVarReference(Arg, ParamVars);
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000155 if (ResolvedParamIndex != ParamCommandComment::InvalidParamIndex) {
156 Command->setParamIndex(ResolvedParamIndex);
Dmitri Gribenko8f0f1b02012-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 Gribenkoa9770ad2012-08-06 19:03:12 +0000166 return;
Dmitri Gribenkof26054f2012-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 Gribenkof24990a2012-07-27 21:34:43 +0000173 // No parameters -- can't suggest a correction.
174 if (ParamVars.size() == 0)
Dmitri Gribenkoa9770ad2012-08-06 19:03:12 +0000175 return;
Dmitri Gribenkof24990a2012-07-27 21:34:43 +0000176
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000177 unsigned CorrectedParamIndex = ParamCommandComment::InvalidParamIndex;
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000178 if (ParamVars.size() == 1) {
Dmitri Gribenkof26054f2012-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 Gribenko4b7f5fe2012-07-23 17:40:30 +0000184 CorrectedParamIndex = correctTypoInParmVarReference(Arg, ParamVars);
Dmitri Gribenkof26054f2012-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 Gribenkoa9770ad2012-08-06 19:03:12 +0000194 return;
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000195}
196
Dmitri Gribenkoa9770ad2012-08-06 19:03:12 +0000197void Sema::actOnParamCommandFinish(ParamCommandComment *Command,
198 ParagraphComment *Paragraph) {
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000199 Command->setParagraph(Paragraph);
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000200 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000201}
202
Dmitri Gribenko34df2202012-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 Gribenko8e5d5f12012-08-06 21:31:15 +0000209 if (!isTemplateOrSpecialization())
Dmitri Gribenko34df2202012-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 Gribenkoa9770ad2012-08-06 19:03:12 +0000217void Sema::actOnTParamCommandParamNameArg(TParamCommandComment *Command,
218 SourceLocation ArgLocBegin,
219 SourceLocation ArgLocEnd,
220 StringRef Arg) {
Dmitri Gribenko34df2202012-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 Gribenko8e5d5f12012-08-06 21:31:15 +0000230 if (!isTemplateOrSpecialization()) {
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000231 // We already warned that this \\tparam is not attached to a template decl.
Dmitri Gribenkoa9770ad2012-08-06 19:03:12 +0000232 return;
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000233 }
234
Dmitri Gribenko527ab212012-08-01 23:08:09 +0000235 const TemplateParameterList *TemplateParameters =
236 ThisDeclInfo->TemplateParameters;
Dmitri Gribenko34df2202012-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 Gribenkoa9770ad2012-08-06 19:03:12 +0000251 return;
Dmitri Gribenko34df2202012-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 Gribenkoa9770ad2012-08-06 19:03:12 +0000259 return;
Dmitri Gribenko34df2202012-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 Gribenkoa9770ad2012-08-06 19:03:12 +0000277 return;
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000278}
279
Dmitri Gribenkoa9770ad2012-08-06 19:03:12 +0000280void Sema::actOnTParamCommandFinish(TParamCommandComment *Command,
281 ParagraphComment *Paragraph) {
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000282 Command->setParagraph(Paragraph);
283 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000284}
285
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000286InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
287 SourceLocation CommandLocEnd,
288 StringRef CommandName) {
289 ArrayRef<InlineCommandComment::Argument> Args;
Dmitri Gribenkod73e4ce2012-07-23 16:43:01 +0000290 return new (Allocator) InlineCommandComment(
291 CommandLocBegin,
292 CommandLocEnd,
293 CommandName,
294 getInlineCommandRenderKind(CommandName),
295 Args);
Dmitri Gribenkoec925312012-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 Gribenkod73e4ce2012-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 Gribenkoec925312012-07-06 00:28:32 +0000315}
316
317InlineContentComment *Sema::actOnUnknownCommand(SourceLocation LocBegin,
318 SourceLocation LocEnd,
319 StringRef Name) {
320 ArrayRef<InlineCommandComment::Argument> Args;
Dmitri Gribenkod73e4ce2012-07-23 16:43:01 +0000321 return new (Allocator) InlineCommandComment(
322 LocBegin, LocEnd, Name,
323 InlineCommandComment::RenderNormal,
324 Args);
Dmitri Gribenkoec925312012-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 Gribenkoa9770ad2012-08-06 19:03:12 +0000346void Sema::actOnVerbatimBlockFinish(
Dmitri Gribenkoec925312012-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 Gribenkoec925312012-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 Gribenkoe00ffc72012-07-13 00:44:24 +0000367HTMLStartTagComment *Sema::actOnHTMLStartTagStart(SourceLocation LocBegin,
368 StringRef TagName) {
369 return new (Allocator) HTMLStartTagComment(LocBegin, TagName);
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000370}
371
Dmitri Gribenkoa9770ad2012-08-06 19:03:12 +0000372void Sema::actOnHTMLStartTagFinish(
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000373 HTMLStartTagComment *Tag,
374 ArrayRef<HTMLStartTagComment::Attribute> Attrs,
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000375 SourceLocation GreaterLoc,
376 bool IsSelfClosing) {
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000377 Tag->setAttrs(Attrs);
378 Tag->setGreaterLoc(GreaterLoc);
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000379 if (IsSelfClosing)
380 Tag->setSelfClosing();
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000381 else if (!isHTMLEndTagForbidden(Tag->getTagName()))
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000382 HTMLOpenTags.push_back(Tag);
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000383}
384
Dmitri Gribenkoe00ffc72012-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 Gribenko9460fbf2012-07-12 23:37:09 +0000394 }
395
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000396 bool FoundOpen = false;
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000397 for (SmallVectorImpl<HTMLStartTagComment *>::const_reverse_iterator
Dmitri Gribenkof26054f2012-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 Gribenkoe00ffc72012-07-13 00:44:24 +0000406 Diag(HET->getLocation(), diag::warn_doc_html_end_unbalanced)
407 << HET->getSourceRange();
408 return HET;
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000409 }
410
411 while (!HTMLOpenTags.empty()) {
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000412 const HTMLStartTagComment *HST = HTMLOpenTags.back();
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000413 HTMLOpenTags.pop_back();
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000414 StringRef LastNotClosedTagName = HST->getTagName();
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000415 if (LastNotClosedTagName == TagName)
416 break;
417
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000418 if (isHTMLEndTagOptional(LastNotClosedTagName))
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000419 continue;
420
421 bool OpenLineInvalid;
422 const unsigned OpenLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000423 HST->getLocation(),
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000424 &OpenLineInvalid);
425 bool CloseLineInvalid;
426 const unsigned CloseLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000427 HET->getLocation(),
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000428 &CloseLineInvalid);
429
430 if (OpenLineInvalid || CloseLineInvalid || OpenLine == CloseLine)
Dmitri Gribenkoe00ffc72012-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 Gribenkof26054f2012-07-11 21:38:39 +0000434 else {
Dmitri Gribenkoe00ffc72012-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 Gribenkof26054f2012-07-11 21:38:39 +0000440 }
441 }
442
Dmitri Gribenkoe00ffc72012-07-13 00:44:24 +0000443 return HET;
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000444}
445
446FullComment *Sema::actOnFullComment(
447 ArrayRef<BlockContentComment *> Blocks) {
Dmitri Gribenko527ab212012-08-01 23:08:09 +0000448 return new (Allocator) FullComment(Blocks, ThisDeclInfo);
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000449}
450
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000451void Sema::checkBlockCommandEmptyParagraph(BlockCommandComment *Command) {
452 ParagraphComment *Paragraph = Command->getParagraph();
453 if (Paragraph->isWhitespace()) {
454 SourceLocation DiagLoc;
Dmitri Gribenko619e75e2012-07-13 19:02:42 +0000455 if (Command->getNumArgs() > 0)
456 DiagLoc = Command->getArgRange(Command->getNumArgs() - 1).getEnd();
Dmitri Gribenkof26054f2012-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 Gribenko64305832012-08-03 21:15:32 +0000465void Sema::checkReturnsCommand(const BlockCommandComment *Command) {
Dmitri Gribenkoca7f80a2012-08-09 00:03:17 +0000466 if (!Traits.isReturnsCommand(Command->getCommandName()))
Dmitri Gribenko64305832012-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 Gribenko558babc2012-08-06 16:29:26 +0000473 if (ThisDeclInfo->IsObjCMethod)
474 DiagKind = 3;
475 else
476 DiagKind = 0;
Dmitri Gribenko64305832012-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 Gribenko5ec0c752012-08-06 17:08:27 +0000499void Sema::checkBlockCommandDuplicate(const BlockCommandComment *Command) {
500 StringRef Name = Command->getCommandName();
501 const BlockCommandComment *PrevCommand = NULL;
Dmitri Gribenkoca7f80a2012-08-09 00:03:17 +0000502 if (Traits.isBriefCommand(Name)) {
Dmitri Gribenko5ec0c752012-08-06 17:08:27 +0000503 if (!BriefCommand) {
504 BriefCommand = Command;
505 return;
506 }
507 PrevCommand = BriefCommand;
Dmitri Gribenkoca7f80a2012-08-09 00:03:17 +0000508 } else if (Traits.isReturnsCommand(Name)) {
Dmitri Gribenko5ec0c752012-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 Gribenko4b7f5fe2012-07-23 17:40:30 +0000532bool Sema::isFunctionDecl() {
Dmitri Gribenko527ab212012-08-01 23:08:09 +0000533 if (!ThisDeclInfo)
534 return false;
535 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko52cb2182012-07-24 20:58:46 +0000536 inspectThisDecl();
Dmitri Gribenko37a7faf2012-08-02 21:45:39 +0000537 return ThisDeclInfo->getKind() == DeclInfo::FunctionKind;
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000538}
539
Dmitri Gribenko8e5d5f12012-08-06 21:31:15 +0000540bool Sema::isTemplateOrSpecialization() {
Dmitri Gribenko527ab212012-08-01 23:08:09 +0000541 if (!ThisDeclInfo)
542 return false;
543 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000544 inspectThisDecl();
Dmitri Gribenko8e5d5f12012-08-06 21:31:15 +0000545 return ThisDeclInfo->getTemplateKind() != DeclInfo::NotTemplate;
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000546}
547
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000548ArrayRef<const ParmVarDecl *> Sema::getParamVars() {
Dmitri Gribenko527ab212012-08-01 23:08:09 +0000549 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko52cb2182012-07-24 20:58:46 +0000550 inspectThisDecl();
Dmitri Gribenko527ab212012-08-01 23:08:09 +0000551 return ThisDeclInfo->ParamVars;
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000552}
553
554void Sema::inspectThisDecl() {
Dmitri Gribenko527ab212012-08-01 23:08:09 +0000555 ThisDeclInfo->fill();
556 ParamVarDocs.resize(ThisDeclInfo->ParamVars.size(), NULL);
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000557}
558
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000559unsigned Sema::resolveParmVarReference(StringRef Name,
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000560 ArrayRef<const ParmVarDecl *> ParamVars) {
561 for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) {
Dmitri Gribenkof26054f2012-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 Gribenko34df2202012-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 Gribenkof26054f2012-07-11 21:38:39 +0000623unsigned Sema::correctTypoInParmVarReference(
624 StringRef Typo,
Dmitri Gribenko4b7f5fe2012-07-23 17:40:30 +0000625 ArrayRef<const ParmVarDecl *> ParamVars) {
Dmitri Gribenko34df2202012-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 Gribenkof26054f2012-07-11 21:38:39 +0000634
Dmitri Gribenko34df2202012-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 Gribenkof26054f2012-07-11 21:38:39 +0000655 }
656 }
Dmitri Gribenko34df2202012-07-31 22:37:06 +0000657 return false;
658}
659} // unnamed namespace
Dmitri Gribenkof26054f2012-07-11 21:38:39 +0000660
Dmitri Gribenko34df2202012-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 Gribenkof26054f2012-07-11 21:38:39 +0000699}
700
Dmitri Gribenkod73e4ce2012-07-23 16:43:01 +0000701InlineCommandComment::RenderKind
702Sema::getInlineCommandRenderKind(StringRef Name) const {
Dmitri Gribenkoca7f80a2012-08-09 00:03:17 +0000703 assert(Traits.isInlineCommand(Name));
Dmitri Gribenkod73e4ce2012-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 Gribenkoe00ffc72012-07-13 00:44:24 +0000712bool Sema::isHTMLEndTagOptional(StringRef Name) {
Dmitri Gribenkoec925312012-07-06 00:28:32 +0000713 return llvm::StringSwitch<bool>(Name)
Dmitri Gribenko9460fbf2012-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 Gribenkoe00ffc72012-07-13 00:44:24 +0000728bool Sema::isHTMLEndTagForbidden(StringRef Name) {
Dmitri Gribenko9460fbf2012-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 Gribenkoec925312012-07-06 00:28:32 +0000735}
736
737} // end namespace comments
738} // end namespace clang
739