Преглед изворни кода

Fix erroneous parse error when declaring variables using struct names

This fixes #3931

The parser would reject a variable declaration like `float A, B;`  if struct named B existed in the same scope. It incorrectly treated B as a type name (TYPE_NAME) rather than as a variable identifier, resulting in a syntax error.

This updates the grammar to allow both `IDENTIFIER` and `TYPE_NAME` tokens for the first variable in a variable declaration (e.g., float A, B;), but only `IDENTIFIER` for subsequent variables after commas.

This is limited to variable declarations only.

Added some tests too.
Diego Novillo пре 3 недеља
родитељ
комит
cefdfc3b63

+ 22 - 0
Test/multiple_struct_names.frag

@@ -0,0 +1,22 @@
+#version 400
+struct B
+{
+    vec3 t;
+};
+
+struct C
+{
+    vec4 u;
+};
+
+struct D
+{
+    vec2 v;
+};
+
+void main()
+{
+    float B, C, D; // Multiple struct names as variable names - should work
+    int B[5], C[3], D[2]; // Multiple struct names as array variable names - should work
+    vec2 B = vec2(1.0, 2.0), C = vec2(3.0, 4.0), D = vec2(5.0, 6.0); // Multiple struct names with initializers - should work
+} 

+ 14 - 0
Test/struct_name_as_struct_member.frag

@@ -0,0 +1,14 @@
+#version 400
+struct B
+{
+    vec3 t;
+};
+
+struct K
+{
+    float A, B; // Struct name B as member name - should work
+    int B;      // Struct name B as member name - should work
+    vec2 B;     // Struct name B as member name - should work
+};
+
+void main(){} 

+ 14 - 0
Test/struct_name_as_struct_member_with_array.frag

@@ -0,0 +1,14 @@
+#version 400
+struct B
+{
+    vec3 t;
+};
+
+struct K
+{
+    float A, B[10]; // Struct name B as array member name - should work
+    int B[5];       // Struct name B as array member name - should work
+    vec2 B[3];      // Struct name B as array member name - should work
+};
+
+void main(){} 

+ 12 - 0
Test/struct_name_as_variable.frag

@@ -0,0 +1,12 @@
+#version 400
+struct B
+{
+    vec3 t;
+};
+
+void main()
+{
+    float B; // Struct name as variable name - should work
+    int B;   // Struct name as variable name - should work
+    vec2 B;  // Struct name as variable name - should work
+} 

+ 12 - 0
Test/struct_name_as_variable_with_array.frag

@@ -0,0 +1,12 @@
+#version 400
+struct B
+{
+    vec3 t;
+};
+
+void main()
+{
+    float B[10]; // Struct name as array variable name - should work
+    int B[5];    // Struct name as array variable name - should work
+    vec2 B[3];   // Struct name as array variable name - should work
+} 

+ 12 - 0
Test/struct_name_as_variable_with_array_initializer.frag

@@ -0,0 +1,12 @@
+#version 400
+struct B
+{
+    vec3 t;
+};
+
+void main()
+{
+    float B[2] = float[2](1.0, 2.0); // Struct name as array variable name with initializer - should work
+    int B[3] = int[3](1, 2, 3);      // Struct name as array variable name with initializer - should work
+    vec2 B[2] = vec2[2](vec2(1.0, 2.0), vec2(3.0, 4.0)); // Struct name as array variable name with initializer - should work
+} 

+ 12 - 0
Test/struct_name_as_variable_with_initializer.frag

@@ -0,0 +1,12 @@
+#version 400
+struct B
+{
+    vec3 t;
+};
+
+void main()
+{
+    float B = 1.0; // Struct name as variable name with initializer - should work
+    int B = 42;    // Struct name as variable name with initializer - should work
+    vec2 B = vec2(1.0, 2.0); // Struct name as variable name with initializer - should work
+} 

+ 31 - 0
Test/struct_name_edge_cases.frag

@@ -0,0 +1,31 @@
+#version 400
+struct B
+{
+    vec3 t;
+};
+
+struct C
+{
+    vec4 u;
+};
+
+void main()
+{
+    // Multiple declarations in one statement
+    float A, B, C; // B and C are struct names - should work
+    
+    // Array declarations
+    int B[10], C[5]; // B and C are struct names - should work
+    
+    // With initializers
+    vec2 B = vec2(1.0, 2.0), C = vec2(3.0, 4.0); // B and C are struct names - should work
+    
+    // Array with initializers
+    float B[2] = float[2](1.0, 2.0), C[2] = float[2](3.0, 4.0); // B and C are struct names - should work
+    
+    // Nested scope
+    {
+        float B = 1.0; // Struct name in nested scope - should work
+        int C = 42;    // Struct name in nested scope - should work
+    }
+} 

