blob: d664bdb6329196fe30d5ec88eedfba376468eb73 [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 Gribenko19523542012-09-29 11:40:46 +000016#include "clang/Lex/Preprocessor.h"
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000017#include "llvm/ADT/StringSwitch.h"
Dmitri Gribenko19523542012-09-29 11:40:46 +000018#include "llvm/ADT/SmallString.h"
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000019
20namespace clang {
21namespace comments {
22
Dmitri Gribenkoc24a76e2012-08-31 02:21:44 +000023namespace {
24#include "clang/AST/CommentHTMLTagsProperties.inc"
25} // unnamed namespace
26
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000027Sema::Sema(llvm::BumpPtrAllocator &Allocator, const SourceManager &SourceMgr,
Dmitri Gribenko19523542012-09-29 11:40:46 +000028 DiagnosticsEngine &Diags, CommandTraits &Traits,
29 const Preprocessor *PP) :
Dmitri Gribenkoaa580812012-08-09 00:03:17 +000030 Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags), Traits(Traits),
Dmitri Gribenko19523542012-09-29 11:40:46 +000031 PP(PP), ThisDeclInfo(NULL), BriefCommand(NULL), ReturnsCommand(NULL) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000032}
33
34void Sema::setDecl(const Decl *D) {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +000035 if (!D)
36 return;
37
38 ThisDeclInfo = new (Allocator) DeclInfo;
Fariborz Jahanianbf967be2012-10-10 18:34:52 +000039 ThisDeclInfo->CommentDecl = D;
Dmitri Gribenko651f8ce2012-08-01 23:21:57 +000040 ThisDeclInfo->IsFilled = false;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000041}
42
43ParagraphComment *Sema::actOnParagraphComment(
44 ArrayRef<InlineContentComment *> Content) {
45 return new (Allocator) ParagraphComment(Content);
46}
47
48BlockCommandComment *Sema::actOnBlockCommandStart(SourceLocation LocBegin,
49 SourceLocation LocEnd,
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +000050 unsigned CommandID) {
51 return new (Allocator) BlockCommandComment(LocBegin, LocEnd, CommandID);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000052}
53
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +000054void Sema::actOnBlockCommandArgs(BlockCommandComment *Command,
55 ArrayRef<BlockCommandComment::Argument> Args) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000056 Command->setArgs(Args);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000057}
58
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +000059void Sema::actOnBlockCommandFinish(BlockCommandComment *Command,
60 ParagraphComment *Paragraph) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000061 Command->setParagraph(Paragraph);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000062 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenko9443c572012-08-06 17:08:27 +000063 checkBlockCommandDuplicate(Command);
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +000064 checkReturnsCommand(Command);
Dmitri Gribenko0bd98382012-09-22 21:47:50 +000065 checkDeprecatedCommand(Command);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000066}
67
68ParamCommandComment *Sema::actOnParamCommandStart(SourceLocation LocBegin,
69 SourceLocation LocEnd,
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +000070 unsigned CommandID) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000071 ParamCommandComment *Command =
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +000072 new (Allocator) ParamCommandComment(LocBegin, LocEnd, CommandID);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000073
Dmitri Gribenko8487c522012-07-23 17:40:30 +000074 if (!isFunctionDecl())
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000075 Diag(Command->getLocation(),
76 diag::warn_doc_param_not_attached_to_a_function_decl)
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +000077 << Command->getCommandNameRange(Traits);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000078
79 return Command;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +000080}
81
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +000082void Sema::actOnParamCommandDirectionArg(ParamCommandComment *Command,
83 SourceLocation ArgLocBegin,
84 SourceLocation ArgLocEnd,
85 StringRef Arg) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +000086 ParamCommandComment::PassDirection Direction;
87 std::string ArgLower = Arg.lower();
88 // TODO: optimize: lower Name first (need an API in SmallString for that),
89 // after that StringSwitch.
90 if (ArgLower == "[in]")
91 Direction = ParamCommandComment::In;
92 else if (ArgLower == "[out]")
93 Direction = ParamCommandComment::Out;
94 else if (ArgLower == "[in,out]" || ArgLower == "[out,in]")
95 Direction = ParamCommandComment::InOut;
96 else {
97 // Remove spaces.
98 std::string::iterator O = ArgLower.begin();
99 for (std::string::iterator I = ArgLower.begin(), E = ArgLower.end();
100 I != E; ++I) {
101 const char C = *I;
102 if (C != ' ' && C != '\n' && C != '\r' &&
103 C != '\t' && C != '\v' && C != '\f')
104 *O++ = C;
105 }
106 ArgLower.resize(O - ArgLower.begin());
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000107
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000108 bool RemovingWhitespaceHelped = false;
109 if (ArgLower == "[in]") {
110 Direction = ParamCommandComment::In;
111 RemovingWhitespaceHelped = true;
112 } else if (ArgLower == "[out]") {
113 Direction = ParamCommandComment::Out;
114 RemovingWhitespaceHelped = true;
115 } else if (ArgLower == "[in,out]" || ArgLower == "[out,in]") {
116 Direction = ParamCommandComment::InOut;
117 RemovingWhitespaceHelped = true;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000118 } else {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000119 Direction = ParamCommandComment::In;
120 RemovingWhitespaceHelped = false;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000121 }
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000122
123 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
124 if (RemovingWhitespaceHelped)
125 Diag(ArgLocBegin, diag::warn_doc_param_spaces_in_direction)
126 << ArgRange
127 << FixItHint::CreateReplacement(
128 ArgRange,
129 ParamCommandComment::getDirectionAsString(Direction));
130 else
131 Diag(ArgLocBegin, diag::warn_doc_param_invalid_direction)
132 << ArgRange;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000133 }
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000134 Command->setDirection(Direction, /* Explicit = */ true);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000135}
136
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000137void Sema::actOnParamCommandParamNameArg(ParamCommandComment *Command,
138 SourceLocation ArgLocBegin,
139 SourceLocation ArgLocEnd,
140 StringRef Arg) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000141 // Parser will not feed us more arguments than needed.
Dmitri Gribenko0eaf69d2012-07-13 19:02:42 +0000142 assert(Command->getNumArgs() == 0);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000143
144 if (!Command->isDirectionExplicit()) {
145 // User didn't provide a direction argument.
146 Command->setDirection(ParamCommandComment::In, /* Explicit = */ false);
147 }
148 typedef BlockCommandComment::Argument Argument;
149 Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
150 ArgLocEnd),
151 Arg);
152 Command->setArgs(llvm::makeArrayRef(A, 1));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000153}
154
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000155void Sema::actOnParamCommandFinish(ParamCommandComment *Command,
156 ParagraphComment *Paragraph) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000157 Command->setParagraph(Paragraph);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000158 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000159}
160
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000161TParamCommandComment *Sema::actOnTParamCommandStart(SourceLocation LocBegin,
162 SourceLocation LocEnd,
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000163 unsigned CommandID) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000164 TParamCommandComment *Command =
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000165 new (Allocator) TParamCommandComment(LocBegin, LocEnd, CommandID);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000166
Dmitri Gribenko04bf29e2012-08-06 21:31:15 +0000167 if (!isTemplateOrSpecialization())
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000168 Diag(Command->getLocation(),
169 diag::warn_doc_tparam_not_attached_to_a_template_decl)
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000170 << Command->getCommandNameRange(Traits);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000171
172 return Command;
173}
174
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000175void Sema::actOnTParamCommandParamNameArg(TParamCommandComment *Command,
176 SourceLocation ArgLocBegin,
177 SourceLocation ArgLocEnd,
178 StringRef Arg) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000179 // Parser will not feed us more arguments than needed.
180 assert(Command->getNumArgs() == 0);
181
182 typedef BlockCommandComment::Argument Argument;
183 Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
184 ArgLocEnd),
185 Arg);
186 Command->setArgs(llvm::makeArrayRef(A, 1));
187
Dmitri Gribenko04bf29e2012-08-06 21:31:15 +0000188 if (!isTemplateOrSpecialization()) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000189 // We already warned that this \\tparam is not attached to a template decl.
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000190 return;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000191 }
192
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000193 const TemplateParameterList *TemplateParameters =
194 ThisDeclInfo->TemplateParameters;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000195 SmallVector<unsigned, 2> Position;
196 if (resolveTParamReference(Arg, TemplateParameters, &Position)) {
197 Command->setPosition(copyArray(llvm::makeArrayRef(Position)));
198 llvm::StringMap<TParamCommandComment *>::iterator PrevCommandIt =
199 TemplateParameterDocs.find(Arg);
200 if (PrevCommandIt != TemplateParameterDocs.end()) {
201 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
202 Diag(ArgLocBegin, diag::warn_doc_tparam_duplicate)
203 << Arg << ArgRange;
204 TParamCommandComment *PrevCommand = PrevCommandIt->second;
205 Diag(PrevCommand->getLocation(), diag::note_doc_tparam_previous)
206 << PrevCommand->getParamNameRange();
207 }
208 TemplateParameterDocs[Arg] = Command;
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000209 return;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000210 }
211
212 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
213 Diag(ArgLocBegin, diag::warn_doc_tparam_not_found)
214 << Arg << ArgRange;
215
216 if (!TemplateParameters || TemplateParameters->size() == 0)
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000217 return;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000218
219 StringRef CorrectedName;
220 if (TemplateParameters->size() == 1) {
221 const NamedDecl *Param = TemplateParameters->getParam(0);
222 const IdentifierInfo *II = Param->getIdentifier();
223 if (II)
224 CorrectedName = II->getName();
225 } else {
226 CorrectedName = correctTypoInTParamReference(Arg, TemplateParameters);
227 }
228
229 if (!CorrectedName.empty()) {
230 Diag(ArgLocBegin, diag::note_doc_tparam_name_suggestion)
231 << CorrectedName
232 << FixItHint::CreateReplacement(ArgRange, CorrectedName);
233 }
234
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000235 return;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000236}
237
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000238void Sema::actOnTParamCommandFinish(TParamCommandComment *Command,
239 ParagraphComment *Paragraph) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000240 Command->setParagraph(Paragraph);
241 checkBlockCommandEmptyParagraph(Command);
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000242}
243
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000244InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
245 SourceLocation CommandLocEnd,
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000246 unsigned CommandID) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000247 ArrayRef<InlineCommandComment::Argument> Args;
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000248 StringRef CommandName = Traits.getCommandInfo(CommandID)->Name;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000249 return new (Allocator) InlineCommandComment(
250 CommandLocBegin,
251 CommandLocEnd,
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000252 CommandID,
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000253 getInlineCommandRenderKind(CommandName),
254 Args);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000255}
256
257InlineCommandComment *Sema::actOnInlineCommand(SourceLocation CommandLocBegin,
258 SourceLocation CommandLocEnd,
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000259 unsigned CommandID,
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000260 SourceLocation ArgLocBegin,
261 SourceLocation ArgLocEnd,
262 StringRef Arg) {
263 typedef InlineCommandComment::Argument Argument;
264 Argument *A = new (Allocator) Argument(SourceRange(ArgLocBegin,
265 ArgLocEnd),
266 Arg);
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000267 StringRef CommandName = Traits.getCommandInfo(CommandID)->Name;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000268
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000269 return new (Allocator) InlineCommandComment(
270 CommandLocBegin,
271 CommandLocEnd,
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000272 CommandID,
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000273 getInlineCommandRenderKind(CommandName),
274 llvm::makeArrayRef(A, 1));
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000275}
276
277InlineContentComment *Sema::actOnUnknownCommand(SourceLocation LocBegin,
278 SourceLocation LocEnd,
Dmitri Gribenkob0b8a962012-09-11 19:22:03 +0000279 StringRef CommandName) {
280 unsigned CommandID = Traits.registerUnknownCommand(CommandName)->getID();
281 return actOnUnknownCommand(LocBegin, LocEnd, CommandID);
282}
283
284InlineContentComment *Sema::actOnUnknownCommand(SourceLocation LocBegin,
285 SourceLocation LocEnd,
286 unsigned CommandID) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000287 ArrayRef<InlineCommandComment::Argument> Args;
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000288 return new (Allocator) InlineCommandComment(
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000289 LocBegin, LocEnd, CommandID,
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000290 InlineCommandComment::RenderNormal,
291 Args);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000292}
293
294TextComment *Sema::actOnText(SourceLocation LocBegin,
295 SourceLocation LocEnd,
296 StringRef Text) {
297 return new (Allocator) TextComment(LocBegin, LocEnd, Text);
298}
299
300VerbatimBlockComment *Sema::actOnVerbatimBlockStart(SourceLocation Loc,
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000301 unsigned CommandID) {
302 StringRef CommandName = Traits.getCommandInfo(CommandID)->Name;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000303 return new (Allocator) VerbatimBlockComment(
304 Loc,
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000305 Loc.getLocWithOffset(1 + CommandName.size()),
306 CommandID);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000307}
308
309VerbatimBlockLineComment *Sema::actOnVerbatimBlockLine(SourceLocation Loc,
310 StringRef Text) {
311 return new (Allocator) VerbatimBlockLineComment(Loc, Text);
312}
313
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000314void Sema::actOnVerbatimBlockFinish(
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000315 VerbatimBlockComment *Block,
316 SourceLocation CloseNameLocBegin,
317 StringRef CloseName,
318 ArrayRef<VerbatimBlockLineComment *> Lines) {
319 Block->setCloseName(CloseName, CloseNameLocBegin);
320 Block->setLines(Lines);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000321}
322
323VerbatimLineComment *Sema::actOnVerbatimLine(SourceLocation LocBegin,
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000324 unsigned CommandID,
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000325 SourceLocation TextBegin,
326 StringRef Text) {
327 return new (Allocator) VerbatimLineComment(
328 LocBegin,
329 TextBegin.getLocWithOffset(Text.size()),
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000330 CommandID,
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000331 TextBegin,
332 Text);
333}
334
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000335HTMLStartTagComment *Sema::actOnHTMLStartTagStart(SourceLocation LocBegin,
336 StringRef TagName) {
337 return new (Allocator) HTMLStartTagComment(LocBegin, TagName);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000338}
339
Dmitri Gribenko7d9b5112012-08-06 19:03:12 +0000340void Sema::actOnHTMLStartTagFinish(
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000341 HTMLStartTagComment *Tag,
342 ArrayRef<HTMLStartTagComment::Attribute> Attrs,
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000343 SourceLocation GreaterLoc,
344 bool IsSelfClosing) {
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000345 Tag->setAttrs(Attrs);
346 Tag->setGreaterLoc(GreaterLoc);
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000347 if (IsSelfClosing)
348 Tag->setSelfClosing();
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000349 else if (!isHTMLEndTagForbidden(Tag->getTagName()))
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000350 HTMLOpenTags.push_back(Tag);
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000351}
352
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000353HTMLEndTagComment *Sema::actOnHTMLEndTag(SourceLocation LocBegin,
354 SourceLocation LocEnd,
355 StringRef TagName) {
356 HTMLEndTagComment *HET =
357 new (Allocator) HTMLEndTagComment(LocBegin, LocEnd, TagName);
358 if (isHTMLEndTagForbidden(TagName)) {
359 Diag(HET->getLocation(), diag::warn_doc_html_end_forbidden)
360 << TagName << HET->getSourceRange();
361 return HET;
Dmitri Gribenko3d986982012-07-12 23:37:09 +0000362 }
363
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000364 bool FoundOpen = false;
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000365 for (SmallVectorImpl<HTMLStartTagComment *>::const_reverse_iterator
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000366 I = HTMLOpenTags.rbegin(), E = HTMLOpenTags.rend();
367 I != E; ++I) {
368 if ((*I)->getTagName() == TagName) {
369 FoundOpen = true;
370 break;
371 }
372 }
373 if (!FoundOpen) {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000374 Diag(HET->getLocation(), diag::warn_doc_html_end_unbalanced)
375 << HET->getSourceRange();
376 return HET;
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000377 }
378
379 while (!HTMLOpenTags.empty()) {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000380 const HTMLStartTagComment *HST = HTMLOpenTags.back();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000381 HTMLOpenTags.pop_back();
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000382 StringRef LastNotClosedTagName = HST->getTagName();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000383 if (LastNotClosedTagName == TagName)
384 break;
385
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000386 if (isHTMLEndTagOptional(LastNotClosedTagName))
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000387 continue;
388
389 bool OpenLineInvalid;
390 const unsigned OpenLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000391 HST->getLocation(),
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000392 &OpenLineInvalid);
393 bool CloseLineInvalid;
394 const unsigned CloseLine = SourceMgr.getPresumedLineNumber(
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000395 HET->getLocation(),
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000396 &CloseLineInvalid);
397
398 if (OpenLineInvalid || CloseLineInvalid || OpenLine == CloseLine)
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000399 Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
400 << HST->getTagName() << HET->getTagName()
401 << HST->getSourceRange() << HET->getSourceRange();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000402 else {
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000403 Diag(HST->getLocation(), diag::warn_doc_html_start_end_mismatch)
404 << HST->getTagName() << HET->getTagName()
405 << HST->getSourceRange();
406 Diag(HET->getLocation(), diag::note_doc_html_end_tag)
407 << HET->getSourceRange();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000408 }
409 }
410
Dmitri Gribenko3f38bf22012-07-13 00:44:24 +0000411 return HET;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000412}
413
414FullComment *Sema::actOnFullComment(
415 ArrayRef<BlockContentComment *> Blocks) {
Fariborz Jahanian749ace62012-10-11 23:52:50 +0000416 FullComment *FC = new (Allocator) FullComment(Blocks, ThisDeclInfo);
Dmitri Gribenko9edd2c82012-08-24 17:45:39 +0000417 resolveParamCommandIndexes(FC);
418 return FC;
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000419}
420
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000421void Sema::checkBlockCommandEmptyParagraph(BlockCommandComment *Command) {
Dmitri Gribenkoabcf0dc2012-09-13 20:36:01 +0000422 if (Traits.getCommandInfo(Command->getCommandID())->IsEmptyParagraphAllowed)
423 return;
424
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000425 ParagraphComment *Paragraph = Command->getParagraph();
426 if (Paragraph->isWhitespace()) {
427 SourceLocation DiagLoc;
Dmitri Gribenko0eaf69d2012-07-13 19:02:42 +0000428 if (Command->getNumArgs() > 0)
429 DiagLoc = Command->getArgRange(Command->getNumArgs() - 1).getEnd();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000430 if (!DiagLoc.isValid())
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000431 DiagLoc = Command->getCommandNameRange(Traits).getEnd();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000432 Diag(DiagLoc, diag::warn_doc_block_command_empty_paragraph)
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000433 << Command->getCommandName(Traits)
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000434 << Command->getSourceRange();
435 }
436}
437
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +0000438void Sema::checkReturnsCommand(const BlockCommandComment *Command) {
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000439 if (!Traits.getCommandInfo(Command->getCommandID())->IsReturnsCommand)
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +0000440 return;
441 if (isFunctionDecl()) {
442 if (ThisDeclInfo->ResultType->isVoidType()) {
443 unsigned DiagKind;
Fariborz Jahanianbf967be2012-10-10 18:34:52 +0000444 switch (ThisDeclInfo->CommentDecl->getKind()) {
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +0000445 default:
Dmitri Gribenko88815f32012-08-06 16:29:26 +0000446 if (ThisDeclInfo->IsObjCMethod)
447 DiagKind = 3;
448 else
449 DiagKind = 0;
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +0000450 break;
451 case Decl::CXXConstructor:
452 DiagKind = 1;
453 break;
454 case Decl::CXXDestructor:
455 DiagKind = 2;
456 break;
457 }
458 Diag(Command->getLocation(),
459 diag::warn_doc_returns_attached_to_a_void_function)
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000460 << Command->getCommandName(Traits)
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +0000461 << DiagKind
462 << Command->getSourceRange();
463 }
464 return;
465 }
466 Diag(Command->getLocation(),
467 diag::warn_doc_returns_not_attached_to_a_function_decl)
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000468 << Command->getCommandName(Traits)
Dmitri Gribenko89ab7d02012-08-03 21:15:32 +0000469 << Command->getSourceRange();
470}
471
Dmitri Gribenko9443c572012-08-06 17:08:27 +0000472void Sema::checkBlockCommandDuplicate(const BlockCommandComment *Command) {
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000473 const CommandInfo *Info = Traits.getCommandInfo(Command->getCommandID());
Dmitri Gribenko9443c572012-08-06 17:08:27 +0000474 const BlockCommandComment *PrevCommand = NULL;
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000475 if (Info->IsBriefCommand) {
Dmitri Gribenko9443c572012-08-06 17:08:27 +0000476 if (!BriefCommand) {
477 BriefCommand = Command;
478 return;
479 }
480 PrevCommand = BriefCommand;
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000481 } else if (Info->IsReturnsCommand) {
Dmitri Gribenko9443c572012-08-06 17:08:27 +0000482 if (!ReturnsCommand) {
483 ReturnsCommand = Command;
484 return;
485 }
486 PrevCommand = ReturnsCommand;
487 } else {
488 // We don't want to check this command for duplicates.
489 return;
490 }
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000491 StringRef CommandName = Command->getCommandName(Traits);
492 StringRef PrevCommandName = PrevCommand->getCommandName(Traits);
Dmitri Gribenko9443c572012-08-06 17:08:27 +0000493 Diag(Command->getLocation(), diag::warn_doc_block_command_duplicate)
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000494 << CommandName
Dmitri Gribenko9443c572012-08-06 17:08:27 +0000495 << Command->getSourceRange();
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000496 if (CommandName == PrevCommandName)
Dmitri Gribenko9443c572012-08-06 17:08:27 +0000497 Diag(PrevCommand->getLocation(), diag::note_doc_block_command_previous)
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000498 << PrevCommandName
499 << PrevCommand->getSourceRange();
Dmitri Gribenko9443c572012-08-06 17:08:27 +0000500 else
501 Diag(PrevCommand->getLocation(),
502 diag::note_doc_block_command_previous_alias)
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000503 << PrevCommandName
504 << CommandName;
Dmitri Gribenko9443c572012-08-06 17:08:27 +0000505}
506
Dmitri Gribenko0bd98382012-09-22 21:47:50 +0000507void Sema::checkDeprecatedCommand(const BlockCommandComment *Command) {
508 if (!Traits.getCommandInfo(Command->getCommandID())->IsDeprecatedCommand)
509 return;
510
Fariborz Jahanianbf967be2012-10-10 18:34:52 +0000511 const Decl *D = ThisDeclInfo->CommentDecl;
Dmitri Gribenko0bd98382012-09-22 21:47:50 +0000512 if (!D)
513 return;
514
515 if (D->hasAttr<DeprecatedAttr>() ||
516 D->hasAttr<AvailabilityAttr>() ||
517 D->hasAttr<UnavailableAttr>())
518 return;
519
520 Diag(Command->getLocation(),
521 diag::warn_doc_deprecated_not_sync)
522 << Command->getSourceRange();
523
524 // Try to emit a fixit with a deprecation attribute.
525 if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
526 // Don't emit a Fix-It for non-member function definitions. GCC does not
527 // accept attributes on them.
528 const DeclContext *Ctx = FD->getDeclContext();
529 if ((!Ctx || !Ctx->isRecord()) &&
530 FD->doesThisDeclarationHaveABody())
531 return;
532
Dmitri Gribenko19523542012-09-29 11:40:46 +0000533 StringRef AttributeSpelling = "__attribute__((deprecated))";
534 if (PP) {
535 TokenValue Tokens[] = {
536 tok::kw___attribute, tok::l_paren, tok::l_paren,
537 PP->getIdentifierInfo("deprecated"),
538 tok::r_paren, tok::r_paren
539 };
540 StringRef MacroName = PP->getLastMacroWithSpelling(FD->getLocation(),
541 Tokens);
542 if (!MacroName.empty())
543 AttributeSpelling = MacroName;
544 }
545
546 SmallString<64> TextToInsert(" ");
547 TextToInsert += AttributeSpelling;
Dmitri Gribenko0bd98382012-09-22 21:47:50 +0000548 Diag(FD->getLocEnd(),
549 diag::note_add_deprecation_attr)
550 << FixItHint::CreateInsertion(FD->getLocEnd().getLocWithOffset(1),
Dmitri Gribenko19523542012-09-29 11:40:46 +0000551 TextToInsert);
Dmitri Gribenko0bd98382012-09-22 21:47:50 +0000552 }
553}
554
Dmitri Gribenko9edd2c82012-08-24 17:45:39 +0000555void Sema::resolveParamCommandIndexes(const FullComment *FC) {
556 if (!isFunctionDecl()) {
557 // We already warned that \\param commands are not attached to a function
558 // decl.
559 return;
560 }
561
562 llvm::SmallVector<ParamCommandComment *, 8> UnresolvedParamCommands;
563
564 // Comment AST nodes that correspond to \c ParamVars for which we have
565 // found a \\param command or NULL if no documentation was found so far.
566 llvm::SmallVector<ParamCommandComment *, 8> ParamVarDocs;
567
568 ArrayRef<const ParmVarDecl *> ParamVars = getParamVars();
569 ParamVarDocs.resize(ParamVars.size(), NULL);
570
571 // First pass over all \\param commands: resolve all parameter names.
572 for (Comment::child_iterator I = FC->child_begin(), E = FC->child_end();
573 I != E; ++I) {
574 ParamCommandComment *PCC = dyn_cast<ParamCommandComment>(*I);
575 if (!PCC || !PCC->hasParamName())
576 continue;
Fariborz Jahanianbf967be2012-10-10 18:34:52 +0000577 StringRef ParamName = PCC->getParamName(0);
Dmitri Gribenko9edd2c82012-08-24 17:45:39 +0000578
579 // Check that referenced parameter name is in the function decl.
580 const unsigned ResolvedParamIndex = resolveParmVarReference(ParamName,
581 ParamVars);
582 if (ResolvedParamIndex == ParamCommandComment::InvalidParamIndex) {
583 UnresolvedParamCommands.push_back(PCC);
584 continue;
585 }
586 PCC->setParamIndex(ResolvedParamIndex);
587 if (ParamVarDocs[ResolvedParamIndex]) {
588 SourceRange ArgRange = PCC->getParamNameRange();
589 Diag(ArgRange.getBegin(), diag::warn_doc_param_duplicate)
590 << ParamName << ArgRange;
591 ParamCommandComment *PrevCommand = ParamVarDocs[ResolvedParamIndex];
592 Diag(PrevCommand->getLocation(), diag::note_doc_param_previous)
593 << PrevCommand->getParamNameRange();
594 }
595 ParamVarDocs[ResolvedParamIndex] = PCC;
596 }
597
598 // Find parameter declarations that have no corresponding \\param.
599 llvm::SmallVector<const ParmVarDecl *, 8> OrphanedParamDecls;
600 for (unsigned i = 0, e = ParamVarDocs.size(); i != e; ++i) {
601 if (!ParamVarDocs[i])
602 OrphanedParamDecls.push_back(ParamVars[i]);
603 }
604
605 // Second pass over unresolved \\param commands: do typo correction.
606 // Suggest corrections from a set of parameter declarations that have no
607 // corresponding \\param.
608 for (unsigned i = 0, e = UnresolvedParamCommands.size(); i != e; ++i) {
609 const ParamCommandComment *PCC = UnresolvedParamCommands[i];
610
611 SourceRange ArgRange = PCC->getParamNameRange();
Fariborz Jahanianbf967be2012-10-10 18:34:52 +0000612 StringRef ParamName = PCC->getParamName(0);
Dmitri Gribenko9edd2c82012-08-24 17:45:39 +0000613 Diag(ArgRange.getBegin(), diag::warn_doc_param_not_found)
614 << ParamName << ArgRange;
615
616 // All parameters documented -- can't suggest a correction.
617 if (OrphanedParamDecls.size() == 0)
618 continue;
619
620 unsigned CorrectedParamIndex = ParamCommandComment::InvalidParamIndex;
621 if (OrphanedParamDecls.size() == 1) {
622 // If one parameter is not documented then that parameter is the only
623 // possible suggestion.
624 CorrectedParamIndex = 0;
625 } else {
626 // Do typo correction.
627 CorrectedParamIndex = correctTypoInParmVarReference(ParamName,
628 OrphanedParamDecls);
629 }
630 if (CorrectedParamIndex != ParamCommandComment::InvalidParamIndex) {
631 const ParmVarDecl *CorrectedPVD = OrphanedParamDecls[CorrectedParamIndex];
632 if (const IdentifierInfo *CorrectedII = CorrectedPVD->getIdentifier())
633 Diag(ArgRange.getBegin(), diag::note_doc_param_name_suggestion)
634 << CorrectedII->getName()
635 << FixItHint::CreateReplacement(ArgRange, CorrectedII->getName());
636 }
637 }
638}
639
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000640bool Sema::isFunctionDecl() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000641 if (!ThisDeclInfo)
642 return false;
643 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko00c59f72012-07-24 20:58:46 +0000644 inspectThisDecl();
Dmitri Gribenkoaf19a6a2012-08-02 21:45:39 +0000645 return ThisDeclInfo->getKind() == DeclInfo::FunctionKind;
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000646}
647
Dmitri Gribenko04bf29e2012-08-06 21:31:15 +0000648bool Sema::isTemplateOrSpecialization() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000649 if (!ThisDeclInfo)
650 return false;
651 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000652 inspectThisDecl();
Dmitri Gribenko04bf29e2012-08-06 21:31:15 +0000653 return ThisDeclInfo->getTemplateKind() != DeclInfo::NotTemplate;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000654}
655
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000656ArrayRef<const ParmVarDecl *> Sema::getParamVars() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000657 if (!ThisDeclInfo->IsFilled)
Dmitri Gribenko00c59f72012-07-24 20:58:46 +0000658 inspectThisDecl();
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000659 return ThisDeclInfo->ParamVars;
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000660}
661
662void Sema::inspectThisDecl() {
Dmitri Gribenko1ca7ecc2012-08-01 23:08:09 +0000663 ThisDeclInfo->fill();
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000664}
665
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000666unsigned Sema::resolveParmVarReference(StringRef Name,
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000667 ArrayRef<const ParmVarDecl *> ParamVars) {
668 for (unsigned i = 0, e = ParamVars.size(); i != e; ++i) {
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000669 const IdentifierInfo *II = ParamVars[i]->getIdentifier();
670 if (II && II->getName() == Name)
671 return i;
672 }
673 return ParamCommandComment::InvalidParamIndex;
674}
675
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000676namespace {
677class SimpleTypoCorrector {
678 StringRef Typo;
679 const unsigned MaxEditDistance;
680
681 const NamedDecl *BestDecl;
682 unsigned BestEditDistance;
683 unsigned BestIndex;
684 unsigned NextIndex;
685
686public:
687 SimpleTypoCorrector(StringRef Typo) :
688 Typo(Typo), MaxEditDistance((Typo.size() + 2) / 3),
689 BestDecl(NULL), BestEditDistance(MaxEditDistance + 1),
690 BestIndex(0), NextIndex(0)
691 { }
692
693 void addDecl(const NamedDecl *ND);
694
695 const NamedDecl *getBestDecl() const {
696 if (BestEditDistance > MaxEditDistance)
697 return NULL;
698
699 return BestDecl;
700 }
701
702 unsigned getBestDeclIndex() const {
703 assert(getBestDecl());
704 return BestIndex;
705 }
706};
707
708void SimpleTypoCorrector::addDecl(const NamedDecl *ND) {
709 unsigned CurrIndex = NextIndex++;
710
711 const IdentifierInfo *II = ND->getIdentifier();
712 if (!II)
713 return;
714
715 StringRef Name = II->getName();
716 unsigned MinPossibleEditDistance = abs((int)Name.size() - (int)Typo.size());
717 if (MinPossibleEditDistance > 0 &&
718 Typo.size() / MinPossibleEditDistance < 3)
719 return;
720
721 unsigned EditDistance = Typo.edit_distance(Name, true, MaxEditDistance);
722 if (EditDistance < BestEditDistance) {
723 BestEditDistance = EditDistance;
724 BestDecl = ND;
725 BestIndex = CurrIndex;
726 }
727}
728} // unnamed namespace
729
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000730unsigned Sema::correctTypoInParmVarReference(
731 StringRef Typo,
Dmitri Gribenko8487c522012-07-23 17:40:30 +0000732 ArrayRef<const ParmVarDecl *> ParamVars) {
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000733 SimpleTypoCorrector Corrector(Typo);
734 for (unsigned i = 0, e = ParamVars.size(); i != e; ++i)
735 Corrector.addDecl(ParamVars[i]);
736 if (Corrector.getBestDecl())
737 return Corrector.getBestDeclIndex();
738 else
Dmitri Gribenko1ad23d62012-09-10 21:20:09 +0000739 return ParamCommandComment::InvalidParamIndex;
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000740}
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000741
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000742namespace {
743bool ResolveTParamReferenceHelper(
744 StringRef Name,
745 const TemplateParameterList *TemplateParameters,
746 SmallVectorImpl<unsigned> *Position) {
747 for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
748 const NamedDecl *Param = TemplateParameters->getParam(i);
749 const IdentifierInfo *II = Param->getIdentifier();
750 if (II && II->getName() == Name) {
751 Position->push_back(i);
752 return true;
753 }
754
755 if (const TemplateTemplateParmDecl *TTP =
756 dyn_cast<TemplateTemplateParmDecl>(Param)) {
757 Position->push_back(i);
758 if (ResolveTParamReferenceHelper(Name, TTP->getTemplateParameters(),
759 Position))
760 return true;
761 Position->pop_back();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000762 }
763 }
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000764 return false;
765}
766} // unnamed namespace
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000767
Dmitri Gribenko96b09862012-07-31 22:37:06 +0000768bool Sema::resolveTParamReference(
769 StringRef Name,
770 const TemplateParameterList *TemplateParameters,
771 SmallVectorImpl<unsigned> *Position) {
772 Position->clear();
773 if (!TemplateParameters)
774 return false;
775
776 return ResolveTParamReferenceHelper(Name, TemplateParameters, Position);
777}
778
779namespace {
780void CorrectTypoInTParamReferenceHelper(
781 const TemplateParameterList *TemplateParameters,
782 SimpleTypoCorrector &Corrector) {
783 for (unsigned i = 0, e = TemplateParameters->size(); i != e; ++i) {
784 const NamedDecl *Param = TemplateParameters->getParam(i);
785 Corrector.addDecl(Param);
786
787 if (const TemplateTemplateParmDecl *TTP =
788 dyn_cast<TemplateTemplateParmDecl>(Param))
789 CorrectTypoInTParamReferenceHelper(TTP->getTemplateParameters(),
790 Corrector);
791 }
792}
793} // unnamed namespace
794
795StringRef Sema::correctTypoInTParamReference(
796 StringRef Typo,
797 const TemplateParameterList *TemplateParameters) {
798 SimpleTypoCorrector Corrector(Typo);
799 CorrectTypoInTParamReferenceHelper(TemplateParameters, Corrector);
800 if (const NamedDecl *ND = Corrector.getBestDecl()) {
801 const IdentifierInfo *II = ND->getIdentifier();
802 assert(II && "SimpleTypoCorrector should not return this decl");
803 return II->getName();
804 }
805 return StringRef();
Dmitri Gribenkoa5ef44f2012-07-11 21:38:39 +0000806}
807
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000808InlineCommandComment::RenderKind
809Sema::getInlineCommandRenderKind(StringRef Name) const {
Dmitri Gribenkoe4330a32012-09-10 20:32:42 +0000810 assert(Traits.getCommandInfo(Name)->IsInlineCommand);
Dmitri Gribenko2d66a502012-07-23 16:43:01 +0000811
812 return llvm::StringSwitch<InlineCommandComment::RenderKind>(Name)
813 .Case("b", InlineCommandComment::RenderBold)
814 .Cases("c", "p", InlineCommandComment::RenderMonospaced)
815 .Cases("a", "e", "em", InlineCommandComment::RenderEmphasized)
816 .Default(InlineCommandComment::RenderNormal);
817}
818
Dmitri Gribenko8d3ba232012-07-06 00:28:32 +0000819} // end namespace comments
820} // end namespace clang
821