Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
parameters.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include "parameters.h"
5
6#include "codechunk.h"
7#include "generator.h"
8#include "tokenizer.h"
9
11
12QRegularExpression Parameters::s_varComment(R"(^/\*\s*([a-zA-Z_0-9]+)\s*\*/$)");
13
14/*!
15 \class Parameter
16 \brief The Parameter class describes one function parameter.
17
18 A parameter can be a function parameter or a macro parameter.
19 It has a name, a data type, and an optional default value.
20 These are all stored as strings so they can be compared with
21 a parameter in a function signature to find a match.
22 */
23
24/*!
25 \fn Parameter::Parameter(const QString &type, const QString &name, const QString &defaultValue)
26
27 Constructs the parameter from the \a type, the optional \a name,
28 and the optional \a defaultValue.
29 */
30
31/*!
32 Reconstructs the text signature for the parameter and returns
33 it. If \a includeValue is true and there is a default value,
34 the default value is appended with '='.
35 */
36QString Parameter::signature(bool includeValue) const
37{
38 QString p = m_type;
39 if (!p.isEmpty() && !p.endsWith(QChar('*')) && !p.endsWith(QChar('&')) &&
40 !p.endsWith(QChar(' ')) && !m_name.isEmpty()) {
41 p += QLatin1Char(' ');
42 }
43 p += m_name;
44 if (includeValue && !m_defaultValue.isEmpty())
45 p += " = " + m_defaultValue;
46 return p;
47}
48
49/*!
50 \class Parameters
51
52 \brief A class for parsing and managing a function parameter list
53
54 The constructor is passed a string that is the text inside the
55 parentheses of a function declaration. The constructor parses
56 the parameter list into a vector of class Parameter.
57
58 The Parameters object is then used in function searches to find
59 the correct function node given the function name and the signature
60 of its parameters.
61 */
62
63Parameters::Parameters() : m_valid(true), m_privateSignal(false), m_tok(0), m_tokenizer(nullptr)
64{
65 // nothing.
66}
67
68Parameters::Parameters(const QString &signature)
69 : m_valid(true), m_privateSignal(false), m_tok(0), m_tokenizer(nullptr)
70{
71 if (!signature.isEmpty()) {
72 if (!parse(signature)) {
73 m_parameters.clear();
74 m_valid = false;
75 }
76 }
77}
78
79/*!
80 Get the next token from the string being parsed and store
81 it in the token variable.
82 */
83void Parameters::readToken()
84{
85 m_tok = m_tokenizer->getToken();
86}
87
88/*!
89 Return the current lexeme from the string being parsed.
90 */
91QString Parameters::lexeme()
92{
93 return m_tokenizer->lexeme();
94}
95
96/*!
97 Return the previous lexeme read from the string being parsed.
98 */
99QString Parameters::previousLexeme()
100{
101 return m_tokenizer->previousLexeme();
102}
103
104/*!
105 If the current token is \a target, read the next token and
106 return \c true. Otherwise, return false without reading the
107 next token.
108 */
109bool Parameters::match(int target)
110{
111 if (m_tok == target) {
112 readToken();
113 return true;
114 }
115 return false;
116}
117
118/*!
119 Reads input inside angled brackets, appending tokens to the code chunk held
120 by \a type, taking nested brackets, parentheses and square brackets into
121 account. Square brackets and regular parentheses are not handled separately.
122
123 Processing begins if the first token is a left angle bracket and continues
124 until a corresponding right angle bracket is found, the end of input occurs,
125 or an excess of right parentheses or square brackets are found.
126
127 After processing, the current token is the first token after the closing
128 right angle bracket, the end of file token, or the mismatched right bracket
129 or parenthesis.
130 */
131void Parameters::matchTemplateAngles(CodeChunk &type)
132{
133 if (m_tok == Tok_LeftAngle) {
134 int leftAngleDepth = 0;
135 int parenAndBraceDepth = 0;
136 do {
137 if (m_tok == Tok_LeftAngle) {
138 leftAngleDepth++;
139 } else if (m_tok == Tok_RightAngle) {
140 leftAngleDepth--;
141 } else if (m_tok == Tok_LeftParen || m_tok == Tok_LeftBrace) {
142 ++parenAndBraceDepth;
143 } else if (m_tok == Tok_RightParen || m_tok == Tok_RightBrace) {
144 if (--parenAndBraceDepth < 0)
145 return;
146 }
147 type.append(lexeme());
148 readToken();
149 } while (leftAngleDepth > 0 && m_tok != Tok_Eoi);
150 }
151}
152
153/*!
154 Uses the current tokenizer to parse the \a name and \a type
155 of the parameter.
156 */
157bool Parameters::matchTypeAndName(CodeChunk &type, QString &name)
158{
159 /*
160 This code is really hard to follow... sorry. The loop is there to match
161 Alpha::Beta::Gamma::...::Omega.
162 */
163 for (;;) {
164 bool virgin = true;
165
166 if (m_tok != Tok_Ident) {
167 /*
168 There is special processing for 'Foo::operator int()'
169 and such elsewhere. This is the only case where we
170 return something with a trailing gulbrandsen ('Foo::').
171 */
172 if (m_tok == Tok_operator)
173 return true;
174
175 /*
176 People may write 'const unsigned short' or
177 'short unsigned const' or any other permutation.
178 */
179 while (match(Tok_const) || match(Tok_volatile))
180 type.append(previousLexeme());
181 QString pending;
182 while (m_tok == Tok_signed || m_tok == Tok_int || m_tok == Tok_unsigned
183 || m_tok == Tok_short || m_tok == Tok_long || m_tok == Tok_int64) {
184 if (m_tok == Tok_signed)
185 pending = lexeme();
186 else {
187 if (m_tok == Tok_unsigned && !pending.isEmpty())
188 type.append(pending);
189 pending.clear();
190 type.append(lexeme());
191 }
192 readToken();
193 virgin = false;
194 }
195 if (!pending.isEmpty()) {
196 type.append(pending);
197 pending.clear();
198 }
199 while (match(Tok_const) || match(Tok_volatile))
200 type.append(previousLexeme());
201
202 if (match(Tok_Tilde))
203 type.append(previousLexeme());
204 }
205
206 if (virgin) {
207 if (match(Tok_Ident)) {
208 /*
209 This is a hack until we replace this "parser"
210 with the real one used in Qt Creator.
211 Is it still needed? mws 11/12/2018
212 */
213 if (lexeme() == "("
214 && ((previousLexeme() == "QT_PREPEND_NAMESPACE")
215 || (previousLexeme() == "NS"))) {
216 readToken();
217 readToken();
218 type.append(previousLexeme());
219 readToken();
220 } else
221 type.append(previousLexeme());
222 } else if (match(Tok_void) || match(Tok_int) || match(Tok_char) || match(Tok_double)
223 || match(Tok_Ellipsis)) {
224 type.append(previousLexeme());
225 } else {
226 return false;
227 }
228 } else if (match(Tok_int) || match(Tok_char) || match(Tok_double)) {
229 type.append(previousLexeme());
230 }
231
232 matchTemplateAngles(type);
233
234 while (match(Tok_const) || match(Tok_volatile))
235 type.append(previousLexeme());
236
237 if (match(Tok_Gulbrandsen))
238 type.append(previousLexeme());
239 else
240 break;
241 }
242
243 while (match(Tok_Ampersand) || match(Tok_Aster) || match(Tok_const) || match(Tok_Caret)
244 || match(Tok_Ellipsis))
245 type.append(previousLexeme());
246
247 if (match(Tok_LeftParenAster)) {
248 /*
249 A function pointer. This would be rather hard to handle without a
250 tokenizer hack, because a type can be followed with a left parenthesis
251 in some cases (e.g., 'operator int()'). The tokenizer recognizes '(*'
252 as a single token.
253 */
254 type.append(" "); // force a space after the type
255 type.append(previousLexeme());
256 type.appendHotspot();
257 if (match(Tok_Ident))
258 name = previousLexeme();
259 if (!match(Tok_RightParen))
260 return false;
261 type.append(previousLexeme());
262 if (!match(Tok_LeftParen))
263 return false;
264 type.append(previousLexeme());
265
266 /* parse the parameters. Ignore the parameter name from the type */
267 while (m_tok != Tok_RightParen && m_tok != Tok_Eoi) {
268 QString dummy;
269 if (!matchTypeAndName(type, dummy))
270 return false;
271 if (match(Tok_Comma))
272 type.append(previousLexeme());
273 }
274 if (!match(Tok_RightParen))
275 return false;
276 type.append(previousLexeme());
277 } else {
278 /*
279 The common case: Look for an optional identifier, then for
280 some array brackets.
281 */
282 type.appendHotspot();
283
284 if (match(Tok_Ident)) {
285 name = previousLexeme();
286 } else if (match(Tok_Comment)) {
287 // Use a regular expression to extract any commented out parameter name.
288 auto match = s_varComment.match(previousLexeme());
289 if (match.hasMatch())
290 name = match.captured(1);
291 } else if (match(Tok_LeftParen)) {
292 name = "(";
293 while (m_tok != Tok_RightParen && m_tok != Tok_Eoi) {
294 name.append(lexeme());
295 readToken();
296 }
297 name.append(")");
298 readToken();
299 if (match(Tok_LeftBracket)) {
300 name.append("[");
301 while (m_tok != Tok_RightBracket && m_tok != Tok_Eoi) {
302 name.append(lexeme());
303 readToken();
304 }
305 name.append("]");
306 readToken();
307 }
308 }
309
310 if (m_tok == Tok_LeftBracket) {
311 int bracketDepth0 = m_tokenizer->bracketDepth();
312 while ((m_tokenizer->bracketDepth() >= bracketDepth0 && m_tok != Tok_Eoi)
313 || m_tok == Tok_RightBracket) {
314 type.append(lexeme());
315 readToken();
316 }
317 }
318 }
319 return true;
320}
321
322/*!
323 Parse the next function parameter, if there is one, and
324 append it to the internal parameter vector. Return true
325 if a parameter is parsed correctly. Otherwise return false.
326 */
327bool Parameters::matchParameter()
328{
329 if (match(Tok_QPrivateSignal)) {
330 m_privateSignal = true;
331 return true;
332 }
333
334 CodeChunk chunk;
335 QString name;
336 if (!matchTypeAndName(chunk, name))
337 return false;
338 QString type = chunk.toString();
339 QString defaultValue;
340 match(Tok_Comment);
341 if (match(Tok_Equal)) {
342 chunk.clear();
343 int pdepth = m_tokenizer->parenDepth();
344 while (m_tokenizer->parenDepth() >= pdepth
345 && (m_tok != Tok_Comma || (m_tokenizer->parenDepth() > pdepth))
346 && m_tok != Tok_Eoi) {
347 chunk.append(lexeme());
348 readToken();
349 }
350 defaultValue = chunk.toString();
351 }
352 append(type, name, defaultValue);
353 return true;
354}
355
356/*!
357 This function uses a Tokenizer to parse the \a signature,
358 which is a comma-separated list of parameter declarations.
359 If an error is detected, the Parameters object is cleared
360 and \c false is returned. Otherwise \c true is returned.
361 */
362bool Parameters::parse(const QString &signature)
363{
364 Tokenizer *outerTokenizer = m_tokenizer;
365 int outerTok = m_tok;
366
367 const QByteArray &latin1 = signature.toLatin1();
368 Tokenizer stringTokenizer(Location(), latin1);
369 stringTokenizer.setParsingFnOrMacro(true);
370 m_tokenizer = &stringTokenizer;
371
372 readToken();
373 do {
374 if (!matchParameter()) {
375 m_parameters.clear();
376 m_valid = false;
377 break;
378 }
379 } while (match(Tok_Comma));
380
381 m_tokenizer = outerTokenizer;
382 m_tok = outerTok;
383 return m_valid;
384}
385
386/*!
387 Append a Parameter constructed from \a type, \a name, and \a value
388 to the parameter vector.
389 */
390void Parameters::append(const QString &type, const QString &name, const QString &value)
391{
392 m_parameters.append(Parameter(type, name, value));
393}
394
395/*!
396 Returns the list of reconstructed parameters. If \a includeValues
397 is true, the default values are included, if any are present.
398 */
399QString Parameters::signature(bool includeValues) const
400{
401 QString result;
402 if (!m_parameters.empty()) {
403 for (int i = 0; i < m_parameters.size(); i++) {
404 if (i > 0)
405 result += ", ";
406 result += m_parameters.at(i).signature(includeValues);
407 }
408 }
409 return result;
410}
411
412/*!
413 Returns the signature of all the parameters with all the
414 spaces and commas removed. It is unintelligible, but that
415 is what the caller wants.
416
417 If \a names is true, the parameter names are included. If
418 \a values is true, the default values are included.
419 */
420QString Parameters::rawSignature(bool names, bool values) const
421{
422 QString raw;
423 const auto params = m_parameters;
424 for (const auto &parameter : params) {
425 raw += parameter.type();
426 if (names)
427 raw += parameter.name();
428 if (values)
429 raw += parameter.defaultValue();
430 }
431 return raw;
432}
433
434/*!
435 Parse the parameter \a signature by splitting the string,
436 and store the individual parameters in the parameter vector.
437
438 This method of parsing is naive but sufficient for QML methods
439 and macros.
440 */
441void Parameters::set(const QString &signature)
442{
443 clear();
444 if (!signature.isEmpty()) {
445 QStringList commaSplit = signature.split(',');
446 m_parameters.resize(commaSplit.size());
447 int i = 0;
448 for (const auto &item : std::as_const(commaSplit)) {
449 QStringList blankSplit = item.split(' ', Qt::SkipEmptyParts);
450 QString pDefault;
451 qsizetype defaultIdx = blankSplit.indexOf(QStringLiteral("="));
452 if (defaultIdx != -1) {
453 if (++defaultIdx < blankSplit.size())
454 pDefault = blankSplit.mid(defaultIdx).join(' ');
455 blankSplit = blankSplit.mid(0, defaultIdx - 1);
456 }
457 QString pName = blankSplit.takeLast();
458 QString pType = blankSplit.join(' ');
459 if (pType.isEmpty() && pName == QLatin1String("..."))
460 qSwap(pType, pName);
461 else {
462 int j = 0;
463 while (j < pName.size() && !pName.at(j).isLetter())
464 j++;
465 if (j > 0) {
466 pType += QChar(' ') + pName.left(j);
467 pName = pName.mid(j);
468 }
469 }
470 m_parameters[i++].set(pType, pName, pDefault);
471 }
472 }
473}
474
475/*!
476 Insert all the parameter names into names.
477 */
479{
480 QSet<QString> names;
481 const auto params = m_parameters;
482 for (const auto &parameter : params) {
483 if (!parameter.name().isEmpty())
484 names.insert(parameter.name());
485 }
486 return names;
487}
488
489/*!
490 Construct a list of the parameter types and return it.
491 */
493{
494 QString out;
495 if (count() > 0) {
496 for (int i = 0; i < count(); ++i) {
497 if (i > 0)
498 out += ", ";
499 out += m_parameters.at(i).type();
500 }
501 }
502 return out;
503}
504
505/*!
506 Construct a list of the parameter type/name pairs and
507 return it.
508*/
510{
511 QString out;
512 if (count() > 0) {
513 for (int i = 0; i < count(); ++i) {
514 if (i != 0)
515 out += ", ";
516 const Parameter &p = m_parameters.at(i);
517 out += p.type();
518 if (out[out.size() - 1].isLetterOrNumber())
519 out += QLatin1Char(' ');
520 out += p.name();
521 }
522 }
523 return out;
524}
525
526/*!
527 Returns true if \a parameters contains the same parameter
528 signature as this.
529 */
530bool Parameters::match(const Parameters &parameters) const
531{
532 if (count() != parameters.count())
533 return false;
534 if (count() == 0)
535 return true;
536 for (int i = 0; i < count(); i++) {
537 if (parameters.at(i).type() != m_parameters.at(i).type())
538 return false;
539 }
540 return true;
541}
542
543QT_END_NAMESPACE
The Location class provides a way to mark a location in a file.
Definition location.h:20
Location()
Constructs an empty location.
Definition location.cpp:46
The Parameter class describes one function parameter.
Definition parameters.h:20
QString signature(bool includeValue=false) const
Reconstructs the text signature for the parameter and returns it.
int getToken()
int parenDepth() const
Definition tokenizer.h:94
int bracketDepth() const
Definition tokenizer.h:95
void setParsingFnOrMacro(bool macro)
Definition tokenizer.h:88
A class for parsing and managing a function parameter list.
Definition main.cpp:28
QSet< QString > getNames() const
Insert all the parameter names into names.
bool match(const Parameters &parameters) const
Returns true if parameters contains the same parameter signature as this.
Parameters(const QString &signature)
QString rawSignature(bool names=false, bool values=false) const
Returns the signature of all the parameters with all the spaces and commas removed.
QString generateTypeList() const
Construct a list of the parameter types and return it.
void clear()
Definition parameters.h:62
const Parameter & at(int i) const
Definition parameters.h:74
QString signature(bool includeValues=false) const
Returns the list of reconstructed parameters.
int count() const
Definition parameters.h:72
QString generateTypeAndNameList() const
Construct a list of the parameter type/name pairs and return it.
void append(const QString &type, const QString &name, const QString &value)
Append a Parameter constructed from type, name, and value to the parameter vector.
void set(const QString &signature)
Parse the parameter signature by splitting the string, and store the individual parameters in the par...
@ Tok_operator
Definition tokenizer.h:57
@ Tok_Eoi
Definition tokenizer.h:24
@ Tok_void
Definition tokenizer.h:62
@ Tok_int64
Definition tokenizer.h:64
@ Tok_unsigned
Definition tokenizer.h:61
@ Tok_RightParen
Definition tokenizer.h:29
@ Tok_char
Definition tokenizer.h:51
@ Tok_short
Definition tokenizer.h:58
@ Tok_Ellipsis
Definition tokenizer.h:39
@ Tok_Gulbrandsen
Definition tokenizer.h:40
@ Tok_Equal
Definition tokenizer.h:31
@ Tok_int
Definition tokenizer.h:55
@ Tok_long
Definition tokenizer.h:56
@ Tok_LeftBracket
Definition tokenizer.h:41
@ Tok_LeftAngle
Definition tokenizer.h:36
@ Tok_Comma
Definition tokenizer.h:38
@ Tok_LeftBrace
Definition tokenizer.h:32
@ Tok_Ident
Definition tokenizer.h:49
@ Tok_LeftParenAster
Definition tokenizer.h:30
@ Tok_double
Definition tokenizer.h:54
@ Tok_LeftParen
Definition tokenizer.h:28
@ Tok_RightAngle
Definition tokenizer.h:37
@ Tok_RightBrace
Definition tokenizer.h:33
@ Tok_RightBracket
Definition tokenizer.h:42
@ Tok_signed
Definition tokenizer.h:59
@ Tok_Comment
Definition tokenizer.h:48
@ Tok_QPrivateSignal
Definition tokenizer.h:65