+ 14 - 0
Test/struct_name_in_compute_shader.comp

@@ -0,0 +1,14 @@
+#version 400
+struct B
+{
+    vec3 t;
+};
+
+layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
+
+void main()
+{
+    float B = 1.0; // Struct name as local variable - should work
+    int B = 42;    // Struct name as local variable - should work
+    vec2 B = vec2(1.0, 2.0); // Struct name as local variable - should work
+} 

+ 10 - 0
Test/struct_name_in_declaration.frag

@@ -0,0 +1,10 @@
+#version 400
+struct B
+{
+    vec3 t;
+};
+struct K
+{
+    float A, B; // This should now work correctly
+};
+void main(){} 

+ 15 - 0
Test/struct_name_in_function_params.frag

@@ -0,0 +1,15 @@
+#version 400
+struct B
+{
+    vec3 t;
+};
+
+void func(float B, int B, vec2 B) // Struct name B as parameter names - should work
+{
+    // Function body
+}
+
+void main()
+{
+    func(1.0, 42, vec2(1.0, 2.0));
+} 

+ 18 - 0
Test/struct_name_in_function_params_with_array.frag

@@ -0,0 +1,18 @@
+#version 400
+struct B
+{
+    vec3 t;
+};
+
+void func(float B[5], int B[3], vec2 B[2]) // Struct name B as array parameter names - should work
+{
+    // Function body
+}
+
+void main()
+{
+    float arr1[5] = float[5](1.0, 2.0, 3.0, 4.0, 5.0);
+    int arr2[3] = int[3](1, 2, 3);
+    vec2 arr3[2] = vec2[2](vec2(1.0, 2.0), vec2(3.0, 4.0));
+    func(arr1, arr2, arr3);
+} 

+ 13 - 0
Test/struct_name_in_middle.frag

@@ -0,0 +1,13 @@
+#version 400
+
+struct A {
+    float x;
+};
+
+struct B {
+    float y;
+};
+
+void main() {
+    float A, x, B;  // A and B are struct names, x is a regular variable
+} 

+ 17 - 0
Test/struct_name_in_vertex_shader.vert

@@ -0,0 +1,17 @@
+#version 400
+struct B
+{
+    vec3 t;
+};
+
+layout(location = 0) in vec3 position;
+layout(location = 1) in vec3 normal;
+
+out vec3 B; // Struct name as output variable - should work
+
+void main()
+{
+    float B = 1.0; // Struct name as local variable - should work
+    vec3 B = normal; // Struct name as local variable - should work
+    gl_Position = vec4(position, 1.0);
+} 

+ 15 - 0
Test/struct_name_with_qualifiers.frag

@@ -0,0 +1,15 @@
+#version 400
+struct B
+{
+    vec3 t;
+};
+
+uniform float B; // Struct name as uniform variable - should work
+in vec3 B;       // Struct name as input variable - should work
+out vec4 B;      // Struct name as output variable - should work
+
+void main()
+{
+    const float B = 1.0; // Struct name as const variable - should work
+    vec4 B = vec4(1.0);  // Struct name as local variable - should work
+} 

+ 24 - 0
glslang/MachineIndependent/glslang.y

@@ -1199,21 +1199,45 @@ single_declaration
         $$.intermNode = 0;
         parseContext.declareVariable($2.loc, *$2.string, $1);
     }
+    | fully_specified_type TYPE_NAME {
+        $$.type = $1;
+        $$.intermNode = 0;
+        parseContext.declareVariable($2.loc, *$2.string, $1);
+    }
+
     | fully_specified_type IDENTIFIER array_specifier {
         $$.type = $1;
         $$.intermNode = 0;
         parseContext.declareVariable($2.loc, *$2.string, $1, $3.arraySizes);
     }
+    | fully_specified_type TYPE_NAME array_specifier {
+        $$.type = $1;
+        $$.intermNode = 0;
+        parseContext.declareVariable($2.loc, *$2.string, $1, $3.arraySizes);
+    }
+
     | fully_specified_type IDENTIFIER array_specifier EQUAL initializer {
         $$.type = $1;
         TIntermNode* initNode = parseContext.declareVariable($2.loc, *$2.string, $1, $3.arraySizes, $5);
         $$.intermNode = parseContext.intermediate.growAggregate(0, initNode, $4.loc);
     }
