blob: 68a9ebbd5a89450cf43f9a08b9ce70bce22eace0 [file] [log] [blame]
Fariborz Jahanianca76bf82013-03-05 19:52:24 +00001//===--- CommentSema.cpp - Doxygen comment semantic analysis --------------===//
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +00002//
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"
Benjamin Kramer2fa67ef2012-12-01 15:09:41 +000011#include "clang/AST/Attr.h"
Dmitri Gribenkoaa580812012-08-09 00:03:17 +000012#include "clang/AST/CommentCommandTraits.h"
Benjamin Kramer2fa67ef2012-12-01 15:09:41 +000013#include "clang/AST/CommentDiagnostic.h"
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000014#include "clang/AST/Decl.h"
Dmitri Gribenko96b09862012-07-31 22:37:06 +000015#include "clang/AST/DeclTemplate.h"
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000016#include "clang/Basic/SourceManager.h"
Dmitri Gribenko19523542012-09-29 11:40:46 +000017#include "clang/Lex/Preprocessor.h"
Dmitri Gribenko19523542012-09-29 11:40:46 +000018#include "llvm/ADT/SmallString.h"
Chandler Carruth55fc8732012-12-04 09:13:33 +000019#include "llvm/ADT/StringSwitch.h"
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000020
21namespace clang {
22namespace comments {
23
Dmitri Gribenkoc24a76e2012-08-31 02:21:44 +000024namespace {
25#include "clang/AST/CommentHTMLTagsProperties.inc"
26} // unnamed namespace
27
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000028Sema::Sema(llvm::BumpPtrAllocator &Allocator, const SourceManager &SourceMgr,
Dmitri Gribenko19523542012-09-29 11:40:46 +000029 DiagnosticsEngine &Diags, CommandTraits &Traits,
30 const Preprocessor *PP) :
Dmitri Gribenkoaa580812012-08-09 00:03:17 +000031 Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags), Traits(Traits),
Fariborz Jahanianf843a582013-01-31 23:12:39 +000032 PP(PP), ThisDeclInfo(NULL), BriefCommand(NULL), ReturnsCommand(NULL),
33 HeaderfileCommand(NULL) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000034}
35
36void Sema::setDecl(const Decl *D) {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +000037 if (!D)
38 return;
39
40 ThisDeclInfo = new (Allocator) DeclInfo;
Fariborz Jahanianbf967be2012-10-10 18:34:52 +000041 ThisDeclInfo->CommentDecl = D;
Dmitri Gribenko651f8ce2012-08-01 23:21:57 +000042 ThisDeclInfo->IsFilled = false;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000043}
44
45ParagraphComment *Sema::actOnParagraphComment(
46 ArrayRef<InlineContentComment *> Content) {
47 return new (Allocator) ParagraphComment(Content);
48}
49
Dmitri Gribenko808383d2013-03-04 23:06:15 +000050BlockCommandComment *Sema::actOnBlockCommandStart(
51 SourceLocation LocBegin,
52 SourceLocation LocEnd,
53 unsigned CommandID,
54 CommandMarkerKind CommandMarker) {
55 return new (Allocator) BlockCommandComment(LocBegin, LocEnd, CommandID,
56 CommandMarker);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000057}
58
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +000059void Sema::actOnBlockCommandArgs(BlockCommandComment *Command,
60 ArrayRef<BlockCommandComment::Argument> Args) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000061 Command->setArgs(Args);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000062}
63
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +000064void Sema::actOnBlockCommandFinish(BlockCommandComment *Command,
65 ParagraphComment *Paragraph) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000066 Command->setParagraph(Paragraph);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000067 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenko9443c572012-08-06 17:08:27 +000068 checkBlockCommandDuplicate(Command);
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +000069 checkReturnsCommand(Command);
Dmitri Gribenko0bd98382012-09-22 21:47:50 +000070 checkDeprecatedCommand(Command);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000071}
72
Dmitri Gribenko808383d2013-03-04 23:06:15 +000073ParamCommandComment *Sema::actOnParamCommandStart(
74 SourceLocation LocBegin,
75 SourceLocation LocEnd,
76 unsigned CommandID,
77 CommandMarkerKind CommandMarker) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000078 ParamCommandComment *Command =
Dmitri Gribenko808383d2013-03-04 23:06:15 +000079 new (Allocator) ParamCommandComment(LocBegin, LocEnd, CommandID,
80 CommandMarker);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000081
Dmitri Gribenko8487c522012-07-23 17:40:30 +000082 if (!isFunctionDecl())
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000083 Diag(Command->getLocation(),
84 diag::warn_doc_param_not_attached_to_a_function_decl)
Dmitri Gribenko808383d2013-03-04 23:06:15 +000085 << CommandMarker
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +000086 << Command->getCommandNameRange(Traits);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000087
88 return Command;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000089}
90
Fariborz Jahanian2a268f22013-03-05 01:05:07 +000091void Sema::checkFunctionDeclVerbatimLine(const BlockCommandComment *Comment) {
92 const CommandInfo *Info = Traits.getCommandInfo(Comment->getCommandID());
Fariborz Jahanian99a70572013-03-05 22:46:07 +000093 if (!Info->IsFunctionDeclarationCommand)
94 return;
95 StringRef Name = Info->Name;
Fariborz Jahanian88f070f2013-03-06 17:36:51 +000096 unsigned DiagSelect = llvm::StringSwitch<unsigned>(Name)
97 .Case("function", !isAnyFunctionDecl() ? 1 : 0)
98 .Case("method", !isObjCMethodDecl() ? 2 : 0)
99 .Case("callback", !isFunctionPointerVarDecl() ? 3 : 0)
Fariborz Jahanian99a70572013-03-05 22:46:07 +0000100 .Default(0);
101
Fariborz Jahanian88f070f2013-03-06 17:36:51 +0000102 if (DiagSelect)
103 Diag(Comment->getLocation(), diag::warn_doc_function_method_decl_mismatch)
104 << Comment->getCommandMarker()
105 << (DiagSelect-1) << (DiagSelect-1)
Fariborz Jahanian2a268f22013-03-05 01:05:07 +0000106 << Comment->getSourceRange();
107}
108
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000109void Sema::actOnParamCommandDirectionArg(ParamCommandComment *Command,
110 SourceLocation ArgLocBegin,
111 SourceLocation ArgLocEnd,
112 StringRef Arg) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000113 ParamCommandComment::PassDirection Direction;
114 std::string ArgLower = Arg.lower();
115 // TODO: optimize: lower Name first (need an API in SmallString for that),
116 // after that StringSwitch.
117 if (ArgLower == "[in]")
118 Direction = ParamCommandComment::In;
119 else if (ArgLower == "[out]")
120 Direction = ParamCommandComment::Out;
121 else if (ArgLower == "[in,out]" || ArgLower == "[out,in]")
122 Direction = ParamCommandComment::InOut;
123 else {
124 // Remove spaces.
125 std::string::iterator O = ArgLower.begin();
126 for (std::string::iterator I = ArgLower.begin(), E = ArgLower.end();
127 I != E; ++I) {
128 const char C = *I;
129 if (C != ' ' && C != '\n' && C != '\r' &&
130 C != '\t' && C != '\v' && C != '\f')
131 *O++ = C;
132 }
133 ArgLower.resize(O - ArgLower.begin());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000134
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000135 bool RemovingWhitespaceHelped = false;
136 if (ArgLower == "[in]") {
137 Direction = ParamCommandComment::In;
138 RemovingWhitespaceHelped = true;
139 } else if (ArgLower == "[out]") {
140 Direction = ParamCommandComment::Out;
141 RemovingWhitespaceHelped = true;
142 } else if (ArgLower == "[in,out]" || ArgLower == "[out,in]") {
143 Direction = ParamCommandComment::InOut;
144 RemovingWhitespaceHelped = true;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000145 } else {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000146 Direction = ParamCommandComment::In;
147 RemovingWhitespaceHelped = false;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000148 }
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000149
150 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
151 if (RemovingWhitespaceHelped)
152 Diag(ArgLocBegin, diag::warn_doc_param_spaces_in_direction)
153 << ArgRange
154 << FixItHint::CreateReplacement(
155 ArgRange,
156 ParamCommandComment::getDirectionAsString(Direction));
157 else
158 Diag(ArgLocBegin, diag::warn_doc_param_invalid_direction)
159 << ArgRange;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000160 }
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000161 Command->setDirection(Direction, /* Explicit = */ true);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000162}
163
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000164void Sema::actOnParamCommandParamNameArg(ParamCommandComment *Command,
165 SourceLocation ArgLocBegin,
166 SourceLocation ArgLocEnd,
167 StringRef Arg) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000168 // Parser will not feed us more arguments than needed.
Dmitri Gribenko0eaf69d2012-07-13 19:02:42 +0000169 assert(Command->getNumArgs() == 0);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000170
171 if (!Command->isDirectionExplicit()) {
172 // User didn't provide a direction argument.
173 Command->setDirection(ParamCommandComment::In, /* Explicit = */ false);
174 }
175 typedef BlockCommandComment::Argument Argument;
176 Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
177 ArgLocEnd),
178 Arg);
179 Command->setArgs(llvm::makeArrayRef(A, 1));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000180}
181
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000182void Sema::actOnParamCommandFinish(ParamCommandComment *Command,
183 ParagraphComment *Paragraph) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000184 Command->setParagraph(Paragraph);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000185 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000186}
187
Dmitri Gribenko808383d2013-03-04 23:06:15 +0000188TParamCommandComment *Sema::actOnTParamCommandStart(
189 SourceLocation LocBegin,
190 SourceLocation LocEnd,
191 unsigned CommandID,
192 CommandMarkerKind CommandMarker) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000193 TParamCommandComment *Command =
Dmitri Gribenko808383d2013-03-04 23:06:15 +0000194 new (Allocator) TParamCommandComment(LocBegin, LocEnd, CommandID,
195 CommandMarker);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000196
Dmitri Gribenko04bf29e2012-08-06 21:31:15 +0000197 if (!isTemplateOrSpecialization())
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000198 Diag(Command->getLocation(),
199 diag::warn_doc_tparam_not_attached_to_a_template_decl)
Dmitri Gribenko808383d2013-03-04 23:06:15 +0000200 << CommandMarker
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000201 << Command->getCommandNameRange(Traits);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000202
203 return Command;
204}
205
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000206void Sema::actOnTParamCommandParamNameArg(TParamCommandComment *Command,
207 SourceLocation ArgLocBegin,
208 SourceLocation ArgLocEnd,
209 StringRef Arg) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000210 // Parser will not feed us more arguments than needed.
211 assert(Command->getNumArgs() == 0);
212
213 typedef BlockCommandComment::Argument Argument;
214 Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
215 ArgLocEnd),
216 Arg);
217 Command->setArgs(llvm::makeArrayRef(A, 1));
218
Dmitri Gribenko04bf29e2012-08-06 21:31:15 +0000219 if (!isTemplateOrSpecialization()) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000220 // We already warned that this \\tparam is not attached to a template decl.
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000221 return;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000222 }
223
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000224 const TemplateParameterList *TemplateParameters =
225 ThisDeclInfo->TemplateParameters;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000226 SmallVector<unsigned, 2> Position;
227 if (resolveTParamReference(Arg, TemplateParameters, &Position)) {
228 Command->setPosition(copyArray(llvm::makeArrayRef(Position)));
229 llvm::StringMap<TParamCommandComment *>::iterator PrevCommandIt =
230 TemplateParameterDocs.find(Arg);
231 if (PrevCommandIt != TemplateParameterDocs.end()) {
232 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
233 Diag(ArgLocBegin, diag::warn_doc_tparam_duplicate)
234 << Arg << ArgRange;
235 TParamCommandComment *PrevCommand = PrevCommandIt->second;
236 Diag(PrevCommand->getLocation(), diag::note_doc_tparam_previous)
237 << PrevCommand->getParamNameRange();
238 }
239 TemplateParameterDocs[Arg] = Command;
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000240 return;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000241 }
242
243 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
244 Diag(ArgLocBegin, diag::warn_doc_tparam_not_found)
245 << Arg << ArgRange;
246
247 if (!TemplateParameters || TemplateParameters->size() == 0)
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000248 return;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000249
250 StringRef CorrectedName;
251 if (TemplateParameters->size() == 1) {
252 const NamedDecl *Param = TemplateParameters->getParam(0);
253 const IdentifierInfo *II = Param->getIdentifier();
254 if (II)
255 CorrectedName = II->getName();
256 } else {
257 CorrectedName = correctTypoInTParamReference(Arg, TemplateParameters);
258 }
259
260 if (!CorrectedName.empty()) {
261 Diag(ArgLocBegin, diag::note_doc_tparam_name_suggestion)
262 << CorrectedName
263 << FixItHint::CreateReplacement(ArgRange, CorrectedName);
264 }
265
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000266 return;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000267}
268
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000269void Sema::actOnTParamCommandFinish(TParamCommandComment *Command,
270 ParagraphComment *Paragraph) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000271 Command->setParagraph(Paragraph);
272 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000273}
274
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000275InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
276 SourceLocation CommandLocEnd,
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000277 unsigned CommandID) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000278 ArrayRef<InlineCommandComment::Argument> Args;
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000279 StringRef CommandName = Traits.getCommandInfo(CommandID)->Name;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000280 return new (Allocator) InlineCommandComment(
281 CommandLocBegin,
282 CommandLocEnd,
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000283 CommandID,
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000284 getInlineCommandRenderKind(CommandName),
285 Args);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000286}
287
288InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
289 SourceLocation CommandLocEnd,
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000290 unsigned CommandID,
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000291 SourceLocation ArgLocBegin,
292 SourceLocation ArgLocEnd,
293 StringRef Arg) {
294 typedef InlineCommandComment::Argument Argument;
295 Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
296 ArgLocEnd),
297 Arg);
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000298 StringRef CommandName = Traits.getCommandInfo(CommandID)->Name;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000299
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000300 return new (Allocator) InlineCommandComment(
301 CommandLocBegin,
302 CommandLocEnd,
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000303 CommandID,
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000304 getInlineCommandRenderKind(CommandName),
305 llvm::makeArrayRef(A, 1));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000306}
307
308InlineContentComment *Sema::actOnUnknownCommand(SourceLocation LocBegin,
309 SourceLocation LocEnd,
Dmitri Gribenkob0b8a962012-09-11 19:22:03 +0000310 StringRef CommandName) {
311 unsigned CommandID = Traits.registerUnknownCommand(CommandName)->getID();
312 return actOnUnknownCommand(LocBegin, LocEnd, CommandID);
313}
314
315InlineContentComment *Sema::actOnUnknownCommand(SourceLocation LocBegin,
316 SourceLocation LocEnd,
317 unsigned CommandID) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000318 ArrayRef<InlineCommandComment::Argument> Args;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000319 return new (Allocator) InlineCommandComment(
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000320 LocBegin, LocEnd, CommandID,
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000321 InlineCommandComment::RenderNormal,
322 Args);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000323}
324
325TextComment *Sema::actOnText(SourceLocation LocBegin,
326 SourceLocation LocEnd,
327 StringRef Text) {
328 return new (Allocator) TextComment(LocBegin, LocEnd, Text);
329}
330
331VerbatimBlockComment *Sema::actOnVerbatimBlockStart(SourceLocation Loc,
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000332 unsigned CommandID) {
333 StringRef CommandName = Traits.getCommandInfo(CommandID)->Name;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000334 return new (Allocator) VerbatimBlockComment(
335 Loc,
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000336 Loc.getLocWithOffset(1 + CommandName.size()),
337 CommandID);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000338}
339
340VerbatimBlockLineComment *Sema::actOnVerbatimBlockLine(SourceLocation Loc,
341 StringRef Text) {
342 return new (Allocator) VerbatimBlockLineComment(Loc, Text);
343}
344
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000345void Sema::actOnVerbatimBlockFinish(
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000346 VerbatimBlockComment *Block,
347 SourceLocation CloseNameLocBegin,
348 StringRef CloseName,
349 ArrayRef<VerbatimBlockLineComment *> Lines) {
350 Block->setCloseName(CloseName, CloseNameLocBegin);
351 Block->setLines(Lines);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000352}
353
354VerbatimLineComment *Sema::actOnVerbatimLine(SourceLocation LocBegin,
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000355 unsigned CommandID,
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000356 SourceLocation TextBegin,
357 StringRef Text) {
Fariborz Jahanianbca97882013-03-05 19:40:47 +0000358 VerbatimLineComment *VL = new (Allocator) VerbatimLineComment(
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000359 LocBegin,
360 TextBegin.getLocWithOffset(Text.size()),
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000361 CommandID,
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000362 TextBegin,
363 Text);
Fariborz Jahanianbca97882013-03-05 19:40:47 +0000364 checkFunctionDeclVerbatimLine(VL);
365 return VL;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000366}
367
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000368HTMLStartTagComment *Sema::actOnHTMLStartTagStart(SourceLocation LocBegin,
369 StringRef TagName) {
370 return new (Allocator) HTMLStartTagComment(LocBegin, TagName);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000371}
372
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000373void Sema::actOnHTMLStartTagFinish(
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000374 HTMLStartTagComment *Tag,
375 ArrayRef<HTMLStartTagComment::Attribute> Attrs,
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000376 SourceLocation GreaterLoc,
377 bool IsSelfClosing) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000378 Tag->setAttrs(Attrs);
379 Tag->setGreaterLoc(GreaterLoc);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000380 if (IsSelfClosing)
381 Tag->setSelfClosing();
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000382 else if (!isHTMLEndTagForbidden(Tag->getTagName()))
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000383 HTMLOpenTags.push_back(Tag);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000384}
385
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000386HTMLEndTagComment *Sema::actOnHTMLEndTag(SourceLocation LocBegin,
387 SourceLocation LocEnd,
388 StringRef TagName) {
389 HTMLEndTagComment *HET =
390 new (Allocator) HTMLEndTagComment(LocBegin, LocEnd, TagName);
391 if (isHTMLEndTagForbidden(TagName)) {
392 Diag(HET->getLocation(), diag::warn_doc_html_end_forbidden)
393 << TagName << HET->getSourceRange();
394 return HET;
Dmitri Gribenko3d986982012-07-12 23:37:09 +0000395 }
396
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000397 bool FoundOpen = false;
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000398 for (SmallVectorImpl<HTMLStartTagComment *>::const_reverse_iterator
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000399 I = HTMLOpenTags.rbegin(), E = HTMLOpenTags.rend();
400 I != E; ++I) {
401 if ((*I)->getTagName() == TagName) {
402 FoundOpen = true;
403 break;
404 }
405 }
406 if (!FoundOpen) {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000407 Diag(HET->getLocation(), diag::warn_doc_html_end_unbalanced)
408 << HET->getSourceRange();
409 return HET;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000410 }
411
412 while (!HTMLOpenTags.empty()) {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000413 const HTMLStartTagComment *HST = HTMLOpenTags.back();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000414 HTMLOpenTags.pop_back();
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000415 StringRef LastNotClosedTagName = HST->getTagName();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000416 if (LastNotClosedTagName == TagName)
417 break;
418
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000419 if (isHTMLEndTagOptional(LastNotClosedTagName))
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000420 continue;
421
422 bool OpenLineInvalid;
423 const unsigned OpenLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000424 HST->getLocation(),
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000425 &OpenLineInvalid);
426 bool CloseLineInvalid;
427 const unsigned CloseLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000428 HET->getLocation(),
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000429 &CloseLineInvalid);
430
431 if (OpenLineInvalid || CloseLineInvalid || OpenLine == CloseLine)
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000432 Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
433 << HST->getTagName() << HET->getTagName()
434 << HST->getSourceRange() << HET->getSourceRange();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000435 else {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000436 Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
437 << HST->getTagName() << HET->getTagName()
438 << HST->getSourceRange();
439 Diag(HET->getLocation(), diag::note_doc_html_end_tag)
440 << HET->getSourceRange();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000441 }
442 }
443
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000444 return HET;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000445}
446
447FullComment *Sema::actOnFullComment(
448 ArrayRef<BlockContentComment *> Blocks) {
Fariborz Jahanian749ace62012-10-11 23:52:50 +0000449 FullComment *FC = new (Allocator) FullComment(Blocks, ThisDeclInfo);
Dmitri Gribenko9edd2c82012-08-24 17:45:39 +0000450 resolveParamCommandIndexes(FC);
451 return FC;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000452}
453
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000454void Sema::checkBlockCommandEmptyParagraph(BlockCommandComment *Command) {
Dmitri Gribenkoabcf0dc2012-09-13 20:36:01 +0000455 if (Traits.getCommandInfo(Command->getCommandID())->IsEmptyParagraphAllowed)
456 return;
457
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000458 ParagraphComment *Paragraph = Command->getParagraph();
459 if (Paragraph->isWhitespace()) {
460 SourceLocation DiagLoc;
Dmitri Gribenko0eaf69d2012-07-13 19:02:42 +0000461 if (Command->getNumArgs() > 0)
462 DiagLoc = Command->getArgRange(Command->getNumArgs() - 1).getEnd();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000463 if (!DiagLoc.isValid())
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000464 DiagLoc = Command->getCommandNameRange(Traits).getEnd();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000465 Diag(DiagLoc, diag::warn_doc_block_command_empty_paragraph)
Dmitri Gribenko808383d2013-03-04 23:06:15 +0000466 << Command->getCommandMarker()
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000467 << Command->getCommandName(Traits)
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000468 << Command->getSourceRange();
469 }
470}
471
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +0000472void Sema::checkReturnsCommand(const BlockCommandComment *Command) {
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000473 if (!Traits.getCommandInfo(Command->getCommandID())->IsReturnsCommand)
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +0000474 return;
475 if (isFunctionDecl()) {
476 if (ThisDeclInfo->ResultType->isVoidType()) {
477 unsigned DiagKind;
Fariborz Jahanianbf967be2012-10-10 18:34:52 +0000478 switch (ThisDeclInfo->CommentDecl->getKind()) {
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +0000479 default:
Dmitri Gribenko88815f32012-08-06 16:29:26 +0000480 if (ThisDeclInfo->IsObjCMethod)
481 DiagKind = 3;
482 else
483 DiagKind = 0;
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +0000484 break;
485 case Decl::CXXConstructor:
486 DiagKind = 1;
487 break;
488 case Decl::CXXDestructor:
489 DiagKind = 2;
490 break;
491 }
492 Diag(Command->getLocation(),
493 diag::warn_doc_returns_attached_to_a_void_function)
Dmitri Gribenko808383d2013-03-04 23:06:15 +0000494 << Command->getCommandMarker()
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000495 << Command->getCommandName(Traits)
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +0000496 << DiagKind
497 << Command->getSourceRange();
498 }
499 return;
500 }
Fariborz Jahanian664e8602013-02-27 00:46:06 +0000501 else if (isObjCPropertyDecl())
502 return;
503
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +0000504 Diag(Command->getLocation(),
505 diag::warn_doc_returns_not_attached_to_a_function_decl)
Dmitri Gribenko808383d2013-03-04 23:06:15 +0000506 << Command->getCommandMarker()
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000507 << Command->getCommandName(Traits)
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +0000508 << Command->getSourceRange();
509}
510
Dmitri Gribenko9443c572012-08-06 17:08:27 +0000511void Sema::checkBlockCommandDuplicate(const BlockCommandComment *Command) {
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000512 const CommandInfo *Info = Traits.getCommandInfo(Command->getCommandID());
Dmitri Gribenko9443c572012-08-06 17:08:27 +0000513 const BlockCommandComment *PrevCommand = NULL;
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000514 if (Info->IsBriefCommand) {
Dmitri Gribenko9443c572012-08-06 17:08:27 +0000515 if (!BriefCommand) {
516 BriefCommand = Command;
517 return;
518 }
519 PrevCommand = BriefCommand;
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000520 } else if (Info->IsReturnsCommand) {
Dmitri Gribenko9443c572012-08-06 17:08:27 +0000521 if (!ReturnsCommand) {
522 ReturnsCommand = Command;
523 return;
524 }
525 PrevCommand = ReturnsCommand;
Fariborz Jahanianf843a582013-01-31 23:12:39 +0000526 } else if (Info->IsHeaderfileCommand) {
527 if (!HeaderfileCommand) {
528 HeaderfileCommand = Command;
529 return;
530 }
531 PrevCommand = HeaderfileCommand;
Dmitri Gribenko9443c572012-08-06 17:08:27 +0000532 } else {
533 // We don't want to check this command for duplicates.
534 return;
535 }
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000536 StringRef CommandName = Command->getCommandName(Traits);
537 StringRef PrevCommandName = PrevCommand->getCommandName(Traits);
Dmitri Gribenko9443c572012-08-06 17:08:27 +0000538 Diag(Command->getLocation(), diag::warn_doc_block_command_duplicate)
Dmitri Gribenko808383d2013-03-04 23:06:15 +0000539 << Command->getCommandMarker()
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000540 << CommandName
Dmitri Gribenko9443c572012-08-06 17:08:27 +0000541 << Command->getSourceRange();
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000542 if (CommandName == PrevCommandName)
Dmitri Gribenko9443c572012-08-06 17:08:27 +0000543 Diag(PrevCommand->getLocation(), diag::note_doc_block_command_previous)
Dmitri Gribenko808383d2013-03-04 23:06:15 +0000544 << PrevCommand->getCommandMarker()
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000545 << PrevCommandName
546 << PrevCommand->getSourceRange();
Dmitri Gribenko9443c572012-08-06 17:08:27 +0000547 else
548 Diag(PrevCommand->getLocation(),
549 diag::note_doc_block_command_previous_alias)
Dmitri Gribenko808383d2013-03-04 23:06:15 +0000550 << PrevCommand->getCommandMarker()
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000551 << PrevCommandName
552 << CommandName;
Dmitri Gribenko9443c572012-08-06 17:08:27 +0000553}
554
Dmitri Gribenko0bd98382012-09-22 21:47:50 +0000555void Sema::checkDeprecatedCommand(const BlockCommandComment *Command) {
556 if (!Traits.getCommandInfo(Command->getCommandID())->IsDeprecatedCommand)
557 return;
558
Fariborz Jahanianbf967be2012-10-10 18:34:52 +0000559 const Decl *D = ThisDeclInfo->CommentDecl;
Dmitri Gribenko0bd98382012-09-22 21:47:50 +0000560 if (!D)
561 return;
562
563 if (D->hasAttr<DeprecatedAttr>() ||
564 D->hasAttr<AvailabilityAttr>() ||
565 D->hasAttr<UnavailableAttr>())
566 return;
567
568 Diag(Command->getLocation(),
569 diag::warn_doc_deprecated_not_sync)
570 << Command->getSourceRange();
571
572 // Try to emit a fixit with a deprecation attribute.
573 if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
574 // Don't emit a Fix-It for non-member function definitions. GCC does not
575 // accept attributes on them.
576 const DeclContext *Ctx = FD->getDeclContext();
577 if ((!Ctx || !Ctx->isRecord()) &&
578 FD->doesThisDeclarationHaveABody())
579 return;
580
Dmitri Gribenko19523542012-09-29 11:40:46 +0000581 StringRef AttributeSpelling = "__attribute__((deprecated))";
582 if (PP) {
583 TokenValue Tokens[] = {
584 tok::kw___attribute, tok::l_paren, tok::l_paren,
585 PP->getIdentifierInfo("deprecated"),
586 tok::r_paren, tok::r_paren
587 };
588 StringRef MacroName = PP->getLastMacroWithSpelling(FD->getLocation(),
589 Tokens);
590 if (!MacroName.empty())
591 AttributeSpelling = MacroName;
592 }
593
594 SmallString<64> TextToInsert(" ");
595 TextToInsert += AttributeSpelling;
Dmitri Gribenko0bd98382012-09-22 21:47:50 +0000596 Diag(FD->getLocEnd(),
597 diag::note_add_deprecation_attr)
598 << FixItHint::CreateInsertion(FD->getLocEnd().getLocWithOffset(1),
Dmitri Gribenko19523542012-09-29 11:40:46 +0000599 TextToInsert);
Dmitri Gribenko0bd98382012-09-22 21:47:50 +0000600 }
601}
602
Dmitri Gribenko9edd2c82012-08-24 17:45:39 +0000603void Sema::resolveParamCommandIndexes(const FullComment *FC) {
604 if (!isFunctionDecl()) {
605 // We already warned that \\param commands are not attached to a function
606 // decl.
607 return;
608 }
609
Dmitri Gribenkocfa88f82013-01-12 19:30:44 +0000610 SmallVector<ParamCommandComment *, 8> UnresolvedParamCommands;
Dmitri Gribenko9edd2c82012-08-24 17:45:39 +0000611
612 // Comment AST nodes that correspond to \c ParamVars for which we have
613 // found a \\param command or NULL if no documentation was found so far.
Dmitri Gribenkocfa88f82013-01-12 19:30:44 +0000614 SmallVector<ParamCommandComment *, 8> ParamVarDocs;
Dmitri Gribenko9edd2c82012-08-24 17:45:39 +0000615
616 ArrayRef<const ParmVarDecl *> ParamVars = getParamVars();
617 ParamVarDocs.resize(ParamVars.size(), NULL);
618
619 // First pass over all \\param commands: resolve all parameter names.
620 for (Comment::child_iterator I = FC->child_begin(), E = FC->child_end();
621 I != E; ++I) {
622 ParamCommandComment *PCC = dyn_cast<ParamCommandComment>(*I);
623 if (!PCC || !PCC->hasParamName())
624 continue;
Fariborz Jahanian262e60c2012-10-18 21:42:42 +0000625 StringRef ParamName = PCC->getParamNameAsWritten();
Dmitri Gribenko9edd2c82012-08-24 17:45:39 +0000626
627 // Check that referenced parameter name is in the function decl.
628 const unsigned ResolvedParamIndex = resolveParmVarReference(ParamName,
629 ParamVars);
630 if (ResolvedParamIndex == ParamCommandComment::InvalidParamIndex) {
631 UnresolvedParamCommands.push_back(PCC);
632 continue;
633 }
634 PCC->setParamIndex(ResolvedParamIndex);
635 if (ParamVarDocs[ResolvedParamIndex]) {
636 SourceRange ArgRange = PCC->getParamNameRange();
637 Diag(ArgRange.getBegin(), diag::warn_doc_param_duplicate)
638 << ParamName << ArgRange;
639 ParamCommandComment *PrevCommand = ParamVarDocs[ResolvedParamIndex];
640 Diag(PrevCommand->getLocation(), diag::note_doc_param_previous)
641 << PrevCommand->getParamNameRange();
642 }
643 ParamVarDocs[ResolvedParamIndex] = PCC;
644 }
645
646 // Find parameter declarations that have no corresponding \\param.
Dmitri Gribenkocfa88f82013-01-12 19:30:44 +0000647 SmallVector<const ParmVarDecl *, 8> OrphanedParamDecls;
Dmitri Gribenko9edd2c82012-08-24 17:45:39 +0000648 for (unsigned i = 0, e = ParamVarDocs.size(); i != e; ++i) {
649 if (!ParamVarDocs[i])
650 OrphanedParamDecls.push_back(ParamVars[i]);
651 }
652
653 // Second pass over unresolved \\param commands: do typo correction.
654 // Suggest corrections from a set of parameter declarations that have no
655 // corresponding \\param.
656 for (unsigned i = 0, e = UnresolvedParamCommands.size(); i != e; ++i) {
657 const ParamCommandComment *PCC = UnresolvedParamCommands[i];
658
659 SourceRange ArgRange = PCC->getParamNameRange();
Fariborz Jahanian262e60c2012-10-18 21:42:42 +0000660 StringRef ParamName = PCC->getParamNameAsWritten();
Dmitri Gribenko9edd2c82012-08-24 17:45:39 +0000661 Diag(ArgRange.getBegin(), diag::warn_doc_param_not_found)
662 << ParamName << ArgRange;
663
664 // All parameters documented -- can't suggest a correction.
665 if (OrphanedParamDecls.size() == 0)
666 continue;
667
668 unsigned CorrectedParamIndex = ParamCommandComment::InvalidParamIndex;
669 if (OrphanedParamDecls.size() == 1) {
670 // If one parameter is not documented then that parameter is the only
671 // possible suggestion.
672 CorrectedParamIndex = 0;
673 } else {
674 // Do typo correction.
675 CorrectedParamIndex = correctTypoInParmVarReference(ParamName,
676 OrphanedParamDecls);
677 }
678 if (CorrectedParamIndex != ParamCommandComment::InvalidParamIndex) {
679 const ParmVarDecl *CorrectedPVD = OrphanedParamDecls[CorrectedParamIndex];
680 if (const IdentifierInfo *CorrectedII = CorrectedPVD->getIdentifier())
681 Diag(ArgRange.getBegin(), diag::note_doc_param_name_suggestion)
682 << CorrectedII->getName()
683 << FixItHint::CreateReplacement(ArgRange, CorrectedII->getName());
684 }
685 }
686}
687
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000688bool Sema::isFunctionDecl() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000689 if (!ThisDeclInfo)
690 return false;
691 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko00c59f72012-07-24 20:58:46 +0000692 inspectThisDecl();
Dmitri Gribenkoaf19a6a2012-08-02 21:45:39 +0000693 return ThisDeclInfo->getKind() == DeclInfo::FunctionKind;
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000694}
Fariborz Jahanian99a70572013-03-05 22:46:07 +0000695
Fariborz Jahanianeb8f69f2013-03-05 23:20:29 +0000696bool Sema::isAnyFunctionDecl() {
697 return isFunctionDecl() && ThisDeclInfo->CurrentDecl &&
698 isa<FunctionDecl>(ThisDeclInfo->CurrentDecl);
699}
700
Fariborz Jahanian99a70572013-03-05 22:46:07 +0000701bool Sema::isObjCMethodDecl() {
702 return isFunctionDecl() && ThisDeclInfo->CurrentDecl &&
703 isa<ObjCMethodDecl>(ThisDeclInfo->CurrentDecl);
704}
Fariborz Jahanian664e8602013-02-27 00:46:06 +0000705
Fariborz Jahanian99a70572013-03-05 22:46:07 +0000706/// isFunctionPointerVarDecl - returns 'true' if declaration is a pointer to
707/// function decl.
708bool Sema::isFunctionPointerVarDecl() {
Fariborz Jahanianbca97882013-03-05 19:40:47 +0000709 if (!ThisDeclInfo)
710 return false;
711 if (!ThisDeclInfo->IsFilled)
712 inspectThisDecl();
713 if (ThisDeclInfo->getKind() == DeclInfo::VariableKind) {
714 if (const VarDecl *VD = dyn_cast_or_null<VarDecl>(ThisDeclInfo->CurrentDecl)) {
715 QualType QT = VD->getType();
716 return QT->isFunctionPointerType();
717 }
718 }
719 return false;
720}
721
Fariborz Jahanian664e8602013-02-27 00:46:06 +0000722bool Sema::isObjCPropertyDecl() {
723 if (!ThisDeclInfo)
724 return false;
725 if (!ThisDeclInfo->IsFilled)
726 inspectThisDecl();
727 return ThisDeclInfo->CurrentDecl->getKind() == Decl::ObjCProperty;
728}
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000729
Dmitri Gribenko04bf29e2012-08-06 21:31:15 +0000730bool Sema::isTemplateOrSpecialization() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000731 if (!ThisDeclInfo)
732 return false;
733 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000734 inspectThisDecl();
Dmitri Gribenko04bf29e2012-08-06 21:31:15 +0000735 return ThisDeclInfo->getTemplateKind() != DeclInfo::NotTemplate;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000736}
737
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000738ArrayRef<const ParmVarDecl *> Sema::getParamVars() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000739 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko00c59f72012-07-24 20:58:46 +0000740 inspectThisDecl();
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000741 return ThisDeclInfo->ParamVars;
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000742}
743
744void Sema::inspectThisDecl() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000745 ThisDeclInfo->fill();
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000746}
747
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000748unsigned Sema::resolveParmVarReference(StringRef Name,
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000749 ArrayRef<const ParmVarDecl *> ParamVars) {
750 for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000751 const IdentifierInfo *II = ParamVars[i]->getIdentifier();
752 if (II && II->getName() == Name)
753 return i;
754 }
755 return ParamCommandComment::InvalidParamIndex;
756}
757
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000758namespace {
759class SimpleTypoCorrector {
760 StringRef Typo;
761 const unsigned MaxEditDistance;
762
763 const NamedDecl *BestDecl;
764 unsigned BestEditDistance;
765 unsigned BestIndex;
766 unsigned NextIndex;
767
768public:
769 SimpleTypoCorrector(StringRef Typo) :
770 Typo(Typo), MaxEditDistance((Typo.size() + 2) / 3),
771 BestDecl(NULL), BestEditDistance(MaxEditDistance + 1),
772 BestIndex(0), NextIndex(0)
773 { }
774
775 void addDecl(const NamedDecl *ND);
776
777 const NamedDecl *getBestDecl() const {
778 if (BestEditDistance > MaxEditDistance)
779 return NULL;
780
781 return BestDecl;
782 }
783
784 unsigned getBestDeclIndex() const {
785 assert(getBestDecl());
786 return BestIndex;
787 }
788};
789
790void SimpleTypoCorrector::addDecl(const NamedDecl *ND) {
791 unsigned CurrIndex = NextIndex++;
792
793 const IdentifierInfo *II = ND->getIdentifier();
794 if (!II)
795 return;
796
797 StringRef Name = II->getName();
798 unsigned MinPossibleEditDistance = abs((int)Name.size() - (int)Typo.size());
799 if (MinPossibleEditDistance > 0 &&
800 Typo.size() / MinPossibleEditDistance < 3)
801 return;
802
803 unsigned EditDistance = Typo.edit_distance(Name, true, MaxEditDistance);
804 if (EditDistance < BestEditDistance) {
805 BestEditDistance = EditDistance;
806 BestDecl = ND;
807 BestIndex = CurrIndex;
808 }
809}
810} // unnamed namespace
811
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000812unsigned Sema::correctTypoInParmVarReference(
813 StringRef Typo,
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000814 ArrayRef<const ParmVarDecl *> ParamVars) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000815 SimpleTypoCorrector Corrector(Typo);
816 for (unsigned i = 0, e = ParamVars.size(); i != e; ++i)
817 Corrector.addDecl(ParamVars[i]);
818 if (Corrector.getBestDecl())
819 return Corrector.getBestDeclIndex();
820 else
Dmitri Gribenko1ad23d62012-09-10 21:20:09 +0000821 return ParamCommandComment::InvalidParamIndex;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000822}
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000823
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000824namespace {
825bool ResolveTParamReferenceHelper(
826 StringRef Name,
827 const TemplateParameterList *TemplateParameters,
828 SmallVectorImpl<unsigned> *Position) {
829 for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
830 const NamedDecl *Param = TemplateParameters->getParam(i);
831 const IdentifierInfo *II = Param->getIdentifier();
832 if (II && II->getName() == Name) {
833 Position->push_back(i);
834 return true;
835 }
836
837 if (const TemplateTemplateParmDecl *TTP =
838 dyn_cast<TemplateTemplateParmDecl>(Param)) {
839 Position->push_back(i);
840 if (ResolveTParamReferenceHelper(Name, TTP->getTemplateParameters(),
841 Position))
842 return true;
843 Position->pop_back();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000844 }
845 }
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000846 return false;
847}
848} // unnamed namespace
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000849
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000850bool Sema::resolveTParamReference(
851 StringRef Name,
852 const TemplateParameterList *TemplateParameters,
853 SmallVectorImpl<unsigned> *Position) {
854 Position->clear();
855 if (!TemplateParameters)
856 return false;
857
858 return ResolveTParamReferenceHelper(Name, TemplateParameters, Position);
859}
860
861namespace {
862void CorrectTypoInTParamReferenceHelper(
863 const TemplateParameterList *TemplateParameters,
864 SimpleTypoCorrector &Corrector) {
865 for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
866 const NamedDecl *Param = TemplateParameters->getParam(i);
867 Corrector.addDecl(Param);
868
869 if (const TemplateTemplateParmDecl *TTP =
870 dyn_cast<TemplateTemplateParmDecl>(Param))
871 CorrectTypoInTParamReferenceHelper(TTP->getTemplateParameters(),
872 Corrector);
873 }
874}
875} // unnamed namespace
876
877StringRef Sema::correctTypoInTParamReference(
878 StringRef Typo,
879 const TemplateParameterList *TemplateParameters) {
880 SimpleTypoCorrector Corrector(Typo);
881 CorrectTypoInTParamReferenceHelper(TemplateParameters, Corrector);
882 if (const NamedDecl *ND = Corrector.getBestDecl()) {
883 const IdentifierInfo *II = ND->getIdentifier();
884 assert(II && "SimpleTypoCorrector should not return this decl");
885 return II->getName();
886 }
887 return StringRef();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000888}
889
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000890InlineCommandComment::RenderKind
891Sema::getInlineCommandRenderKind(StringRef Name) const {
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000892 assert(Traits.getCommandInfo(Name)->IsInlineCommand);
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000893
894 return llvm::StringSwitch<InlineCommandComment::RenderKind>(Name)
895 .Case("b", InlineCommandComment::RenderBold)
896 .Cases("c", "p", InlineCommandComment::RenderMonospaced)
897 .Cases("a", "e", "em", InlineCommandComment::RenderEmphasized)
898 .Default(InlineCommandComment::RenderNormal);
899}
900
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000901} // end namespace comments
902} // end namespace clang
903