/*
* Cppcheck - A tool for static C/C++ code analysis
* Copyright (C) 2007-2026 Cppcheck team.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "fixture.h"
#include "helpers.h"
#include "settings.h"
#include "standards.h"
#include "token.h"
#include "tokenlist.h"
#include "vfvalue.h"
#include
#include
#include
#include
#include
class TestToken : public TestFixture {
public:
TestToken() : TestFixture("TestToken") {}
private:
class TokenTest final : public Token
{
friend class TestToken;
};
const TokenList list{settingsDefault, Standards::Language::C};
std::vector arithmeticalOps;
std::vector logicalOps;
std::vector bitOps;
std::vector comparisonOps;
std::vector extendedOps;
std::vector assignmentOps;
void run() override {
arithmeticalOps = { "+", "-", "*", "/", "%", "" };
logicalOps = { "&&", "||", "!" };
comparisonOps = { "==", "!=", "=" };
bitOps = { "&", "|", "^", "~" };
extendedOps = { ",", "[", "]", "(", ")", "?", ":" };
assignmentOps = { "=", "+=", "-=", "*=", "/=", "%=", "&=", "^=", "|=", "=" };
TEST_CASE(nextprevious);
TEST_CASE(multiCompare);
TEST_CASE(multiCompare2); // #3294 - false negative multi compare between "=" and "=="
TEST_CASE(multiCompare3); // false positive for %or% on code using "|="
TEST_CASE(multiCompare4);
TEST_CASE(multiCompare5);
TEST_CASE(multiCompare6);
TEST_CASE(charTypes);
TEST_CASE(stringTypes);
TEST_CASE(getStrLength);
TEST_CASE(getStrSize);
TEST_CASE(strValue);
TEST_CASE(concatStr);
TEST_CASE(deleteLast);
TEST_CASE(deleteFirst);
TEST_CASE(nextArgument);
TEST_CASE(eraseTokens);
TEST_CASE(matchAny);
TEST_CASE(matchSingleChar);
TEST_CASE(matchNothingOrAnyNotElse);
TEST_CASE(matchType);
TEST_CASE(matchChar);
TEST_CASE(matchCompOp);
TEST_CASE(matchStr);
TEST_CASE(matchVarid);
TEST_CASE(matchNumeric);
TEST_CASE(matchBoolean);
TEST_CASE(matchOr);
TEST_CASE(matchOp);
TEST_CASE(matchConstOp);
TEST_CASE(isArithmeticalOp);
TEST_CASE(isOp);
TEST_CASE(isConstOp);
TEST_CASE(isExtendedOp);
TEST_CASE(isAssignmentOp);
TEST_CASE(isStandardType);
TEST_CASE(literals);
TEST_CASE(operators);
TEST_CASE(updateProperties);
TEST_CASE(isNameGuarantees1);
TEST_CASE(isNameGuarantees2);
TEST_CASE(isNameGuarantees3);
TEST_CASE(isNameGuarantees4);
TEST_CASE(isNameGuarantees5);
TEST_CASE(isNameGuarantees6);
TEST_CASE(canFindMatchingBracketsNeedsOpen);
TEST_CASE(canFindMatchingBracketsInnerPair);
TEST_CASE(canFindMatchingBracketsOuterPair);
TEST_CASE(canFindMatchingBracketsWithTooManyClosing);
TEST_CASE(canFindMatchingBracketsWithTooManyOpening);
TEST_CASE(findClosingBracket);
TEST_CASE(findClosingBracket2);
TEST_CASE(findClosingBracket3);
TEST_CASE(findClosingBracket4);
TEST_CASE(expressionString);
TEST_CASE(hasKnownIntValue);
TEST_CASE(update_property_info);
TEST_CASE(update_property_info_evariable);
TEST_CASE(update_property_info_ekeyword_c);
TEST_CASE(update_property_info_ekeyword_cpp);
TEST_CASE(update_property_info_ebracket_link);
TEST_CASE(update_property_info_ecomparisonop_link);
TEST_CASE(update_property_info_etype_c);
TEST_CASE(update_property_info_etype_cpp);
TEST_CASE(update_property_info_replace); // #13743
TEST_CASE(varid_reset);
}
void nextprevious() const {
auto tokensFrontBack = std::make_shared();
auto *token = new Token(list, std::move(tokensFrontBack));
token->str("1");
(void)token->insertToken("2");
(void)token->next()->insertToken("3");
Token *last = token->tokAt(2);
ASSERT_EQUALS(token->str(), "1");
ASSERT_EQUALS(token->strAt(1), "2");
ASSERT_EQUALS(token->tokAt(2)->str(), "3");
ASSERT_EQUALS_MSG(true, last->next() == nullptr, "Null was expected");
ASSERT_EQUALS(last->str(), "3");
ASSERT_EQUALS(last->strAt(-1), "2");
ASSERT_EQUALS(last->tokAt(-2)->str(), "1");
ASSERT_EQUALS_MSG(true, token->previous() == nullptr, "Null was expected");
TokenList::deleteTokens(token);
}
#define MatchCheck(...) MatchCheck_(__FILE__, __LINE__, __VA_ARGS__)
bool MatchCheck_(const char* file, int line, const std::string& code, const std::string& pattern) {
SimpleTokenizer tokenizer(settingsDefault, *this);
const std::string code2 = ";" + code + ";";
try {
ASSERT_LOC(tokenizer.tokenize(code2), file, line);
} catch (...) {}
return Token::Match(tokenizer.tokens()->next(), pattern.c_str());
}
void multiCompare() const {
// Test for found
{
auto tokensFrontBack = std::make_shared();
Token one(list, std::move(tokensFrontBack));
one.str("one");
ASSERT_EQUALS(1, TokenTest::multiCompare(&one, "one|two", 0));
}
{
auto tokensFrontBack = std::make_shared();
Token two(list, std::move(tokensFrontBack));
two.str("two");
ASSERT_EQUALS(1, TokenTest::multiCompare(&two, "one|two", 0));
ASSERT_EQUALS(1, TokenTest::multiCompare(&two, "verybig|two|", 0));
}
// Test for empty string found
{
auto tokensFrontBack = std::make_shared();
Token notfound(list, std::move(tokensFrontBack));
notfound.str("notfound");
ASSERT_EQUALS(0, TokenTest::multiCompare(¬found, "one|two|", 0));
// Test for not found
ASSERT_EQUALS(-1, TokenTest::multiCompare(¬found, "one|two", 0));
}
{
auto tokensFrontBack = std::make_shared();
Token s(list, std::move(tokensFrontBack));
s.str("s");
ASSERT_EQUALS(-1, TokenTest::multiCompare(&s, "verybig|two", 0));
}
{
auto tokensFrontBack = std::make_shared();
Token ne(list, std::move(tokensFrontBack));
ne.str("ne");
ASSERT_EQUALS(-1, TokenTest::multiCompare(&ne, "one|two", 0));
}
{
auto tokensFrontBack = std::make_shared();
Token a(list, std::move(tokensFrontBack));
a.str("a");
ASSERT_EQUALS(-1, TokenTest::multiCompare(&a, "abc|def", 0));
}
{
auto tokensFrontBack = std::make_shared();
Token abcd(list, std::move(tokensFrontBack));
abcd.str("abcd");
ASSERT_EQUALS(-1, TokenTest::multiCompare(&abcd, "abc|def", 0));
}
{
auto tokensFrontBack = std::make_shared();
Token def(list, std::move(tokensFrontBack));
def.str("default");
ASSERT_EQUALS(-1, TokenTest::multiCompare(&def, "abc|def", 0));
}
// %op%
{
auto tokensFrontBack = std::make_shared();
Token plus(list, std::move(tokensFrontBack));
plus.str("+");
ASSERT_EQUALS(1, TokenTest::multiCompare(&plus, "one|%op%", 0));
ASSERT_EQUALS(1, TokenTest::multiCompare(&plus, "%op%|two", 0));
}
{
auto tokensFrontBack = std::make_shared();
Token x(list, std::move(tokensFrontBack));
x.str("x");
ASSERT_EQUALS(-1, TokenTest::multiCompare(&x, "one|%op%", 0));
ASSERT_EQUALS(-1, TokenTest::multiCompare(&x, "%op%|two", 0));
}
}
void multiCompare2() const { // #3294
// Original pattern that failed: [[,(=+-*|&^] %num% [+-*/] %num% ]|,|)|;|=|%op%
const SimpleTokenList toks("a == 1");
ASSERT_EQUALS(true, Token::Match(toks.front(), "a =|%op%"));
}
void multiCompare3() const {
// Original pattern that failed: "return|(|&&|%oror% %name% &&|%oror%|==|!=|=||-|%or% %name% )|&&|%oror%|;"
// Code snippet that failed: "return lv@86 |= rv@87 ;"
// Note: Also test "reverse" alternative pattern, two different code paths to handle it
const SimpleTokenList toks("return a |= b ;");
ASSERT_EQUALS(false, Token::Match(toks.front(), "return %name% xyz|%or% %name% ;"));
ASSERT_EQUALS(false, Token::Match(toks.front(), "return %name% %or%|xyz %name% ;"));
const SimpleTokenList toks2("return a | b ;");
ASSERT_EQUALS(true, Token::Match(toks2.front(), "return %name% xyz|%or% %name% ;"));
ASSERT_EQUALS(true, Token::Match(toks2.front(), "return %name% %or%|xyz %name% ;"));
const SimpleTokenList toks3("return a || b ;");
ASSERT_EQUALS(false, Token::Match(toks3.front(), "return %name% xyz|%or% %name% ;"));
ASSERT_EQUALS(false, Token::Match(toks3.front(), "return %name% %or%|xyz %name% ;"));
ASSERT_EQUALS(true, Token::Match(toks3.front(), "return %name% xyz|%oror% %name% ;"));
ASSERT_EQUALS(true, Token::Match(toks3.front(), "return %name% %oror%|xyz %name% ;"));
const SimpleTokenList toks4("a % b ;");
ASSERT_EQUALS(true, Token::Match(toks4.front(), "%name% >>|||findClosingBracket();
ASSERT(t == nullptr);
}
void canFindMatchingBracketsInnerPair() {
SimpleTokenizer var(*this);
ASSERT(var.tokenize("std::deque intsets;"));
const Token * const t = var.tokens()->tokAt(7)->findClosingBracket();
ASSERT_EQUALS(">", t->str());
ASSERT(var.tokens()->tokAt(9) == t);
}
void canFindMatchingBracketsOuterPair() {
SimpleTokenizer var(*this);
ASSERT(var.tokenize("std::deque intsets;"));
const Token* const t = var.tokens()->tokAt(3)->findClosingBracket();
ASSERT_EQUALS(">", t->str());
ASSERT(var.tokens()->tokAt(10) == t);
}
void canFindMatchingBracketsWithTooManyClosing() {
SimpleTokenizer var(*this);
ASSERT(var.tokenize("X< 1>2 > x1;"));
const Token* const t = var.tokens()->next()->findClosingBracket();
ASSERT_EQUALS(">", t->str());
ASSERT(var.tokens()->tokAt(3) == t);
}
void canFindMatchingBracketsWithTooManyOpening() {
SimpleTokenizer var(*this);
ASSERT(var.tokenize("X < (2 < 1) > x1;"));
const Token* t = var.tokens()->next()->findClosingBracket();
ASSERT(t != nullptr && t->str() == ">");
t = var.tokens()->tokAt(4)->findClosingBracket();
ASSERT(t == nullptr);
}
void findClosingBracket() {
SimpleTokenizer var(*this);
ASSERT(var.tokenize("template struct S : public Fred {}"));
const Token* const t = var.tokens()->next()->findClosingBracket();
ASSERT(Token::simpleMatch(t, "> struct"));
}
void findClosingBracket2() {
SimpleTokenizer var(*this); // #11275
ASSERT(var.tokenize("const auto g = []() {};\n"));
const Token* const t = Token::findsimplematch(var.tokens(), ""));
}
void findClosingBracket3() {
SimpleTokenizer var(*this); // #12789
ASSERT(var.tokenize("template \n"
"void f();\n"));
const Token* const t = Token::findsimplematch(var.tokens(), ""));
}
void findClosingBracket4() {
SimpleTokenizer var(*this); // #12923
ASSERT(var.tokenize("template\n"
"class C;\n"));
const Token *const t = Token::findsimplematch(var.tokens(), "