+    | fully_specified_type TYPE_NAME array_specifier EQUAL initializer {
+        $$.type = $1;
+        TIntermNode* initNode = parseContext.declareVariable($2.loc, *$2.string, $1, $3.arraySizes, $5);
+        $$.intermNode = parseContext.intermediate.growAggregate(0, initNode, $4.loc);
+    }
+
     | fully_specified_type IDENTIFIER EQUAL initializer {
         $$.type = $1;
         TIntermNode* initNode = parseContext.declareVariable($2.loc, *$2.string, $1, 0, $4);
         $$.intermNode = parseContext.intermediate.growAggregate(0, initNode, $3.loc);
     }
+    | fully_specified_type TYPE_NAME EQUAL initializer {
+        $$.type = $1;
+        TIntermNode* initNode = parseContext.declareVariable($2.loc, *$2.string, $1, 0, $4);
+        $$.intermNode = parseContext.intermediate.growAggregate(0, initNode, $3.loc);
+    }
+
 
 // Grammar Note:  No 'enum', or 'typedef'.
 

Разлика између датотеке није приказан због своје велике величине
+ 919 - 1248
glslang/MachineIndependent/glslang_tab.cpp


+ 1 - 0
gtests/CMakeLists.txt

@@ -56,6 +56,7 @@ if(GLSLANG_TESTS)
             ${CMAKE_CURRENT_SOURCE_DIR}/LiveTraverser.FromFile.cpp
             ${CMAKE_CURRENT_SOURCE_DIR}/Pp.FromFile.cpp
             ${CMAKE_CURRENT_SOURCE_DIR}/Spv.FromFile.cpp
+            ${CMAKE_CURRENT_SOURCE_DIR}/StructName.FromFile.cpp
             ${CMAKE_CURRENT_SOURCE_DIR}/VkRelaxed.FromFile.cpp
             ${CMAKE_CURRENT_SOURCE_DIR}/GlslMapIO.FromFile.cpp)
 

+ 322 - 0
gtests/StructName.FromFile.cpp

