clang 20.0.0git
CommentSema.cpp
Go to the documentation of this file.
1//===--- CommentSema.cpp - Doxygen comment semantic analysis --------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
10#include "clang/AST/Attr.h"
12#include "clang/AST/Decl.h"
15#include "clang/Basic/LLVM.h"
18#include "llvm/ADT/StringSwitch.h"
19
20namespace clang {
21namespace comments {
22
23namespace {
24#include "clang/AST/CommentHTMLTagsProperties.inc"
25} // end anonymous namespace
26
27Sema::Sema(llvm::BumpPtrAllocator &Allocator, const SourceManager &SourceMgr,
28 DiagnosticsEngine &Diags, CommandTraits &Traits,
29 const Preprocessor *PP) :
30 Allocator(Allocator), SourceMgr(SourceMgr), Diags(Diags), Traits(Traits),
31 PP(PP), ThisDeclInfo(nullptr), BriefCommand(nullptr),
32 HeaderfileCommand(nullptr) {
33}
34
35void Sema::setDecl(const Decl *D) {
36 if (!D)
37 return;
38
39 ThisDeclInfo = new (Allocator) DeclInfo;
40 ThisDeclInfo->CommentDecl = D;
41 ThisDeclInfo->IsFilled = false;
42}
43
46 return new (Allocator) ParagraphComment(Content);
47}
48
50 SourceLocation LocBegin,
51 SourceLocation LocEnd,
52 unsigned CommandID,
53 CommandMarkerKind CommandMarker) {
54 BlockCommandComment *BC = new (Allocator) BlockCommandComment(LocBegin, LocEnd,
55 CommandID,
56 CommandMarker);
57 checkContainerDecl(BC);
58 return BC;
59}
60
63 Command->setArgs(Args);
64}
65
67 ParagraphComment *Paragraph) {
68 Command->setParagraph(Paragraph);
69 checkBlockCommandEmptyParagraph(Command);
70 checkBlockCommandDuplicate(Command);
71 if (ThisDeclInfo) {
72 // These checks only make sense if the comment is attached to a
73 // declaration.
74 checkReturnsCommand(Command);
75 checkDeprecatedCommand(Command);
76 }
77}
78
80 SourceLocation LocBegin,
81 SourceLocation LocEnd,
82 unsigned CommandID,
83 CommandMarkerKind CommandMarker) {
84 ParamCommandComment *Command =
85 new (Allocator) ParamCommandComment(LocBegin, LocEnd, CommandID,
86 CommandMarker);
87
88 if (!involvesFunctionType())
89 Diag(Command->getLocation(),
90 diag::warn_doc_param_not_attached_to_a_function_decl)
91 << CommandMarker
92 << Command->getCommandNameRange(Traits);
93
94 return Command;
95}
96
97void Sema::checkFunctionDeclVerbatimLine(const BlockCommandComment *Comment) {
98 const CommandInfo *Info = Traits.getCommandInfo(Comment->getCommandID());
100 return;
101
102 unsigned DiagSelect;
103 switch (Comment->getCommandID()) {
104 case CommandTraits::KCI_function:
105 DiagSelect = (!isAnyFunctionDecl() && !isFunctionTemplateDecl())? 1 : 0;
106 break;
107 case CommandTraits::KCI_functiongroup:
108 DiagSelect = (!isAnyFunctionDecl() && !isFunctionTemplateDecl())? 2 : 0;
109 break;
110 case CommandTraits::KCI_method:
111 DiagSelect = !isObjCMethodDecl() ? 3 : 0;
112 break;
113 case CommandTraits::KCI_methodgroup:
114 DiagSelect = !isObjCMethodDecl() ? 4 : 0;
115 break;
116 case CommandTraits::KCI_callback:
117 DiagSelect = !isFunctionPointerVarDecl() ? 5 : 0;
118 break;
119 default:
120 DiagSelect = 0;
121 break;
122 }
123 if (DiagSelect)
124 Diag(Comment->getLocation(), diag::warn_doc_function_method_decl_mismatch)
125 << Comment->getCommandMarker()
126 << (DiagSelect-1) << (DiagSelect-1)
127 << Comment->getSourceRange();
128}
129
130void Sema::checkContainerDeclVerbatimLine(const BlockCommandComment *Comment) {
131 const CommandInfo *Info = Traits.getCommandInfo(Comment->getCommandID());
132 if (!Info->IsRecordLikeDeclarationCommand)
133 return;
134 unsigned DiagSelect;
135 switch (Comment->getCommandID()) {
136 case CommandTraits::KCI_class:
137 DiagSelect =
138 (!isClassOrStructOrTagTypedefDecl() && !isClassTemplateDecl()) ? 1
139 : 0;
140 // Allow @class command on @interface declarations.
141 // FIXME. Currently, \class and @class are indistinguishable. So,
142 // \class is also allowed on an @interface declaration
143 if (DiagSelect && Comment->getCommandMarker() && isObjCInterfaceDecl())
144 DiagSelect = 0;
145 break;
146 case CommandTraits::KCI_interface:
147 DiagSelect = !isObjCInterfaceDecl() ? 2 : 0;
148 break;
149 case CommandTraits::KCI_protocol:
150 DiagSelect = !isObjCProtocolDecl() ? 3 : 0;
151 break;
152 case CommandTraits::KCI_struct:
153 DiagSelect = !isClassOrStructOrTagTypedefDecl() ? 4 : 0;
154 break;
155 case CommandTraits::KCI_union:
156 DiagSelect = !isUnionDecl() ? 5 : 0;
157 break;
158 default:
159 DiagSelect = 0;
160 break;
161 }
162 if (DiagSelect)
163 Diag(Comment->getLocation(), diag::warn_doc_api_container_decl_mismatch)
164 << Comment->getCommandMarker()
165 << (DiagSelect-1) << (DiagSelect-1)
166 << Comment->getSourceRange();
167}
168
169void Sema::checkContainerDecl(const BlockCommandComment *Comment) {
170 const CommandInfo *Info = Traits.getCommandInfo(Comment->getCommandID());
171 if (!Info->IsRecordLikeDetailCommand || isRecordLikeDecl())
172 return;
173 unsigned DiagSelect;
174 switch (Comment->getCommandID()) {
175 case CommandTraits::KCI_classdesign:
176 DiagSelect = 1;
177 break;
178 case CommandTraits::KCI_coclass:
179 DiagSelect = 2;
180 break;
181 case CommandTraits::KCI_dependency:
182 DiagSelect = 3;
183 break;
184 case CommandTraits::KCI_helper:
185 DiagSelect = 4;
186 break;
187 case CommandTraits::KCI_helperclass:
188 DiagSelect = 5;
189 break;
190 case CommandTraits::KCI_helps:
191 DiagSelect = 6;
192 break;
193 case CommandTraits::KCI_instancesize:
194 DiagSelect = 7;
195 break;
196 case CommandTraits::KCI_ownership:
197 DiagSelect = 8;
198 break;
199 case CommandTraits::KCI_performance:
200 DiagSelect = 9;
201 break;
202 case CommandTraits::KCI_security:
203 DiagSelect = 10;
204 break;
205 case CommandTraits::KCI_superclass:
206 DiagSelect = 11;
207 break;
208 default:
209 DiagSelect = 0;
210 break;
211 }
212 if (DiagSelect)
213 Diag(Comment->getLocation(), diag::warn_doc_container_decl_mismatch)
214 << Comment->getCommandMarker()
215 << (DiagSelect-1)
216 << Comment->getSourceRange();
217}
218
219/// Turn a string into the corresponding PassDirection or -1 if it's not
220/// valid.
222 return llvm::StringSwitch<ParamCommandPassDirection>(Arg)
223 .Case("[in]", ParamCommandPassDirection::In)
224 .Case("[out]", ParamCommandPassDirection::Out)
225 .Cases("[in,out]", "[out,in]", ParamCommandPassDirection::InOut)
226 .Default(static_cast<ParamCommandPassDirection>(-1));
227}
228
230 SourceLocation ArgLocBegin,
231 SourceLocation ArgLocEnd,
232 StringRef Arg) {
233 std::string ArgLower = Arg.lower();
235
236 if (Direction == static_cast<ParamCommandPassDirection>(-1)) {
237 // Try again with whitespace removed.
238 llvm::erase_if(ArgLower, clang::isWhitespace);
239 Direction = getParamPassDirection(ArgLower);
240
241 SourceRange ArgRange(ArgLocBegin, ArgLocEnd);
242 if (Direction != static_cast<ParamCommandPassDirection>(-1)) {
243 const char *FixedName =
245 Diag(ArgLocBegin, diag::warn_doc_param_spaces_in_direction)
246 << ArgRange << FixItHint::CreateReplacement(ArgRange, FixedName);
247 } else {
248 Diag(ArgLocBegin, diag::warn_doc_param_invalid_direction) << ArgRange;
249 Direction = ParamCommandPassDirection::In; // Sane fall back.
250 }
251 }
252 Command->setDirection(Direction,
253 /*Explicit=*/true);
254}
255
257 SourceLocation ArgLocBegin,
258 SourceLocation ArgLocEnd,
259 StringRef Arg) {
260 // Parser will not feed us more arguments than needed.
261 assert(Command->getNumArgs() == 0);
262
263 if (!Command->isDirectionExplicit()) {
264 // User didn't provide a direction argument.
266 /* Explicit = */ false);
267 }
268 auto *A = new (Allocator)
269 Comment::Argument{SourceRange(ArgLocBegin, ArgLocEnd), Arg};
270 Command->setArgs(llvm::ArrayRef(A, 1));
271}
272
274 ParagraphComment *Paragraph) {
275 Command->setParagraph(Paragraph);
276 checkBlockCommandEmptyParagraph(Command);
277}
278
280 SourceLocation LocBegin,
281 SourceLocation LocEnd,
282 unsigned CommandID,
283 CommandMarkerKind CommandMarker) {
284 TParamCommandComment *Command =
285 new (Allocator) TParamCommandComment(LocBegin, LocEnd, CommandID,
286 CommandMarker);
287
288 if (!isTemplateOrSpecialization())