Cppcheck
checksizeof.cpp
Go to the documentation of this file.
1 /*
2  * Cppcheck - A tool for static C/C++ code analysis
3  * Copyright (C) 2007-2024 Cppcheck team.
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 
20 //---------------------------------------------------------------------------
21 #include "checksizeof.h"
22 
23 #include "errortypes.h"
24 #include "library.h"
25 #include "settings.h"
26 #include "symboldatabase.h"
27 #include "token.h"
28 #include "tokenize.h"
29 
30 #include <algorithm>
31 #include <iterator>
32 #include <map>
33 #include <vector>
34 
35 //---------------------------------------------------------------------------
36 
37 // Register this check class (by creating a static instance of it)
38 namespace {
39  CheckSizeof instance;
40 }
41 
42 // CWE IDs used:
43 static const CWE CWE467(467U); // Use of sizeof() on a Pointer Type
44 static const CWE CWE682(682U); // Incorrect Calculation
45 //---------------------------------------------------------------------------
46 //---------------------------------------------------------------------------
48 {
50  return;
51 
52  logChecker("CheckSizeof::checkSizeofForNumericParameter"); // warning
53 
54  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
55  for (const Scope * scope : symbolDatabase->functionScopes) {
56  for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
57  if (Token::Match(tok, "sizeof ( %num% )") ||
58  Token::Match(tok, "sizeof %num%")) {
60  }
61  }
62  }
63 }
64 
66 {
68  "sizeofwithnumericparameter", "Suspicious usage of 'sizeof' with a numeric constant as parameter.\n"
69  "It is unusual to use a constant value with sizeof. For example, 'sizeof(10)'"
70  " returns 4 (in 32-bit systems) or 8 (in 64-bit systems) instead of 10. 'sizeof('A')'"
71  " and 'sizeof(char)' can return different results.", CWE682, Certainty::normal);
72 }
73 
74 
75 //---------------------------------------------------------------------------
76 //---------------------------------------------------------------------------
78 {
80  return;
81 
82  logChecker("CheckSizeof::checkSizeofForArrayParameter"); // warning
83 
84  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
85  for (const Scope * scope : symbolDatabase->functionScopes) {
86  for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
87  if (Token::Match(tok, "sizeof ( %var% )") ||
88  Token::Match(tok, "sizeof %var% !![")) {
89  const Token* varTok = tok->next();
90  if (varTok->str() == "(") {
91  varTok = varTok->next();
92  }
93 
94  const Variable *var = varTok->variable();
95  if (var && var->isArray() && var->isArgument() && !var->isReference())
97  }
98  }
99  }
100 }
101 
103 {
105  "sizeofwithsilentarraypointer", "Using 'sizeof' on array given as function argument "
106  "returns size of a pointer.\n"
107  "Using 'sizeof' for array given as function argument returns the size of a pointer. "
108  "It does not return the size of the whole array in bytes as might be "
109  "expected. For example, this code:\n"
110  " int f(char a[100]) {\n"
111  " return sizeof(a);\n"
112  " }\n"
113  "returns 4 (in 32-bit systems) or 8 (in 64-bit systems) instead of 100 (the "
114  "size of the array in bytes).", CWE467, Certainty::normal
115  );
116 }
117 
119 {
121  return;
122 
123  logChecker("CheckSizeof::checkSizeofForPointerSize"); // warning
124 
125  const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();
126  for (const Scope * scope : symbolDatabase->functionScopes) {
127  for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
128  const Token* tokSize;
129  const Token* tokFunc;
130  const Token *variable = nullptr;
131  const Token *variable2 = nullptr;
132 
133  // Find any function that may use sizeof on a pointer
134  // Once leaving those tests, it is mandatory to have:
135  // - variable matching the used pointer
136  // - tokVar pointing on the argument where sizeof may be used
137  if (Token::Match(tok->tokAt(2), "%name% (") && mSettings->library.getAllocFuncInfo(tok->tokAt(2))) {
138  if (Token::Match(tok, "%var% ="))
139  variable = tok;
140  else if (tok->strAt(1) == ")" && Token::Match(tok->linkAt(1)->tokAt(-2), "%var% ="))
141  variable = tok->linkAt(1)->tokAt(-2);
142  else if (tok->link() && Token::Match(tok, "> ( %name% (") && mSettings->library.getAllocFuncInfo(tok->tokAt(2)) && Token::Match(tok->link()->tokAt(-3), "%var% ="))
143  variable = tok->link()->tokAt(-3);
144  tokSize = tok->tokAt(4);
145  tokFunc = tok->tokAt(2);
146  } else if (Token::simpleMatch(tok, "memset (") && tok->strAt(-1) != ".") {
147  variable = tok->tokAt(2);
148  tokSize = variable->nextArgument();
149  if (tokSize)
150  tokSize = tokSize->nextArgument();
151  tokFunc = tok;
152  } else if (Token::Match(tok, "memcpy|memcmp|memmove|strncpy|strncmp|strncat (") && tok->strAt(-1) != ".") {
153  variable = tok->tokAt(2);
154  variable2 = variable->nextArgument();
155  if (!variable2)
156  continue;
157  tokSize = variable2->nextArgument();
158  tokFunc = tok;
159  } else {
160  continue;
161  }
162 
163  if (tokSize && tokFunc->str() == "calloc")
164  tokSize = tokSize->nextArgument();
165 
166  if (tokSize) {
167  const Token * const paramsListEndTok = tokFunc->linkAt(1);
168  for (const Token* tok2 = tokSize; tok2 != paramsListEndTok; tok2 = tok2->next()) {
169  if (Token::simpleMatch(tok2, "/ sizeof")) {
170  // Allow division with sizeof(char)
171  if (Token::simpleMatch(tok2->next(), "sizeof (")) {
172  const Token *sztok = tok2->tokAt(2)->astOperand2();
173  const ValueType *vt = ((sztok != nullptr) ? sztok->valueType() : nullptr);
174  if (vt && vt->type == ValueType::CHAR && vt->pointer == 0)
175  continue;
176  }
177  auto hasMultiplication = [](const Token* parTok) -> bool {
178  while (parTok) { // Allow division if followed by multiplication
179  if (parTok->isArithmeticalOp() && parTok->str() == "*") {
180  const Token* szToks[] = { parTok->astOperand1(), parTok->astOperand2() };
181  if (std::any_of(std::begin(szToks), std::end(szToks), [](const Token* szTok) {
182  return Token::simpleMatch(szTok, "(") && Token::simpleMatch(szTok->previous(), "sizeof");
183  }))
184  return true;
185  }
186  parTok = parTok->astParent();
187  }
188  return false;
189  };
190  if (hasMultiplication(tok2->astParent()))
191  continue;
192 
193  divideBySizeofError(tok2, tokFunc->str());
194  }
195  }
196  }
197 
198  if (!variable || !tokSize)
199  continue;
200 
201  while (Token::Match(variable, "%var% ::|."))
202  variable = variable->tokAt(2);
203 
204  while (Token::Match(variable2, "%var% ::|."))
205  variable2 = variable2->tokAt(2);
206 
207  if (!variable)
208  continue;
209 
210  // Ensure the variables are in the symbol database
211  // Also ensure the variables are pointers
212  // Only keep variables which are pointers
213  const Variable *var = variable->variable();
214  if (!var || !var->isPointer() || var->isArray()) {
215  variable = nullptr;
216  }
217 
218  if (variable2) {
219  var = variable2->variable();
220  if (!var || !var->isPointer() || var->isArray()) {
221  variable2 = nullptr;
222  }
223  }
224 
225  // If there are no pointer variable at this point, there is
226  // no need to continue
227  if (variable == nullptr && variable2 == nullptr) {
228  continue;
229  }
230 
231  // Jump to the next sizeof token in the function and in the parameter
232  // This is to allow generic operations with sizeof
233  for (; tokSize && tokSize->str() != ")" && tokSize->str() != "," && tokSize->str() != "sizeof"; tokSize = tokSize->next()) {}
234 
235  if (tokSize->str() != "sizeof")
236  continue;
237 
238  // Now check for the sizeof usage: Does the level of pointer indirection match?
239  const Token * const tokLink = tokSize->linkAt(1);
240  if (tokLink && tokLink->strAt(-1) == "*") {
241  if (variable && variable->valueType() && variable->valueType()->pointer == 1 && variable->valueType()->type != ValueType::VOID)
242  sizeofForPointerError(variable, variable->str());
243  else if (variable2 && variable2->valueType() && variable2->valueType()->pointer == 1 && variable2->valueType()->type != ValueType::VOID)
244  sizeofForPointerError(variable2, variable2->str());
245  }
246 
247  if (Token::simpleMatch(tokSize, "sizeof ( &"))
248  tokSize = tokSize->tokAt(3);
249  else if (Token::Match(tokSize, "sizeof (|&"))
250  tokSize = tokSize->tokAt(2);
251  else
252  tokSize = tokSize->next();
253 
254  while (Token::Match(tokSize, "%var% ::|."))
255  tokSize = tokSize->tokAt(2);
256 
257  if (Token::Match(tokSize, "%var% [|("))
258  continue;
259 
260  // Now check for the sizeof usage again. Once here, everything using sizeof(varid) or sizeof(&varid)
261  // looks suspicious
262  if (variable && tokSize->varId() == variable->varId())
263  sizeofForPointerError(variable, variable->str());
264  if (variable2 && tokSize->varId() == variable2->varId())
265  sizeofForPointerError(variable2, variable2->str());
266  }
267  }
268 }
269 
270 void CheckSizeof::sizeofForPointerError(const Token *tok, const std::string &varname)
271 {
272  reportError(tok, Severity::warning, "pointerSize",
273  "Size of pointer '" + varname + "' used instead of size of its data.\n"
274  "Size of pointer '" + varname + "' used instead of size of its data. "
275  "This is likely to lead to a buffer overflow. You probably intend to "
276  "write 'sizeof(*" + varname + ")'.", CWE467, Certainty::normal);
277 }
278 
279 void CheckSizeof::divideBySizeofError(const Token *tok, const std::string &memfunc)
280 {
281  reportError(tok, Severity::warning, "sizeofDivisionMemfunc",
282  "Division by result of sizeof(). " + memfunc + "() expects a size in bytes, did you intend to multiply instead?", CWE682, Certainty::normal);
283 }
284 
285 //-----------------------------------------------------------------------------
286 //-----------------------------------------------------------------------------
288 {
290  return;
291 
292  logChecker("CheckSizeof::sizeofsizeof"); // warning
293 
294  for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {
295  if (Token::Match(tok, "sizeof (| sizeof")) {
296  sizeofsizeofError(tok);
297  tok = tok->next();
298  }
299  }
300 }
301 
303 {
305  "sizeofsizeof", "Calling 'sizeof' on 'sizeof'.\n"
306  "Calling sizeof for 'sizeof looks like a suspicious code and "
307  "most likely there should be just one 'sizeof'. The current "
308  "code is equivalent to 'sizeof(size_t)'", CWE682, Certainty::normal);
309 }
310 
311 //-----------------------------------------------------------------------------
312 
314 {
316  return;
317 
318  logChecker("CheckSizeof::sizeofCalculation"); // warning
319 
320  const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive);
321 
322  for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {
323  if (!Token::simpleMatch(tok, "sizeof ("))
324  continue;
325 
326  // ignore if the `sizeof` result is cast to void inside a macro, i.e. the calculation is
327  // expected to be parsed but skipped, such as in a disabled custom ASSERT() macro
328  if (tok->isExpandedMacro() && tok->previous()) {
329  const Token *cast_end = (tok->previous()->str() == "(") ? tok->previous() : tok;
330  if (Token::simpleMatch(cast_end->tokAt(-3), "( void )") ||
331  Token::simpleMatch(cast_end->tokAt(-4), "static_cast < void >")) {
332  continue;
333  }
334  }
335 
336  const Token *argument = tok->next()->astOperand2();
337  if (!argument || !argument->isCalculation())
338  continue;
339 
340  bool inconclusive = false;
341  if (argument->isExpandedMacro())
342  inconclusive = true;
343  else if (tok->next()->isExpandedMacro())
344  inconclusive = true;
345 
346  if (!inconclusive || printInconclusive)
348  }
349 }
350 
351 void CheckSizeof::sizeofCalculationError(const Token *tok, bool inconclusive)
352 {
354  "sizeofCalculation", "Found calculation inside sizeof().", CWE682, inconclusive ? Certainty::inconclusive : Certainty::normal);
355 }
356 
357 //-----------------------------------------------------------------------------
358 
360 {
362  return;
363 
364  logChecker("CheckSizeof::sizeofFunction"); // warning
365 
366  for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {
367  if (Token::simpleMatch(tok, "sizeof (")) {
368 
369  // ignore if the `sizeof` result is cast to void inside a macro, i.e. the calculation is
370  // expected to be parsed but skipped, such as in a disabled custom ASSERT() macro
371  if (tok->isExpandedMacro() && tok->previous()) {
372  const Token *cast_end = (tok->previous()->str() == "(") ? tok->previous() : tok;
373  if (Token::simpleMatch(cast_end->tokAt(-3), "( void )") ||
374  Token::simpleMatch(cast_end->tokAt(-4), "static_cast < void >")) {
375  continue;
376  }
377  }
378 
379  if (const Token *argument = tok->next()->astOperand2()) {
380  const Token *checkToken = argument->previous();
381  if (checkToken->tokType() == Token::eName)
382  break;
383  const Function * fun = checkToken->function();
384  // Don't report error if the function is overloaded
385  if (fun && fun->nestedIn->functionMap.count(checkToken->str()) == 1) {
386  sizeofFunctionError(tok);
387  }
388  }
389  }
390  }
391 }
392 
394 {
396  "sizeofFunctionCall", "Found function call inside sizeof().", CWE682, Certainty::normal);
397 }
398 
399 //-----------------------------------------------------------------------------
400 // Check for code like sizeof()*sizeof() or sizeof(ptr)/value
401 //-----------------------------------------------------------------------------
403 {
405  return;
406 
407  logChecker("CheckSizeof::suspiciousSizeofCalculation"); // warning,inconclusive
408 
409  for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {
410  if (Token::simpleMatch(tok, "sizeof (")) {
411  const Token* lPar = tok->astParent();
412  if (lPar && lPar->str() == "(") {
413  const Token* const rPar = lPar->link();
414  const Token* varTok = lPar->astOperand2();
415  int derefCount = 0;
416  while (Token::Match(varTok, "[|*")) {
417  ++derefCount;
418  varTok = varTok->astOperand1();
419  }
420  if (lPar->astParent() && lPar->astParent()->str() == "/") {
421  const Variable* var = varTok ? varTok->variable() : nullptr;
422  if (var && var->isPointer() && !var->isArray() && !(var->valueType() && var->valueType()->pointer <= derefCount))
423  divideSizeofError(tok);
424  }
425  else if (Token::simpleMatch(rPar, ") * sizeof") && rPar->next()->astOperand1() == tok->next())
426  multiplySizeofError(tok);
427  }
428  }
429  }
430 }
431 
433 {
435  "multiplySizeof", "Multiplying sizeof() with sizeof() indicates a logic error.", CWE682, Certainty::inconclusive);
436 }
437 
439 {
441  "divideSizeof", "Division of result of sizeof() on pointer type.\n"
442  "Division of result of sizeof() on pointer type. sizeof() returns the size of the pointer, "
443  "not the size of the memory area it points to.", CWE682, Certainty::inconclusive);
444 }
445 
447 {
449  return;
450 
451  logChecker("CheckSizeof::sizeofVoid"); // portability
452 
453  for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {
454  if (Token::simpleMatch(tok, "sizeof ( void )")) {
455  sizeofVoidError(tok);
456  } else if (Token::simpleMatch(tok, "sizeof (") && tok->next()->astOperand2()) {
457  const ValueType *vt = tok->next()->astOperand2()->valueType();
458  if (vt && vt->type == ValueType::Type::VOID && vt->pointer == 0U)
459  sizeofDereferencedVoidPointerError(tok, tok->strAt(3));
460  } else if (tok->str() == "-") {
461  // only warn for: 'void *' - 'integral'
462  const ValueType *vt1 = tok->astOperand1() ? tok->astOperand1()->valueType() : nullptr;
463  const ValueType *vt2 = tok->astOperand2() ? tok->astOperand2()->valueType() : nullptr;
464  const bool op1IsvoidPointer = (vt1 && vt1->type == ValueType::Type::VOID && vt1->pointer == 1U);
465  const bool op2IsIntegral = (vt2 && vt2->isIntegral() && vt2->pointer == 0U);
466  if (op1IsvoidPointer && op2IsIntegral)
467  arithOperationsOnVoidPointerError(tok, tok->astOperand1()->expressionString(), vt1->str());
468  } else if (Token::Match(tok, "+|++|--|+=|-=")) { // Arithmetic operations on variable of type "void*"
469  const ValueType *vt1 = tok->astOperand1() ? tok->astOperand1()->valueType() : nullptr;
470  const ValueType *vt2 = tok->astOperand2() ? tok->astOperand2()->valueType() : nullptr;
471 
472  const bool voidpointer1 = (vt1 && vt1->type == ValueType::Type::VOID && vt1->pointer == 1U);
473  const bool voidpointer2 = (vt2 && vt2->type == ValueType::Type::VOID && vt2->pointer == 1U);
474 
475  if (voidpointer1)
476  arithOperationsOnVoidPointerError(tok, tok->astOperand1()->expressionString(), vt1->str());
477 
478  if (!tok->isAssignmentOp() && voidpointer2)
479  arithOperationsOnVoidPointerError(tok, tok->astOperand2()->expressionString(), vt2->str());
480  }
481  }
482 }
483 
485 {
486  const std::string message = "Behaviour of 'sizeof(void)' is not covered by the ISO C standard.";
487  const std::string verbose = message + " A value for 'sizeof(void)' is defined only as part of a GNU C extension, which defines 'sizeof(void)' to be 1.";
488  reportError(tok, Severity::portability, "sizeofVoid", message + "\n" + verbose, CWE682, Certainty::normal);
489 }
490 
491 void CheckSizeof::sizeofDereferencedVoidPointerError(const Token *tok, const std::string &varname)
492 {
493  const std::string message = "'*" + varname + "' is of type 'void', the behaviour of 'sizeof(void)' is not covered by the ISO C standard.";
494  const std::string verbose = message + " A value for 'sizeof(void)' is defined only as part of a GNU C extension, which defines 'sizeof(void)' to be 1.";
495  reportError(tok, Severity::portability, "sizeofDereferencedVoidPointer", message + "\n" + verbose, CWE682, Certainty::normal);
496 }
497 
498 void CheckSizeof::arithOperationsOnVoidPointerError(const Token* tok, const std::string &varname, const std::string &vartype)
499 {
500  const std::string message = "'$symbol' is of type '" + vartype + "'. When using void pointers in calculations, the behaviour is undefined.";
501  const std::string verbose = message + " Arithmetic operations on 'void *' is a GNU C extension, which defines the 'sizeof(void)' to be 1.";
502  reportError(tok, Severity::portability, "arithOperationsOnVoidPointer", "$symbol:" + varname + '\n' + message + '\n' + verbose, CWE467, Certainty::normal);
503 }
static const CWE CWE682(682U)
static const CWE CWE467(467U)
checks on usage of sizeof() operator
Definition: checksizeof.h:41
void sizeofVoid()
Check for using sizeof(void)
void sizeofFunction()
Check for function call inside sizeof
void sizeofDereferencedVoidPointerError(const Token *tok, const std::string &varname)
void sizeofForArrayParameterError(const Token *tok)
void sizeofVoidError(const Token *tok)
void checkSizeofForNumericParameter()
Check for using sizeof with numeric given as function argument
Definition: checksizeof.cpp:47
void checkSizeofForPointerSize()
Check for using sizeof of a variable when allocating it
void suspiciousSizeofCalculation()
Check for suspicious calculations with sizeof results
void sizeofsizeofError(const Token *tok)
void sizeofForNumericParameterError(const Token *tok)
Definition: checksizeof.cpp:65
void divideBySizeofError(const Token *tok, const std::string &memfunc)
void checkSizeofForArrayParameter()
Check for using sizeof with array given as function argument
Definition: checksizeof.cpp:77
void divideSizeofError(const Token *tok)
void sizeofsizeof()
Check for 'sizeof sizeof ..'
void multiplySizeofError(const Token *tok)
void sizeofFunctionError(const Token *tok)
void sizeofForPointerError(const Token *tok, const std::string &varname)
void arithOperationsOnVoidPointerError(const Token *tok, const std::string &varname, const std::string &vartype)
void sizeofCalculationError(const Token *tok, bool inconclusive)
void sizeofCalculation()
Check for calculations inside sizeof
void reportError(const Token *tok, const Severity severity, const std::string &id, const std::string &msg)
report an error
Definition: check.h:138
const Settings *const mSettings
Definition: check.h:134
const Tokenizer *const mTokenizer
Definition: check.h:133
void logChecker(const char id[])
log checker
Definition: check.cpp:129
const Scope * nestedIn
Scope the function is declared in.
const AllocFunc * getAllocFuncInfo(const Token *tok) const
get allocation info for function
Definition: library.cpp:1077
std::multimap< std::string, const Function * > functionMap
const Token * bodyStart
'{' token
const Token * bodyEnd
'}' token
Library library
Library.
Definition: settings.h:237
SimpleEnableGroup< Certainty > certainty
Definition: settings.h:359
SimpleEnableGroup< Severity > severity
Definition: settings.h:358
bool isEnabled(T flag) const
Definition: settings.h:66
std::vector< const Scope * > functionScopes
Fast access to function scopes.
The token list that the TokenList generates is a linked-list of this class.
Definition: token.h:150
void str(T &&s)
Definition: token.h:179
static bool Match(const Token *tok, const char pattern[], nonneg int varid=0)
Match given token (or list of tokens) to a pattern list.
Definition: token.cpp:722
bool isExpandedMacro() const
Definition: token.h:455
const ValueType * valueType() const
Definition: token.h:331
const std::string & strAt(int index) const
Definition: token.cpp:457
void astOperand1(Token *tok)
Definition: token.cpp:1490
bool isCalculation() const
Is current token a calculation? Only true for operands.
Definition: token.cpp:1590
void function(const Function *f)
Associate this token with given function.
Definition: token.cpp:1127
nonneg int varId() const
Definition: token.h:870
const Token * tokAt(int index) const
Definition: token.cpp:427
Token::Type tokType() const
Definition: token.h:343
void astOperand2(Token *tok)
Definition: token.cpp:1502
void link(Token *linkToToken)
Create link to given token.
Definition: token.h:1015
const Token * linkAt(int index) const
Definition: token.cpp:447
@ eName
Definition: token.h:161
Token * previous()
Definition: token.h:862
void variable(const Variable *v)
Associate this token with given variable.
Definition: token.h:1070
Token * next()
Definition: token.h:830
const Token * nextArgument() const
Definition: token.cpp:903
static bool simpleMatch(const Token *tok, const char(&pattern)[count])
Match given token (or list of tokens) to a pattern list.
Definition: token.h:252
void astParent(Token *tok)
Definition: token.cpp:1471
const Token * tokens() const
Definition: tokenize.h:592
const SymbolDatabase * getSymbolDatabase() const
Definition: tokenize.h:563
Value type.
enum ValueType::Type type
bool isIntegral() const
nonneg int pointer
0=>not pointer, 1=>*, 2=>**, 3=>***, etc
std::string str() const
Information about a member variable.
bool isArgument() const
Is variable a function argument.
bool isReference() const
Is reference variable.
bool isArray() const
Is variable an array.
bool isPointer() const
Is pointer variable.
const ValueType * valueType() const
@ warning
Warning.
@ portability
Portability warning.