void CheckNullPointer::nullPointerByDeRefAndChec() { for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { const Variable *var = tok->variable(); if (!var || !var->isPointer() || tok == var->nameToken()) continue; // Can pointer be NULL? const ValueFlow::Value *value = tok->getValue(0); if (!value) continue; if (!_settings->inconclusive && value->inconclusive) continue; // Is pointer used as function parameter? if (Token::Match(tok->previous(), "[(,] %var% [,)]")) { const Token *ftok = tok->previous(); while (ftok && ftok->str() != "(") { if (ftok->str() == ")") ftok = ftok->link(); ftok = ftok->previous(); } if (!ftok || !ftok->previous()) continue; std::list<const Token *> varlist; parseFunctionCall(*ftok->previous(), varlist, &_settings->library, 0); if (std::find(varlist.begin(), varlist.end(), tok) != varlist.end()) { if (value->condition == nullptr) nullPointerError(tok, tok->str()); else if (_settings->isEnabled("warning")) nullPointerError(tok, tok->str(), value->condition, value->inconclusive); } continue; } // Pointer dereference. bool unknown = false; if (!isPointerDeRef(tok,unknown)) { if (_settings->inconclusive && unknown) { if (value->condition == nullptr) nullPointerError(tok, tok->str(), true); else nullPointerError(tok, tok->str(), value->condition, true); } continue; } if (value->condition == nullptr) nullPointerError(tok, tok->str(), value->inconclusive); else if (_settings->isEnabled("warning")) nullPointerError(tok, tok->str(), value->condition, value->inconclusive); } }
static bool parseValueExpression(MemoryStack* stack, LexerCarriage* carriage, Array<SyntaxError>* errors, ASTNode* result) { if (parseFunctionCall(stack, carriage, errors, result)) return true; else if (parseLValueExpression(stack, carriage, errors, result)) return true; else if (carriage->topToken.type == TOK_NUMBER_CONSTANT) { result->nodeType = AST_EXPRESSION_CONSTANT; result->constantValue = carriage->topToken.constant.value; parseNextToken(carriage); return true; } else return false; }
/** Dereferencing null constant (simplified token list) */ void CheckNullPointer::nullConstantDereference() { // this is kept at 0 for all scopes that are not executing unsigned int indentlevel = 0; for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { // start of executable scope.. if (indentlevel == 0 && Token::Match(tok, ") const| {")) indentlevel = 1; else if (indentlevel >= 1) { if (tok->str() == "{") ++indentlevel; else if (tok->str() == "}") { if (indentlevel <= 2) indentlevel = 0; else --indentlevel; } if (tok->str() == "(" && Token::Match(tok->previous(), "sizeof|decltype")) tok = tok->link(); else if (Token::simpleMatch(tok, "exit ( )")) { // Goto end of scope while (tok && tok->str() != "}") { if (tok->str() == "{") tok = tok->link(); tok = tok->next(); } if (!tok) break; } else if (Token::simpleMatch(tok, "* 0")) { if (Token::Match(tok->previous(), "return|;|{|}|=|(|,|%op%")) { nullPointerError(tok); } } else if (indentlevel > 0 && Token::Match(tok, "%var% (")) { std::list<const Token *> var; parseFunctionCall(*tok, var, 0); // is one of the var items a NULL pointer? for (std::list<const Token *>::const_iterator it = var.begin(); it != var.end(); ++it) { if ((*it)->str() == "0") { nullPointerError(*it); } } } } } }
/** Dereferencing null constant (simplified token list) */ void CheckNullPointer::nullConstantDereference() { const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); // THIS ARRAY MUST BE ORDERED ALPHABETICALLY static const char* const stl_stream[] = { "fstream", "ifstream", "iostream", "istream", "istringstream", "stringstream", "wistringstream", "wstringstream" }; const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; if (scope->function == 0 || !scope->function->hasBody) // We only look for functions with a body continue; const Token *tok = scope->classStart; if (scope->function && scope->function->isConstructor()) tok = scope->function->token; // Check initialization list for (; tok != scope->classEnd; tok = tok->next()) { if (Token::Match(tok, "sizeof|decltype|typeid|typeof (")) tok = tok->next()->link(); else if (Token::simpleMatch(tok, "* 0")) { if (Token::Match(tok->previous(), "return|throw|;|{|}|:|[|(|,") || tok->previous()->isOp()) { nullPointerError(tok); } } else if (Token::Match(tok, "0 [") && (tok->previous()->str() != "&" || !Token::Match(tok->next()->link()->next(), "[.(]"))) nullPointerError(tok); else if (Token::Match(tok->previous(), "!!. %var% (") && (tok->previous()->str() != "::" || tok->strAt(-2) == "std")) { if (Token::simpleMatch(tok->tokAt(2), "0 )") && tok->varId()) { // constructor call const Variable *var = tok->variable(); if (var && !var->isPointer() && !var->isArray() && Token::Match(var->typeStartToken(), "std :: string|wstring !!::")) nullPointerError(tok); } else { // function call std::list<const Token *> var; parseFunctionCall(*tok, var, &_settings->library, 0); // is one of the var items a NULL pointer? for (std::list<const Token *>::const_iterator it = var.begin(); it != var.end(); ++it) { if (Token::Match(*it, "0|NULL [,)]")) { nullPointerError(*it); } } } } else if (Token::Match(tok, "std :: string|wstring ( 0 )")) nullPointerError(tok); else if (Token::simpleMatch(tok->previous(), ">> 0")) { // Only checking input stream operations is safe here, because otherwise 0 can be an integer as well const Token* tok2 = tok->previous(); // Find start of statement for (; tok2; tok2 = tok2->previous()) { if (Token::Match(tok2->previous(), ";|{|}|:")) break; } if (Token::simpleMatch(tok2, "std :: cin")) nullPointerError(tok); if (tok2 && tok2->varId() != 0) { const Variable *var = tok2->variable(); if (var && var->isStlType(stl_stream)) nullPointerError(tok); } } const Variable *ovar = nullptr; if (Token::Match(tok, "0 ==|!= %var% !!.")) ovar = tok->tokAt(2)->variable(); else if (Token::Match(tok, "%var% ==|!= 0")) ovar = tok->variable(); else if (Token::Match(tok, "%var% =|+ 0 )|]|,|;|+")) ovar = tok->variable(); if (ovar && !ovar->isPointer() && !ovar->isArray() && Token::Match(ovar->typeStartToken(), "std :: string|wstring !!::")) nullPointerError(tok); } } }
void CheckNullPointer::nullPointerByCheckAndDeRef() { const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); // Check if pointer is NULL and then dereference it.. for (std::list<Scope>::const_iterator i = symbolDatabase->scopeList.begin(); i != symbolDatabase->scopeList.end(); ++i) { if (i->type != Scope::eIf && i->type != Scope::eElseIf && i->type != Scope::eWhile) continue; if (!i->classDef || i->classDef->isExpandedMacro()) continue; const Token* const tok = i->type != Scope::eElseIf ? i->classDef->next() : i->classDef->tokAt(2); // TODO: investigate false negatives: // - handle "while"? // - if there are logical operators // - if (x) { } else { ... } // If the if-body ends with a unknown macro then bailout if (Token::Match(i->classEnd->tokAt(-3), "[;{}] %var% ;") && i->classEnd->tokAt(-2)->isUpperCaseName()) continue; // vartok : token for the variable const Token *vartok = nullptr; const Token *checkConditionStart = nullptr; if (Token::Match(tok, "( ! %var% )|&&")) { vartok = tok->tokAt(2); checkConditionStart = vartok->next(); } else if (Token::Match(tok, "( %var% )|&&")) { vartok = tok->next(); } else if (Token::Match(tok, "( ! ( %var% =")) { vartok = tok->tokAt(3); if (Token::simpleMatch(tok->linkAt(2), ") &&")) checkConditionStart = tok->linkAt(2); } else continue; // Check if variable is a pointer const Variable *var = vartok->variable(); if (!var || !var->isPointer()) continue; // variable id for pointer const unsigned int varid(vartok->varId()); const Scope* declScope = &*i; while (declScope->nestedIn && var->scope() != declScope && declScope->type != Scope::eFunction) declScope = declScope->nestedIn; if (Token::Match(vartok->next(), "&& ( %varid% =", varid)) continue; // Name and line of the pointer const std::string &pointerName = vartok->str(); // Check the condition (eg. ( !x && x->i ) if (checkConditionStart) { const Token * const conditionEnd = tok->link(); for (const Token *tok2 = checkConditionStart; tok2 != conditionEnd; tok2 = tok2->next()) { // If we hit a || operator, abort if (tok2->str() == "||") break; // Pointer is used bool unknown = _settings->inconclusive; if (tok2->varId() == varid && (isPointerDeRef(tok2, unknown) || unknown)) { nullPointerError(tok2, pointerName, vartok, unknown); break; } } } // start token = inside the if-body const Token *tok1 = i->classStart; if (Token::Match(tok, "( %var% )|&&")) { // start token = first token after the if/while body tok1 = i->classEnd->next(); if (!tok1) continue; } int indentlevel = 0; // Set to true if we would normally bail out the check. bool inconclusive = false; // Count { and } for tok2 for (const Token *tok2 = tok1; tok2 != declScope->classEnd; tok2 = tok2->next()) { if (tok2->str() == "{") ++indentlevel; else if (tok2->str() == "}") { if (indentlevel == 0) { if (_settings->inconclusive) inconclusive = true; else break; } --indentlevel; // calling exit function? bool unknown = false; if (_tokenizer->IsScopeNoReturn(tok2, &unknown)) { if (_settings->inconclusive && unknown) inconclusive = true; else break; } if (indentlevel <= 0) { // skip all "else" blocks because they are not executed in this execution path while (Token::simpleMatch(tok2, "} else if (")) tok2 = tok2->linkAt(3)->linkAt(1); if (Token::simpleMatch(tok2, "} else {")) tok2 = tok2->linkAt(2); } } if (tok2->str() == "return" || tok2->str() == "throw") { bool unknown = _settings->inconclusive; for (; tok2 && tok2->str() != ";"; tok2 = tok2->next()) { if (tok2->varId() == varid) { if (CheckNullPointer::isPointerDeRef(tok2, unknown)) nullPointerError(tok2, pointerName, vartok, inconclusive); else if (unknown) nullPointerError(tok2, pointerName, vartok, true); if (Token::Match(tok2, "%var% %oror%|&&|?")) break; } } break; } // Bailout for "if". if (tok2->str() == "if") { if (_settings->inconclusive) inconclusive = true; else break; } if (Token::Match(tok2, "goto|continue|break|switch|for")) break; // parameters to sizeof are not dereferenced if (Token::Match(tok2, "decltype|sizeof|typeof")) { if (tok2->strAt(1) != "(") tok2 = tok2->next(); else tok2 = tok2->next()->link(); continue; } // function call, check if pointer is dereferenced if (Token::Match(tok2, "%var% (") && !Token::Match(tok2, "if|while")) { std::list<const Token *> vars; parseFunctionCall(*tok2, vars, &_settings->library, 0); for (std::list<const Token *>::const_iterator it = vars.begin(); it != vars.end(); ++it) { if (Token::Match(*it, "%varid% [,)]", varid)) { nullPointerError(*it, pointerName, vartok, inconclusive); break; } } } // calling unknown function (abort/init).. else if (Token::simpleMatch(tok2, ") ;") && (Token::Match(tok2->link()->tokAt(-2), "[;{}.] %var% (") || Token::Match(tok2->link()->tokAt(-5), "[;{}] ( * %var% ) ("))) { // noreturn function? bool unknown = false; if (_tokenizer->IsScopeNoReturn(tok2->tokAt(2), &unknown)) { if (!unknown || !_settings->inconclusive) { break; } inconclusive = _settings->inconclusive; } // init function (global variables) if (!(var->isLocal() || var->isArgument())) break; } if (tok2->varId() == varid) { // unknown: this is set to true by isPointerDeRef if // the function fails to determine if there // is a dereference or not bool unknown = _settings->inconclusive; if (Token::Match(tok2->previous(), "[;{}=] %var% = 0 ;")) ; else if (CheckNullPointer::isPointerDeRef(tok2, unknown)) nullPointerError(tok2, pointerName, vartok, inconclusive); else if (unknown && _settings->inconclusive) nullPointerError(tok2, pointerName, vartok, true); else break; } } } }
/** Dereferencing null constant (simplified token list) */ void CheckNullPointer::nullConstantDereference() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { if (scope->function == nullptr || !scope->function->hasBody()) // We only look for functions with a body continue; const Token *tok = scope->bodyStart; if (scope->function->isConstructor()) tok = scope->function->token; // Check initialization list for (; tok != scope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "sizeof|decltype|typeid|typeof (")) tok = tok->next()->link(); else if (Token::simpleMatch(tok, "* 0")) { if (Token::Match(tok->previous(), "return|throw|;|{|}|:|[|(|,") || tok->previous()->isOp()) { nullPointerError(tok); } } else if (Token::Match(tok, "0 [") && (tok->previous()->str() != "&" || !Token::Match(tok->next()->link()->next(), "[.(]"))) nullPointerError(tok); else if (Token::Match(tok->previous(), "!!. %name% (") && (tok->previous()->str() != "::" || tok->strAt(-2) == "std")) { if (Token::Match(tok->tokAt(2), "0|NULL|nullptr )") && tok->varId()) { // constructor call const Variable *var = tok->variable(); if (var && !var->isPointer() && !var->isArray() && var->isStlStringType()) nullPointerError(tok); } else { // function call std::list<const Token *> var; parseFunctionCall(*tok, var, &mSettings->library); // is one of the var items a NULL pointer? for (const Token *vartok : var) { if (Token::Match(vartok, "0|NULL|nullptr [,)]")) { nullPointerError(vartok); } } } } else if (Token::Match(tok, "std :: string|wstring ( 0|NULL|nullptr )")) nullPointerError(tok); else if (Token::Match(tok->previous(), "::|. %name% (")) { const std::vector<const Token *> &args = getArguments(tok); for (int argnr = 0; argnr < args.size(); ++argnr) { const Token *argtok = args[argnr]; if (!argtok->hasKnownIntValue()) continue; if (argtok->values().front().intvalue != 0) continue; if (mSettings->library.isnullargbad(tok, argnr+1)) nullPointerError(argtok); } } else if (Token::Match(tok->previous(), ">> 0|NULL|nullptr")) { // Only checking input stream operations is safe here, because otherwise 0 can be an integer as well const Token* tok2 = tok->previous(); // Find start of statement for (; tok2; tok2 = tok2->previous()) { if (Token::Match(tok2->previous(), ";|{|}|:|(")) break; } if (tok2 && tok2->previous() && tok2->previous()->str()=="(") continue; if (Token::simpleMatch(tok2, "std :: cin")) nullPointerError(tok); if (tok2 && tok2->varId() != 0) { const Variable *var = tok2->variable(); if (var && var->isStlType(stl_istream)) nullPointerError(tok); } } const Variable *ovar = nullptr; const Token *tokNull = nullptr; if (Token::Match(tok, "0|NULL|nullptr ==|!=|>|>=|<|<= %var%")) { if (!Token::Match(tok->tokAt(3),".|[")) { ovar = tok->tokAt(2)->variable(); tokNull = tok; } } else if (Token::Match(tok, "%var% ==|!=|>|>=|<|<= 0|NULL|nullptr") || Token::Match(tok, "%var% =|+ 0|NULL|nullptr )|]|,|;|+")) { ovar = tok->variable(); tokNull = tok->tokAt(2); } if (ovar && !ovar->isPointer() && !ovar->isArray() && ovar->isStlStringType() && tokNull && tokNull->originalName() != "'\\0'") nullPointerError(tokNull); } } }
bool CheckNullPointer::isPointerDeRef(const Token *tok, bool &unknown, const Settings *settings) { unknown = false; // Is pointer used as function parameter? if (Token::Match(tok->previous(), "[(,] %name% [,)]") && settings) { const Token *ftok = tok->previous(); while (ftok && ftok->str() != "(") { if (ftok->str() == ")") ftok = ftok->link(); ftok = ftok->previous(); } if (ftok && ftok->previous()) { std::list<const Token *> varlist; parseFunctionCall(*ftok->previous(), varlist, &settings->library); if (std::find(varlist.begin(), varlist.end(), tok) != varlist.end()) { return true; } } } const Token* parent = tok->astParent(); if (!parent) return false; if (parent->str() == "." && parent->astOperand2() == tok) return isPointerDeRef(parent, unknown, settings); const bool firstOperand = parent->astOperand1() == tok; while (parent->str() == "(" && (parent->astOperand2() == nullptr && parent->strAt(1) != ")")) { // Skip over casts parent = parent->astParent(); if (!parent) return false; } // Dereferencing pointer.. if (parent->isUnaryOp("*") && !Token::Match(parent->tokAt(-2), "sizeof|decltype|typeof")) return true; // array access if (firstOperand && parent->str() == "[" && (!parent->astParent() || parent->astParent()->str() != "&")) return true; // address of member variable / array element const Token *parent2 = parent; while (Token::Match(parent2, "[|.")) parent2 = parent2->astParent(); if (parent2 != parent && parent2 && parent2->isUnaryOp("&")) return false; // read/write member variable if (firstOperand && parent->str() == "." && (!parent->astParent() || parent->astParent()->str() != "&")) { if (!parent->astParent() || parent->astParent()->str() != "(" || parent->astParent() == tok->previous()) return true; unknown = true; return false; } if (Token::Match(tok, "%name% (")) return true; if (Token::Match(tok, "%var% = %var% .") && tok->varId() == tok->tokAt(2)->varId()) return true; // std::string dereferences nullpointers if (Token::Match(parent->tokAt(-3), "std :: string|wstring (") && tok->strAt(1) == ")") return true; if (Token::Match(parent->previous(), "%name% (") && tok->strAt(1) == ")") { const Variable* var = tok->tokAt(-2)->variable(); if (var && !var->isPointer() && !var->isArray() && var->isStlStringType()) return true; } // streams dereference nullpointers if (Token::Match(parent, "<<|>>") && !firstOperand) { const Variable* var = tok->variable(); if (var && var->isPointer() && Token::Match(var->typeStartToken(), "char|wchar_t")) { // Only outputting or reading to char* can cause problems const Token* tok2 = parent; // Find start of statement for (; tok2; tok2 = tok2->previous()) { if (Token::Match(tok2->previous(), ";|{|}|:")) break; } if (Token::Match(tok2, "std :: cout|cin|cerr")) return true; if (tok2 && tok2->varId() != 0) { const Variable* var2 = tok2->variable(); if (var2 && var2->isStlType(stl_stream)) return true; } } } const Variable *ovar = nullptr; if (Token::Match(parent, "+|==|!=") || (parent->str() == "=" && !firstOperand)) { if (parent->astOperand1() == tok && parent->astOperand2()) ovar = parent->astOperand2()->variable(); else if (parent->astOperand1() && parent->astOperand2() == tok) ovar = parent->astOperand1()->variable(); } if (ovar && !ovar->isPointer() && !ovar->isArray() && ovar->isStlStringType()) return true; // assume that it's not a dereference (no false positives) return false; }
//Core Method shared_ptr<Statement> Parser::parseStatement() { Tag tags[] = { EQ, UNEQ, LT, GT, LE, GE }; auto isRealOp = [&](Tag t){ for (auto i : tags) { if (t == i) { return true; } } return false; }; switch (it->tag) { //VAR,TYPE,Function call,If,Else,While,For,有的可改变currNode,有的不可 /*case BEGIN: break;*/ case IDENT://此处默认为赋值表达式,实际上大错特错,如果是 函数调用 何如? if (advance(BIND)) { shared_ptr<AssignStmt> assign = parseAssign(); //currNode->addNode(assign); return assign; } else if (advance(OpenBracket)) { shared_ptr<FuncCall> funcCall = parseFunctionCall(); //currNode->addNode(funcCall); return funcCall; } //暂不支持嵌套函数 /*case FUNCTION: break; case PROCEDURE: break;*/ case IF: { shared_ptr<Node> tmp = currNode; shared_ptr<IfStmt> ifstmt = static_pointer_cast<IfStmt>(parseIf()); currNode = tmp; //currNode->addNode(ifstmt); return ifstmt; } case WHILE: { shared_ptr<Node> tmp = currNode; shared_ptr<WhileStmt> whStmt = static_pointer_cast<WhileStmt>(parseWhile()); currNode = tmp; //currNode->addNode(whStmt); return whStmt; } case REPEAT: { shared_ptr<Node> tmp = currNode; shared_ptr<RepeatStmt> repeatStmt = static_pointer_cast<RepeatStmt>(parseRepeat()); currNode = tmp; //currNode->addNode(repeatStmt); return repeatStmt; } case FOR: { shared_ptr<Node> tmp = currNode; shared_ptr<ForStmt> forStmt = static_pointer_cast<ForStmt>(parseFor()); currNode = tmp; //currNode->addNode(forStmt); return forStmt; } case CASE: { shared_ptr<Node> tmp = currNode; shared_ptr<CaseStmt> caseStmt = static_pointer_cast<CaseStmt>(parseCase()); currNode = tmp; //currNode->addNode(caseStmt); return caseStmt; } default: return NULL; } return NULL; }
static bool parseStatement(MemoryStack* stack, LexerCarriage* carriage, Array<SyntaxError>* errors, ASTNode* result) { if (parseVariableDefinition(stack, carriage, errors, result)) return true; if (parseFunctionCall(stack, carriage, errors, result)) return true; if (parseAssignment(stack, carriage, errors, result)) return true; if (carriage->topToken.type == TOK_IDENTIFIER) { Token identifier = carriage->topToken; parseNextToken(carriage); if (carriage->topToken.type == TOK_DOUBLE_DOT) { parseNextToken(carriage); if (carriage->topToken.type == TOK_OPEN_BRACKET) // function definition { parseNextToken(carriage); result->nodeType = AST_STATEMENT_FUNCTION_DEFINITION; result->functionDefinition.lineNumber = carriage->currentLineNumber; result->functionDefinition.identifier = identifier.identifierValue; ASTNode arg; LinkedList<ASTNode> args = {}; if (carriage->topToken.type != TOK_CLOSE_BRACKET) { while (true) { if (parseVariableDefinition(stack, carriage, errors, &arg)) { *pushElement(&args, stack) = arg; if (carriage->topToken.type != TOK_COMMA) break; else parseNextToken(carriage); // pop , } else { pushBack(errors, SyntaxError{ SET_EXPRESSION_EXPECTED }); break; } }; } expectAndEat(carriage, TOK_CLOSE_BRACKET, errors); result->functionDefinition.arguments = args; if (carriage->topToken.type != TOK_VOID) { result->functionDefinition.returnType = pushMemory<ASTNode>(stack); if (!parseTypeIdentifier(stack, carriage, errors, result->functionDefinition.returnType)) { // TODO: change this SyntaxError error = SyntaxError{ SET_UNEXPECTED_TOKEN }; error.unexpectedToken.token = carriage->topToken; error.unexpectedToken.expecting = TOK_IDENTIFIER; pushBack(errors, error); } } else { parseNextToken(carriage); result->functionDefinition.returnType = nullptr; } result->functionDefinition.body = nullptr; if (carriage->topToken.type == TOK_ASSIGN) { parseNextToken(carriage); result->functionDefinition.body = pushMemory<ASTNode>(stack); expectAndEatStatement(stack, carriage, errors, result->functionDefinition.body); } return true; } else if (carriage->topToken.type == TOK_STRUCT) { parseNextToken(carriage); expectAndEat(carriage, TOK_ASSIGN, errors); int bracesOpenedAt = carriage->currentLineNumber; expectAndEat(carriage, TOK_OPEN_BRACES, errors); result->nodeType = AST_STATEMENT_TYPE_DEFINITION; result->typeDefinition.lineNumber = carriage->currentLineNumber; result->typeDefinition.identifier = identifier.identifierValue; result->typeDefinition.definitions = {}; ASTNode definition; do { if (parseVariableDefinition(stack, carriage, errors, &definition)) *pushElement(&result->typeDefinition.definitions, stack) = definition; else break; } while (true); if (carriage->topToken.type == TOK_CLOSE_BRACES) parseNextToken(carriage); else { SyntaxError error = { SET_UNCLOSED_BRACKETS, bracesOpenedAt }; error.unclosedBrackets.closedAt = carriage->currentLineNumber; error.unclosedBrackets.token = carriage->topToken; pushBack(errors, error); } return true; } else { // TODO: error } } else { // TODO: error } } else if (carriage->topToken.type == TOK_OPEN_BRACES) { int openedAt = carriage->currentLineNumber; parseNextToken(carriage); result->nodeType = AST_STATEMENTS_BLOCK; result->statementsBlock.statements = { }; parseStatements(stack, carriage, errors, &result->statementsBlock.statements); if (carriage->topToken.type == TOK_CLOSE_BRACES) parseNextToken(carriage); else { SyntaxError error = SyntaxError{ SET_UNCLOSED_BRACKETS, openedAt }; error.unclosedBrackets.closedAt = carriage->currentLineNumber; error.unclosedBrackets.token = carriage->topToken; pushBack(errors, error); } return true; } else if (carriage->topToken.type == TOK_IF) { parseNextToken(carriage); result->nodeType = AST_STATEMENT_IF; result->ifStatement.condition = pushMemory<ASTNode>(stack); result->ifStatement.ifCase = pushMemory<ASTNode>(stack); expectAndEatExpression(stack, carriage, errors, result->ifStatement.condition); expectAndEatStatement(stack, carriage, errors, result->ifStatement.ifCase); if (carriage->topToken.type == TOK_ELSE) { parseNextToken(carriage); result->ifStatement.elseCase = pushMemory<ASTNode>(stack); expectAndEatStatement(stack, carriage, errors, result->ifStatement.elseCase); } else result->ifStatement.elseCase = nullptr; return true; } else if (carriage->topToken.type == TOK_WHILE) { parseNextToken(carriage); result->nodeType = AST_STATEMENT_WHILE; result->whileStatement.condition = pushMemory<ASTNode>(stack); result->whileStatement.body = pushMemory<ASTNode>(stack); expectAndEatExpression(stack, carriage, errors, result->whileStatement.condition); expectAndEatStatement(stack, carriage, errors, result->whileStatement.body); return true; } return false; }
/** Dereferencing null constant (simplified token list) */ void CheckNullPointer::nullConstantDereference() { const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); for (std::list<Scope>::const_iterator i = symbolDatabase->scopeList.begin(); i != symbolDatabase->scopeList.end(); ++i) { if (i->type != Scope::eFunction || !i->classStart) continue; const Token *tok = i->classStart; if (i->function && (i->function->type == Function::eConstructor || i->function->type == Function::eCopyConstructor)) tok = i->function->token; // Check initialization list for (; tok && tok != i->classEnd; tok = tok->next()) { if (Token::Match(tok, "sizeof|decltype|typeid (")) tok = tok->next()->link(); else if (tok && Token::simpleMatch(tok, "* 0")) { if (Token::Match(tok->previous(), "return|throw|;|{|}|:|[|(|,") || tok->previous()->isOp() || tok->previous()->isAssignmentOp()) { nullPointerError(tok); } } else if (tok && Token::Match(tok, "0 [") && (tok->previous()->str() != "&" || !Token::Match(tok->next()->link()->next(), "[.(]"))) nullPointerError(tok); else if (tok && Token::Match(tok->previous(), "!!. %var% (") && (tok->previous()->str() != "::" || tok->strAt(-2) == "std")) { if (Token::simpleMatch(tok->tokAt(2), "0 )") && tok->varId()) { // constructor call const Variable* var = symbolDatabase->getVariableFromVarId(tok->varId()); if (var && !var->isPointer() && !var->isArray() && Token::Match(var->typeStartToken(), "std :: string !!::")) nullPointerError(tok); } else { // function call std::list<const Token *> var; parseFunctionCall(*tok, var, 0); // is one of the var items a NULL pointer? for (std::list<const Token *>::const_iterator it = var.begin(); it != var.end(); ++it) { if (Token::Match(*it, "0 [,)]")) { nullPointerError(*it); } } } } else if (Token::simpleMatch(tok, "std :: string ( 0 )")) nullPointerError(tok); else if (tok && Token::simpleMatch(tok->previous(), ">> 0")) { // Only checking input stream operations is safe here, because otherwise 0 can be an integer as well const Token* tok2 = tok->previous(); // Find start of statement for (; tok2; tok2 = tok2->previous()) { if (Token::Match(tok2->previous(), ";|{|}|:")) break; } if (Token::simpleMatch(tok2, "std :: cin")) nullPointerError(tok); if (tok2 && tok2->varId() != 0) { const Variable* var = symbolDatabase->getVariableFromVarId(tok2->varId()); if (var && Token::Match(var->typeStartToken(), "std :: istream|ifstream|istringstream|stringstream|fstream|iostream")) nullPointerError(tok); } } unsigned int ovarid = 0; if (Token::Match(tok, "0 ==|!= %var%")) ovarid = tok->tokAt(2)->varId(); else if (Token::Match(tok, "%var% ==|!= 0")) ovarid = tok->varId(); else if (Token::Match(tok, "%var% =|+=|+ 0 )|]|,|;|+")) ovarid = tok->varId(); if (ovarid) { const Variable* var = symbolDatabase->getVariableFromVarId(ovarid); if (var && !var->isPointer() && !var->isArray() && Token::Match(var->typeStartToken(), "std :: string !!::")) nullPointerError(tok); } } } }
//dereferenceBeforeCheck void CheckNullPointer::nullPointerByDeRefAndChec() { const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); // Dereferencing a pointer and then checking if it's NULL.. // This check will first scan for the check. And then scan backwards // from the check, searching for dereferencing. for (std::list<Scope>::const_iterator i = symbolDatabase->scopeList.begin(); i != symbolDatabase->scopeList.end(); ++i) { // TODO: false negatives. // - logical operators const Token* tok = i->classDef; if ((i->type == Scope::eIf || i->type == Scope::eElseIf || i->type == Scope::eWhile) && tok && Token::Match(tok, "else| %var% ( !| %var% )|%oror%|&&") && !tok->next()->isExpandedMacro()) { if (tok->str() == "else") tok = tok->next(); const Token * vartok = tok->tokAt(2); if (vartok && vartok->str() == "!") vartok = vartok->next(); if(!vartok) continue; // Variable id for pointer const unsigned int varid(vartok->varId()); if (varid == 0) continue; // Name of pointer const std::string& varname(vartok->str()); const Variable* var = symbolDatabase->getVariableFromVarId(varid); // Check that variable is a pointer.. if (!var || !var->isPointer()) continue; const Token * const decltok = var->nameToken(); bool inconclusive = false; for (const Token *tok1 = tok->previous(); tok1 && tok1 != decltok; tok1 = tok1->previous()) { if (tok1->str() == ")" && Token::Match(tok1->link()->previous(), "%var% (")) { const Token *tok2 = tok1->link(); while (tok2 && !Token::Match(tok2, "[;{}?:]")) tok2 = tok2->previous(); if (tok2 && Token::Match(tok2, "[?:]")) break; if (tok2 && Token::Match(tok2->next(), "%varid% = %var%", varid)) break; if (tok2 && Token::Match(tok2->next(), "while ( %varid%", varid)) break; if (Token::Match(tok1->link(), "( ! %varid% %oror%", varid) || Token::Match(tok1->link(), "( %varid% &&", varid)) { tok1 = tok1->link(); continue; } if (Token::simpleMatch(tok1->link()->previous(), "sizeof (")) { tok1 = tok1->link()->previous(); continue; } if (tok2 && Token::Match(tok2->next(), "%var% ( %varid% ,", varid)) { std::list<const Token *> varlist; parseFunctionCall(*(tok2->next()), varlist, 0); if (!varlist.empty() && varlist.front() == tok2->tokAt(3)) { nullPointerError(tok2->tokAt(3), varname, tok, inconclusive); // break; continue; } } // Passing pointer as parameter.. if (tok2 && Token::Match(tok2->next(), "%type% (")) { bool unknown = false; if (CanFunctionAssignPointer(tok2->next(), varid, unknown)) { if (!_settings->inconclusive || !unknown) break; inconclusive = true; } } // calling unknown function => it might initialize the pointer if (!(var->isLocal() || var->isArgument())) break; } if (tok1->str() == "break") break; if (tok1->varId() == varid) { // Don't write warning if the dereferencing is // guarded by ?: or && const Token *tok2 = tok1->previous(); if (tok2 && (tok2->isArithmeticalOp() || tok2->str() == "(")) { while (tok2 && !Token::Match(tok2, "[;{}?:]")) { if (tok2->str() == ")") { tok2 = tok2->link(); if (Token::Match(tok2, "( %varid% =", varid)) { tok2 = tok2->next(); break; } } // guarded by && if (tok2->varId() == varid && tok2->next()->str() == "&&") break; tok2 = tok2->previous(); } } if (!tok2 || Token::Match(tok2, "[?:]") || tok2->varId() == varid) continue; // unknown : this is set by isPointerDeRef if it is // uncertain bool unknown = _settings->inconclusive; // reassign : is the pointer reassigned like this: // tok = tok->next(); bool reassign = false; if (Token::Match(tok1->previous(), "= %varid% .", varid)) { const Token *back = tok1->tokAt(-2); while (back) { if (back->varId() == varid) { reassign = true; break; } if (Token::Match(back, "[{};,(]")) { break; } back = back->previous(); } } if (reassign) { break; } else if (Token::simpleMatch(tok1->tokAt(-2), "* )") && Token::Match(tok1->linkAt(-1)->tokAt(-2), "%varid% = (", tok1->varId())) { break; } else if (Token::simpleMatch(tok1->tokAt(-3), "* ) (") && Token::Match(tok1->linkAt(-2)->tokAt(-2), "%varid% = (", tok1->varId())) { break; } else if (Token::Match(tok1->previous(), "&&|%oror%")) { break; } else if (Token::Match(tok1->tokAt(-2), "&&|%oror% !")) { break; } else if (CheckNullPointer::isPointerDeRef(tok1, unknown, symbolDatabase)) { nullPointerError(tok1, varname, tok, inconclusive); break; } else if (tok1->strAt(-1) == "&") { break; } else if (tok1->strAt(1) == "=") { break; } } else if (tok1->str() == "{" || tok1->str() == "}") break; // label.. else if (Token::Match(tok1, "%type% :")) break; } } } }