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