@@ -282,6 +282,10 @@ class TestSymbolDatabase : public TestFixture {
282282 TEST_CASE(hasGlobalVariables1);
283283 TEST_CASE(hasGlobalVariables2);
284284 TEST_CASE(hasGlobalVariables3);
285+ TEST_CASE(globalVariableRedeclarations); // #6418
286+ TEST_CASE(globalVariableRedeclarationMetadata);
287+ TEST_CASE(globalVariableRedeclarationArrayBounds);
288+ TEST_CASE(globalVariableRedeclarationScopes);
285289
286290 TEST_CASE(checkTypeStartEndToken1);
287291 TEST_CASE(checkTypeStartEndToken2); // handling for unknown macro: 'void f() MACRO {..'
@@ -1858,9 +1862,11 @@ class TestSymbolDatabase : public TestFixture {
18581862 GET_SYMBOL_DB_C("extern alignas(16) int x;\n"
18591863 "alignas(16) int x;\n");
18601864 ASSERT(db);
1861- ASSERT_EQUALS(2 , db->scopeList.front().varlist.size());
1865+ ASSERT_EQUALS(1 , db->scopeList.front().varlist.size());
18621866 const Variable *x1 = Token::findsimplematch(tokenizer.tokens(), "x")->variable();
18631867 ASSERT(x1 && Token::simpleMatch(x1->typeStartToken(), "int x ;"));
1868+ const Token *x2 = findToken(tokenizer, "x ;", 2);
1869+ ASSERT(x2 && x2->variable() == x1);
18641870 }
18651871
18661872 void memberVar1() {
@@ -2696,6 +2702,159 @@ class TestSymbolDatabase : public TestFixture {
26962702 ASSERT(var->typeStartToken()->str() == "int");
26972703 }
26982704
2705+ void globalVariableRedeclarations() { // #6418
2706+ GET_SYMBOL_DB_C("int tentative;\n"
2707+ "int tentative;\n"
2708+ "extern int defined;\n"
2709+ "int before(void) { return defined; }\n"
2710+ "int defined;\n"
2711+ "extern int defined;\n"
2712+ "int after(void) { return defined; }\n"
2713+ "int reverse;\n"
2714+ "extern int reverse;\n");
2715+ ASSERT(db);
2716+ ASSERT_EQUALS(3, db->scopeList.front().varlist.size());
2717+ for (const Variable& var : db->scopeList.front().varlist) {
2718+ ASSERT(var.isGlobal());
2719+ ASSERT(!var.isExtern());
2720+ ASSERT(var.declarationId() != 0);
2721+ ASSERT(db->getVariableFromVarId(var.declarationId()) == &var);
2722+ for (const Token* tok = tokenizer.tokens(); tok; tok = tok->next()) {
2723+ if (tok->str() != var.name())
2724+ continue;
2725+ ASSERT_EQUALS(var.declarationId(), tok->varId());
2726+ ASSERT(tok->variable() == &var);
2727+ }
2728+ }
2729+ }
2730+
2731+ void globalVariableRedeclarationMetadata() {
2732+ GET_SYMBOL_DB_C("extern int value;\n"
2733+ "int value = 7;\n"
2734+ "extern int value;\n"
2735+ "int reverse = 9;\n"
2736+ "extern int reverse;\n"
2737+ "extern int data[];\n"
2738+ "int before(void) { return data[0]; }\n"
2739+ "int data[4];\n"
2740+ "extern int data[];\n"
2741+ "int after(void) { return data[0]; }\n"
2742+ "static int internal;\n"
2743+ "extern int internal = 1;\n");
2744+ ASSERT(db);
2745+ ASSERT_EQUALS(4, db->scopeList.front().varlist.size());
2746+
2747+ const Variable* value = Token::findsimplematch(tokenizer.tokens(), "value")->variable();
2748+ ASSERT(value && value->isInit() && !value->isExtern());
2749+ ASSERT_EQUALS(2, value->nameToken()->linenr());
2750+ const Variable* reverse = Token::findsimplematch(tokenizer.tokens(), "reverse")->variable();
2751+ ASSERT(reverse && reverse->isInit() && !reverse->isExtern());
2752+ ASSERT_EQUALS(4, reverse->nameToken()->linenr());
2753+
2754+ const Variable* data = Token::findsimplematch(tokenizer.tokens(), "data")->variable();
2755+ ASSERT(data && data->isArray() && !data->isExtern());
2756+ ASSERT_EQUALS(1U, data->dimensions().size());
2757+ ASSERT(data->dimensions()[0].known);
2758+ ASSERT_EQUALS(4, data->dimension(0));
2759+ const Variable* internal = Token::findsimplematch(tokenizer.tokens(), "internal")->variable();
2760+ ASSERT(internal && internal->isStatic() && internal->isInit());
2761+ ASSERT_EQUALS(12, internal->nameToken()->linenr());
2762+
2763+ for (const Variable& var : db->scopeList.front().varlist) {
2764+ ASSERT(db->getVariableFromVarId(var.declarationId()) == &var);
2765+ for (const Token* tok = tokenizer.tokens(); tok; tok = tok->next()) {
2766+ if (tok->str() == var.name())
2767+ ASSERT(tok->variable() == &var);
2768+ }
2769+ }
2770+ }
2771+
2772+ void globalVariableRedeclarationArrayBounds() {
2773+ GET_SYMBOL_DB_C("int a[4];\n"
2774+ "int a[] = { 1 };\n"
2775+ "extern int b[4];\n"
2776+ "int b[] = { 1 };\n");
2777+ ASSERT(db);
2778+ ASSERT_EQUALS(2, db->scopeList.front().varlist.size());
2779+ for (const Variable& var : db->scopeList.front().varlist) {
2780+ ASSERT(var.isArray() && var.isInit() && !var.isExtern());
2781+ ASSERT_EQUALS(1U, var.dimensions().size());
2782+ ASSERT(var.dimensions()[0].known);
2783+ ASSERT_EQUALS(4, var.dimension(0));
2784+ for (const Token* tok = tokenizer.tokens(); tok; tok = tok->next()) {
2785+ if (tok->str() == var.name())
2786+ ASSERT(tok->variable() == &var);
2787+ }
2788+ }
2789+ }
2790+
2791+ void globalVariableRedeclarationScopes() {
2792+ {
2793+ GET_SYMBOL_DB_C("int x;\n"
2794+ "struct A { int x; };\n"
2795+ "struct B { int x; };\n"
2796+ "void f(void) { int x; { int x; } }\n"
2797+ "void g(void) { int x; }\n"
2798+ "int x;\n");
2799+ ASSERT(db);
2800+ ASSERT_EQUALS(1, db->scopeList.front().varlist.size());
2801+ const Variable* global = &db->scopeList.front().varlist.front();
2802+ std::set<const Variable*> variables;
2803+ for (const Token* tok = tokenizer.tokens(); tok; tok = tok->next()) {
2804+ if (tok->str() != "x")
2805+ continue;
2806+ ASSERT(tok->variable());
2807+ variables.insert(tok->variable());
2808+ if (tok->linenr() == 1 || tok->linenr() == 6)
2809+ ASSERT(tok->variable() == global);
2810+ else
2811+ ASSERT(tok->variable() != global);
2812+ }
2813+ ASSERT_EQUALS(6, variables.size());
2814+ }
2815+ {
2816+ GET_SYMBOL_DB_C("int x;\n"
2817+ "int f(int x);\n"
2818+ "int f(int x) { return x; }\n"
2819+ "int x;\n");
2820+ ASSERT(db);
2821+ ASSERT_EQUALS(1, db->scopeList.front().varlist.size());
2822+ const Variable* global = &db->scopeList.front().varlist.front();
2823+ const Token* prototypeArg = findToken(tokenizer, "x )", 2);
2824+ ASSERT(prototypeArg && prototypeArg->varId() != 0);
2825+ ASSERT(prototypeArg->varId() != global->declarationId());
2826+ const Token* arg = findToken(tokenizer, "x )", 3);
2827+ const Token* use = findToken(tokenizer, "x ;", 3);
2828+ ASSERT(arg && arg->variable() && arg->variable()->isArgument());
2829+ ASSERT(arg->variable() != global);
2830+ ASSERT(use && use->variable() == arg->variable());
2831+ }
2832+ {
2833+ GET_SYMBOL_DB_C("int x;\n"
2834+ "struct { int x; } s;\n"
2835+ "int x;\n");
2836+ ASSERT(db);
2837+ ASSERT_EQUALS(2, db->scopeList.front().varlist.size());
2838+ const Token* global = findToken(tokenizer, "x ;", 1);
2839+ const Token* member = findToken(tokenizer, "x ;", 2);
2840+ const Token* redeclaration = findToken(tokenizer, "x ;", 3);
2841+ ASSERT(global && global->variable() && global->variable()->isGlobal());
2842+ ASSERT(member && member->variable() && member->variable()->isMember());
2843+ ASSERT(redeclaration && redeclaration->variable() == global->variable());
2844+ ASSERT(member->variable() != global->variable());
2845+ }
2846+ {
2847+ // Preserve the existing C++ handling.
2848+ GET_SYMBOL_DB("extern int x; int x;\n");
2849+ ASSERT_EQUALS(2, db->scopeList.front().varlist.size());
2850+ const Token* first = Token::findsimplematch(tokenizer.tokens(), "x ;");
2851+ ASSERT(first && first->variable());
2852+ const Token* second = Token::findsimplematch(first->next(), "x ;");
2853+ ASSERT(second && second->variable());
2854+ ASSERT(first->variable() != second->variable());
2855+ }
2856+ }
2857+
26992858 void checkTypeStartEndToken1() {
27002859 GET_SYMBOL_DB("static std::string i;\n"
27012860 "static const std::string j;\n"
0 commit comments