From cb2197105fd35d277885d7ba8d4f00056b9da234 Mon Sep 17 00:00:00 2001 From: Laurent DORE Date: Mon, 14 Nov 2022 09:51:14 +0100 Subject: [PATCH] Fix #478 (pragma-before-semicolon) --- pycparser/c_parser.py | 54 +++++++++++---- tests/test_c_parser.py | 146 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 186 insertions(+), 14 deletions(-) diff --git a/pycparser/c_parser.py b/pycparser/c_parser.py index c9782e09..9360a6e4 100644 --- a/pycparser/c_parser.py +++ b/pycparser/c_parser.py @@ -480,6 +480,13 @@ def _select_struct_union_class(self, token): else: return c_ast.Union + def _to_list(self, p): + """ Normalize the input to a list: if given a list, return as-is, + else return single-element list. + """ + return p if isinstance(p, list) else [p] + + ## ## Precedence and associativity of operators ## @@ -682,10 +689,10 @@ def p_pragmacomp_or_statement(self, p): """ if len(p) == 3: p[0] = c_ast.Compound( - block_items=p[1]+[p[2]], + block_items=p[1] + self._to_list(p[2]), coord=self._token_coord(p, 1)) else: - p[0] = p[1] + p[0] = c_ast.Compound(block_items=p[1], coord=self._token_coord(p, 1)) if isinstance(p[1], list) else p[1] # In C, declarations can come several in a line: # int x, *px, romulo = 5; @@ -759,8 +766,9 @@ def p_decl_body(self, p): # line is reached. def p_declaration(self, p): """ declaration : decl_body SEMI + | decl_body pppragma_directive_list SEMI """ - p[0] = p[1] + p[0] = p[1] if len(p) == 3 else self._to_list(p[1]) + p[2] # Since each declaration is a list of declarations, this # rule will combine all the declarations and return a single @@ -1529,7 +1537,7 @@ def p_block_item(self, p): """ block_item : declaration | statement """ - p[0] = p[1] if isinstance(p[1], list) else [p[1]] + p[0] = self._to_list(p[1]) # Since we made block_item a list, this just combines lists # @@ -1563,7 +1571,7 @@ def p_selection_statement_1(self, p): p[0] = c_ast.If(p[3], p[5], None, self._token_coord(p, 1)) def p_selection_statement_2(self, p): - """ selection_statement : IF LPAREN expression RPAREN statement ELSE pragmacomp_or_statement """ + """ selection_statement : IF LPAREN expression RPAREN pragmacomp_or_statement ELSE pragmacomp_or_statement """ p[0] = c_ast.If(p[3], p[5], p[7], self._token_coord(p, 1)) def p_selection_statement_3(self, p): @@ -1576,8 +1584,10 @@ def p_iteration_statement_1(self, p): p[0] = c_ast.While(p[3], p[5], self._token_coord(p, 1)) def p_iteration_statement_2(self, p): - """ iteration_statement : DO pragmacomp_or_statement WHILE LPAREN expression RPAREN SEMI """ - p[0] = c_ast.DoWhile(p[5], p[2], self._token_coord(p, 1)) + """ iteration_statement : DO pragmacomp_or_statement WHILE LPAREN expression RPAREN SEMI + | DO pragmacomp_or_statement WHILE LPAREN expression RPAREN pppragma_directive_list SEMI + """ + p[0] = c_ast.DoWhile(p[5], p[2], self._token_coord(p, 1)) if len(p) == 8 else [c_ast.DoWhile(p[5], p[2], self._token_coord(p, 1))] + p[7] def p_iteration_statement_3(self, p): """ iteration_statement : FOR LPAREN expression_opt SEMI expression_opt SEMI expression_opt RPAREN pragmacomp_or_statement """ @@ -1589,16 +1599,22 @@ def p_iteration_statement_4(self, p): p[4], p[6], p[8], self._token_coord(p, 1)) def p_jump_statement_1(self, p): - """ jump_statement : GOTO ID SEMI """ - p[0] = c_ast.Goto(p[2], self._token_coord(p, 1)) + """ jump_statement : GOTO ID SEMI + | GOTO ID pppragma_directive_list SEMI + """ + p[0] = c_ast.Goto(p[2], self._token_coord(p, 1)) if len(p) == 4 else [c_ast.Goto(p[2], self._token_coord(p, 1))] + p[3] def p_jump_statement_2(self, p): - """ jump_statement : BREAK SEMI """ - p[0] = c_ast.Break(self._token_coord(p, 1)) + """ jump_statement : BREAK SEMI + | BREAK pppragma_directive_list SEMI + """ + p[0] = c_ast.Break(self._token_coord(p, 1)) if len(p) == 3 else [c_ast.Break(self._token_coord(p, 1))] + p[2] def p_jump_statement_3(self, p): - """ jump_statement : CONTINUE SEMI """ - p[0] = c_ast.Continue(self._token_coord(p, 1)) + """ jump_statement : CONTINUE SEMI + | CONTINUE pppragma_directive_list SEMI + """ + p[0] = c_ast.Continue(self._token_coord(p, 1)) if len(p) == 3 else [c_ast.Continue(self._token_coord(p, 1))] + p[2] def p_jump_statement_4(self, p): """ jump_statement : RETURN expression SEMI @@ -1606,10 +1622,20 @@ def p_jump_statement_4(self, p): """ p[0] = c_ast.Return(p[2] if len(p) == 4 else None, self._token_coord(p, 1)) + def p_jump_statement_5(self, p): + """ jump_statement : RETURN expression pppragma_directive_list SEMI + | RETURN pppragma_directive_list SEMI + """ + p[0] = [c_ast.Return(p[2] if len(p) == 5 else None, self._token_coord(p, 1))] + p[3 if len(p) == 5 else 2] + def p_expression_statement(self, p): - """ expression_statement : expression_opt SEMI """ + """ expression_statement : expression_opt SEMI + | expression pppragma_directive_list SEMI + """ if p[1] is None: p[0] = c_ast.EmptyStatement(self._token_coord(p, 2)) + elif len(p) == 4: + p[0] = [p[1]] + p[2] else: p[0] = p[1] diff --git a/tests/test_c_parser.py b/tests/test_c_parser.py index 9b536226..08e6a0e2 100755 --- a/tests/test_c_parser.py +++ b/tests/test_c_parser.py @@ -1810,6 +1810,152 @@ def test_pragmacomp_or_statement(self): self.assertIsInstance(s1_ast.ext[0].body.block_items[5].stmt.stmts[0].block_items[1], Assignment) + def test_pragma_in_statement(self): + s1 = r''' + void f(void) { + a = 1; + a = 1 + #pragma foo + ; + a = 1 + #pragma bar + #pragma fuz + ; + f(); + f() + #pragma foo + ; + f() + #pragma bar + #pragma fuz + ; + + do { + break + #pragma in_break + ; + } while (1) + #pragma after_while + ; + + if (1) + a += 1 + #pragma in_if + ; + else + a +=2 + #pragma in_else + ; + + return + #pragma after_return + ; + return 1 + #pragma after_return + ; + + goto target + #pragma after_goto + ; + + if (1) + a += 1 + #pragma in_if_noelse + ; + + while (0) + a += 1 + #pragma in_while + ; + + target: + return; + } + struct knote; + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wnullability-completeness" + struct klist { struct knote *slh_first; } + #pragma clang diagnostic pop + ; + ''' + s1_ast = self.parse(s1) + self.assertIsInstance(s1_ast.ext[0].body.block_items[2], Pragma) + self.assertEqual(s1_ast.ext[0].body.block_items[2].string, 'foo') + self.assertEqual(s1_ast.ext[0].body.block_items[2].coord.line, 5) + + self.assertIsInstance(s1_ast.ext[0].body.block_items[4], Pragma) + self.assertEqual(s1_ast.ext[0].body.block_items[4].string, 'bar') + self.assertEqual(s1_ast.ext[0].body.block_items[4].coord.line, 8) + + self.assertIsInstance(s1_ast.ext[0].body.block_items[5], Pragma) + self.assertEqual(s1_ast.ext[0].body.block_items[5].string, 'fuz') + self.assertEqual(s1_ast.ext[0].body.block_items[5].coord.line, 9) + + self.assertIsInstance(s1_ast.ext[0].body.block_items[8], Pragma) + self.assertEqual(s1_ast.ext[0].body.block_items[8].string, 'foo') + self.assertEqual(s1_ast.ext[0].body.block_items[8].coord.line, 13) + + self.assertIsInstance(s1_ast.ext[0].body.block_items[10], Pragma) + self.assertEqual(s1_ast.ext[0].body.block_items[10].string, 'bar') + self.assertEqual(s1_ast.ext[0].body.block_items[10].coord.line, 16) + + self.assertIsInstance(s1_ast.ext[0].body.block_items[11], Pragma) + self.assertEqual(s1_ast.ext[0].body.block_items[11].string, 'fuz') + self.assertEqual(s1_ast.ext[0].body.block_items[11].coord.line, 17) + + self.assertIsInstance(s1_ast.ext[0].body.block_items[12], DoWhile) + self.assertIsInstance(s1_ast.ext[0].body.block_items[12].stmt, Compound) + self.assertIsInstance(s1_ast.ext[0].body.block_items[12].stmt.block_items[1], Pragma) + self.assertEqual(s1_ast.ext[0].body.block_items[12].stmt.block_items[1].string, 'in_break') + self.assertEqual(s1_ast.ext[0].body.block_items[12].stmt.block_items[1].coord.line, 22) + + self.assertIsInstance(s1_ast.ext[0].body.block_items[13], Pragma) + self.assertEqual(s1_ast.ext[0].body.block_items[13].string, 'after_while') + self.assertEqual(s1_ast.ext[0].body.block_items[13].coord.line, 25) + + self.assertIsInstance(s1_ast.ext[0].body.block_items[14], If) + self.assertIsInstance(s1_ast.ext[0].body.block_items[14].iftrue, Compound) + self.assertIsInstance(s1_ast.ext[0].body.block_items[14].iftrue.block_items[1], Pragma) + self.assertEqual(s1_ast.ext[0].body.block_items[14].iftrue.block_items[1].string, 'in_if') + self.assertEqual(s1_ast.ext[0].body.block_items[14].iftrue.block_items[1].coord.line, 30) + self.assertIsInstance(s1_ast.ext[0].body.block_items[14].iffalse, Compound) + self.assertIsInstance(s1_ast.ext[0].body.block_items[14].iffalse.block_items[1], Pragma) + self.assertEqual(s1_ast.ext[0].body.block_items[14].iffalse.block_items[1].string, 'in_else') + self.assertEqual(s1_ast.ext[0].body.block_items[14].iffalse.block_items[1].coord.line, 34) + + self.assertIsInstance(s1_ast.ext[0].body.block_items[15], Return) + self.assertIsInstance(s1_ast.ext[0].body.block_items[16], Pragma) + self.assertEqual(s1_ast.ext[0].body.block_items[16].string, 'after_return') + self.assertEqual(s1_ast.ext[0].body.block_items[16].coord.line, 38) + + self.assertIsInstance(s1_ast.ext[0].body.block_items[17], Return) + self.assertIsInstance(s1_ast.ext[0].body.block_items[18], Pragma) + self.assertEqual(s1_ast.ext[0].body.block_items[18].string, 'after_return') + self.assertEqual(s1_ast.ext[0].body.block_items[18].coord.line, 41) + + self.assertIsInstance(s1_ast.ext[0].body.block_items[19], Goto) + self.assertIsInstance(s1_ast.ext[0].body.block_items[20], Pragma) + self.assertEqual(s1_ast.ext[0].body.block_items[20].string, 'after_goto') + self.assertEqual(s1_ast.ext[0].body.block_items[20].coord.line, 45) + + self.assertIsInstance(s1_ast.ext[0].body.block_items[21], If) + self.assertIsInstance(s1_ast.ext[0].body.block_items[21].iftrue, Compound) + self.assertIsInstance(s1_ast.ext[0].body.block_items[21].iftrue.block_items[1], Pragma) + self.assertEqual(s1_ast.ext[0].body.block_items[21].iftrue.block_items[1].string, 'in_if_noelse') + self.assertEqual(s1_ast.ext[0].body.block_items[21].iftrue.block_items[1].coord.line, 50) + + self.assertIsInstance(s1_ast.ext[0].body.block_items[22], While) + self.assertIsInstance(s1_ast.ext[0].body.block_items[22].stmt, Compound) + self.assertIsInstance(s1_ast.ext[0].body.block_items[22].stmt.block_items[1], Pragma) + self.assertEqual(s1_ast.ext[0].body.block_items[22].stmt.block_items[1].string, 'in_while') + self.assertEqual(s1_ast.ext[0].body.block_items[22].stmt.block_items[1].coord.line, 55) + + self.assertIsInstance(s1_ast.ext[1], Decl) + self.assertIsInstance(s1_ast.ext[2], Pragma) + self.assertIsInstance(s1_ast.ext[3], Pragma) + self.assertIsInstance(s1_ast.ext[4], Decl) + self.assertIsInstance(s1_ast.ext[5], Pragma) + class TestCParser_whole_code(TestCParser_base): """ Testing of parsing whole chunks of code.