@@ -0,0 +1,322 @@
+// Copyright (C) 2025 NVIDIA Corporation
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+//    Redistributions of source code must retain the above copyright
+//    notice, this list of conditions and the following disclaimer.
+//
+//    Redistributions in binary form must reproduce the above
+//    copyright notice, this list of conditions and the following
+//    disclaimer in the documentation and/or other materials provided
+//    with the distribution.
+//
+//    Neither the name of The Khronos Group Inc. nor the names of its
+//    contributors may be used to endorse or promote products derived
+//    from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#include "TestFixture.h"
+
+namespace glslangtest {
+namespace {
+
+using StructNameTest = GlslangTest<::testing::Test>;
+
+// Test that struct names can be used as variable names in declarations (should NOT have syntax errors)
+TEST_F(StructNameTest, StructNameInDeclaration)
+{
+    const std::string inputFname = GlobalTestSettings.testRoot + "/struct_name_in_declaration.frag";
+    std::string input;
+    tryLoadFile(inputFname, "input", &input);
+    
+    EShMessages controls = DeriveOptions(Source::GLSL, Semantics::OpenGL, Target::AST);
+    GlslangResult result = compileAndLink("struct_name_in_declaration.frag", input, "", controls,
+                                         glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0);
+    
+    // Should NOT have the original syntax error
+    EXPECT_EQ(result.linkingError.find("syntax error, unexpected TYPE_NAME, expecting IDENTIFIER"), std::string::npos);
+    
+    // Should parse successfully (no syntax errors)
+    EXPECT_TRUE(result.linkingError.empty() || 
+                result.linkingError.find("syntax error") == std::string::npos);
+}
+
+// Test that struct names can be used as single variable names (should parse successfully, no redefinition error)
+TEST_F(StructNameTest, StructNameAsVariable)
+{
+    const std::string inputFname = GlobalTestSettings.testRoot + "/struct_name_as_variable.frag";
+    std::string input;
+    tryLoadFile(inputFname, "input", &input);
+    
+    EShMessages controls = DeriveOptions(Source::GLSL, Semantics::OpenGL, Target::AST);
+    GlslangResult result = compileAndLink("struct_name_as_variable.frag", input, "", controls,
+                                         glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0);
+    
+    // Should NOT have the original syntax error
+    EXPECT_EQ(result.linkingError.find("syntax error, unexpected TYPE_NAME, expecting IDENTIFIER"), std::string::npos);
+    // Should parse successfully (no syntax errors)
+    EXPECT_TRUE(result.linkingError.empty() || 
+                result.linkingError.find("syntax error") == std::string::npos);
+}
+
+// Test that struct names can be used as variable names with initializers (should parse successfully, no redefinition error)
+TEST_F(StructNameTest, StructNameAsVariableWithInitializer)
+{
+    const std::string inputFname = GlobalTestSettings.testRoot + "/struct_name_as_variable_with_initializer.frag";
+    std::string input;
+    tryLoadFile(inputFname, "input", &input);
+    
+    EShMessages controls = DeriveOptions(Source::GLSL, Semantics::OpenGL, Target::AST);
+    GlslangResult result = compileAndLink("struct_name_as_variable_with_initializer.frag", input, "", controls,
+                                         glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0);
+    
+    // Should NOT have the original syntax error
+    EXPECT_EQ(result.linkingError.find("syntax error, unexpected TYPE_NAME, expecting IDENTIFIER"), std::string::npos);
+    // Should parse successfully (no syntax errors)
+    EXPECT_TRUE(result.linkingError.empty() || 
+                result.linkingError.find("syntax error") == std::string::npos);
+}
+
+// Test that struct names can be used as variable names with arrays (should parse successfully, no redefinition error)
+TEST_F(StructNameTest, StructNameAsVariableWithArray)
+{
+    const std::string inputFname = GlobalTestSettings.testRoot + "/struct_name_as_variable_with_array.frag";
+    std::string input;
+    tryLoadFile(inputFname, "input", &input);
+    
+    EShMessages controls = DeriveOptions(Source::GLSL, Semantics::OpenGL, Target::AST);
+    GlslangResult result = compileAndLink("struct_name_as_variable_with_array.frag", input, "", controls,
+                                         glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0);
+    
+    // Should NOT have the original syntax error
+    EXPECT_EQ(result.linkingError.find("syntax error, unexpected TYPE_NAME, expecting IDENTIFIER"), std::string::npos);
+    // Should parse successfully (no syntax errors)
+    EXPECT_TRUE(result.linkingError.empty() || 
+                result.linkingError.find("syntax error") == std::string::npos);
+}
+
+// Test that struct names can be used as variable names with array initializers (should parse successfully, no redefinition error)
+TEST_F(StructNameTest, StructNameAsVariableWithArrayInitializer)
+{
+    const std::string inputFname = GlobalTestSettings.testRoot + "/struct_name_as_variable_with_array_initializer.frag";
+    std::string input;
+    tryLoadFile(inputFname, "input", &input);
+    
+    EShMessages controls = DeriveOptions(Source::GLSL, Semantics::OpenGL, Target::AST);
+    GlslangResult result = compileAndLink("struct_name_as_variable_with_array_initializer.frag", input, "", controls,
+                                         glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0);
+    
+    // Should NOT have the original syntax error
+    EXPECT_EQ(result.linkingError.find("syntax error, unexpected TYPE_NAME, expecting IDENTIFIER"), std::string::npos);
+    // Should parse successfully (no syntax errors)
+    EXPECT_TRUE(result.linkingError.empty() || 
+                result.linkingError.find("syntax error") == std::string::npos);
+}
+
+// Test that struct names can be used as struct member names (should parse successfully)
+TEST_F(StructNameTest, StructNameAsStructMember)
+{
+    const std::string inputFname = GlobalTestSettings.testRoot + "/struct_name_as_struct_member.frag";
+    std::string input;
+    tryLoadFile(inputFname, "input", &input);
+    
+    EShMessages controls = DeriveOptions(Source::GLSL, Semantics::OpenGL, Target::AST);
+    GlslangResult result = compileAndLink("struct_name_as_struct_member.frag", input, "", controls,
+                                         glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0);
+    
+    // Should NOT have the original syntax error
+    EXPECT_EQ(result.linkingError.find("syntax error, unexpected TYPE_NAME, expecting IDENTIFIER"), std::string::npos);
+    
+    // Should parse successfully
+    EXPECT_TRUE(result.linkingError.empty() || 
+                result.linkingError.find("syntax error") == std::string::npos);
+}
+
+// Test that struct names can be used as struct member names with arrays (should parse successfully)
+TEST_F(StructNameTest, StructNameAsStructMemberWithArray)
+{
+    const std::string inputFname = GlobalTestSettings.testRoot + "/struct_name_as_struct_member_with_array.frag";
+    std::string input;
+    tryLoadFile(inputFname, "input", &input);
+    
+    EShMessages controls = DeriveOptions(Source::GLSL, Semantics::OpenGL, Target::AST);
+    GlslangResult result = compileAndLink("struct_name_as_struct_member_with_array.frag", input, "", controls,
+                                         glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0);
+    
+    // Should NOT have the original syntax error
+    EXPECT_EQ(result.linkingError.find("syntax error, unexpected TYPE_NAME, expecting IDENTIFIER"), std::string::npos);
+    
+    // Should parse successfully
+    EXPECT_TRUE(result.linkingError.empty() || 
+                result.linkingError.find("syntax error") == std::string::npos);
+}
+
+// Test that multiple struct names can be used in the same declaration (should parse successfully, no redefinition error)
+TEST_F(StructNameTest, MultipleStructNames)
+{
+    const std::string inputFname = GlobalTestSettings.testRoot + "/multiple_struct_names.frag";
+    std::string input;
+    tryLoadFile(inputFname, "input", &input);
+    
+    EShMessages controls = DeriveOptions(Source::GLSL, Semantics::OpenGL, Target::AST);
+    GlslangResult result = compileAndLink("multiple_struct_names.frag", input, "", controls,
+                                         glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0);
+    
+    // Should NOT have the original syntax error
+    EXPECT_EQ(result.linkingError.find("syntax error, unexpected TYPE_NAME, expecting IDENTIFIER"), std::string::npos);
+    // Should parse successfully (no syntax errors)
+    EXPECT_TRUE(result.linkingError.empty() || 
+                result.linkingError.find("syntax error") == std::string::npos);
+}
+
+// Test that struct names can be used in function parameters (should parse successfully)
+TEST_F(StructNameTest, StructNameInFunctionParams)
+{
+    const std::string inputFname = GlobalTestSettings.testRoot + "/struct_name_in_function_params.frag";
+    std::string input;
+    tryLoadFile(inputFname, "input", &input);
+    
+    EShMessages controls = DeriveOptions(Source::GLSL, Semantics::OpenGL, Target::AST);
+    GlslangResult result = compileAndLink("struct_name_in_function_params.frag", input, "", controls,
+                                         glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0);
+    
+    // Should NOT have the original syntax error
+    EXPECT_EQ(result.linkingError.find("syntax error, unexpected TYPE_NAME, expecting IDENTIFIER"), std::string::npos);
+    
+    // Should parse successfully
+    EXPECT_TRUE(result.linkingError.empty() || 
+                result.linkingError.find("syntax error") == std::string::npos);
+}
+
+// Test that struct names can be used in function parameters with arrays (should parse successfully)
+TEST_F(StructNameTest, StructNameInFunctionParamsWithArray)
+{
+    const std::string inputFname = GlobalTestSettings.testRoot + "/struct_name_in_function_params_with_array.frag";
+    std::string input;
+    tryLoadFile(inputFname, "input", &input);
+    
+    EShMessages controls = DeriveOptions(Source::GLSL, Semantics::OpenGL, Target::AST);
+    GlslangResult result = compileAndLink("struct_name_in_function_params_with_array.frag", input, "", controls,
+                                         glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0);
+    
+    // Should NOT have the original syntax error
+    EXPECT_EQ(result.linkingError.find("syntax error, unexpected TYPE_NAME, expecting IDENTIFIER"), std::string::npos);
+    
+    // Should parse successfully
+    EXPECT_TRUE(result.linkingError.empty() || 
+                result.linkingError.find("syntax error") == std::string::npos);
+}
+
+// Test that struct names can be used in vertex shaders (should parse successfully)
+TEST_F(StructNameTest, StructNameInVertexShader)
+{
+    const std::string inputFname = GlobalTestSettings.testRoot + "/struct_name_in_vertex_shader.vert";
+    std::string input;
+    tryLoadFile(inputFname, "input", &input);
+    
+    EShMessages controls = DeriveOptions(Source::GLSL, Semantics::OpenGL, Target::AST);
+    GlslangResult result = compileAndLink("struct_name_in_vertex_shader.vert", input, "", controls,
+                                         glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0);
+    
+    // Should NOT have the original syntax error
+    EXPECT_EQ(result.linkingError.find("syntax error, unexpected TYPE_NAME, expecting IDENTIFIER"), std::string::npos);
+    
+    // Should parse successfully
+    EXPECT_TRUE(result.linkingError.empty() || 
+                result.linkingError.find("syntax error") == std::string::npos);
+}
+
+// Test that struct names can be used in compute shaders (should parse successfully)
+TEST_F(StructNameTest, StructNameInComputeShader)
+{
+    const std::string inputFname = GlobalTestSettings.testRoot + "/struct_name_in_compute_shader.comp";
+    std::string input;
+    tryLoadFile(inputFname, "input", &input);
+    
+    EShMessages controls = DeriveOptions(Source::GLSL, Semantics::OpenGL, Target::AST);
+    GlslangResult result = compileAndLink("struct_name_in_compute_shader.comp", input, "", controls,
+                                         glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0);
+    
+    // Should NOT have the original syntax error
+    EXPECT_EQ(result.linkingError.find("syntax error, unexpected TYPE_NAME, expecting IDENTIFIER"), std::string::npos);
+    
+    // Should parse successfully
+    EXPECT_TRUE(result.linkingError.empty() || 
+                result.linkingError.find("syntax error") == std::string::npos);
+}
+
+// Test that struct names can be used with qualifiers (should parse successfully)
+TEST_F(StructNameTest, StructNameWithQualifiers)
+{
+    const std::string inputFname = GlobalTestSettings.testRoot + "/struct_name_with_qualifiers.frag";
+    std::string input;
+    tryLoadFile(inputFname, "input", &input);
+    
+    EShMessages controls = DeriveOptions(Source::GLSL, Semantics::OpenGL, Target::AST);
+    GlslangResult result = compileAndLink("struct_name_with_qualifiers.frag", input, "", controls,
+                                         glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0);
+    
+    // Should NOT have the original syntax error
+    EXPECT_EQ(result.linkingError.find("syntax error, unexpected TYPE_NAME, expecting IDENTIFIER"), std::string::npos);
+    
+    // Should parse successfully
+    EXPECT_TRUE(result.linkingError.empty() || 
+                result.linkingError.find("syntax error") == std::string::npos);
+}
+
+// Test edge cases with struct names (should parse successfully)
+TEST_F(StructNameTest, StructNameEdgeCases)
+{
+    const std::string inputFname = GlobalTestSettings.testRoot + "/struct_name_edge_cases.frag";
+    std::string input;
+    tryLoadFile(inputFname, "input", &input);
+    
+    EShMessages controls = DeriveOptions(Source::GLSL, Semantics::OpenGL, Target::AST);
+    GlslangResult result = compileAndLink("struct_name_edge_cases.frag", input, "", controls,
+                                         glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0);
+    
+    // Should NOT have the original syntax error
+    EXPECT_EQ(result.linkingError.find("syntax error, unexpected TYPE_NAME, expecting IDENTIFIER"), std::string::npos);
+    
+    // Should parse successfully
+    EXPECT_TRUE(result.linkingError.empty() || 
+                result.linkingError.find("syntax error") == std::string::npos);
+}
+
+// Test that struct names can be used in the middle of variable declarations (should parse successfully)
+TEST_F(StructNameTest, StructNameInMiddle)
+{
+    const std::string inputFname = GlobalTestSettings.testRoot + "/struct_name_in_middle.frag";
+    std::string input;
+    tryLoadFile(inputFname, "input", &input);
+    
+    EShMessages controls = DeriveOptions(Source::GLSL, Semantics::OpenGL, Target::AST);
+    GlslangResult result = compileAndLink("struct_name_in_middle.frag", input, "", controls,
+                                         glslang::EShTargetVulkan_1_0, glslang::EShTargetSpv_1_0);
+    
+    // Should NOT have the original syntax error
+    EXPECT_EQ(result.linkingError.find("syntax error, unexpected TYPE_NAME, expecting IDENTIFIER"), std::string::npos);
+    // Should NOT have any syntax errors
+    EXPECT_EQ(result.linkingError.find("syntax error"), std::string::npos);
+    // Should NOT have redefinition errors (these are different variables)
+    EXPECT_EQ(result.linkingError.find("redefinition"), std::string::npos);
+}
+
+}  // anonymous namespace
+}  // namespace glslangtest 

Неке датотеке нису приказане због велике количине промена