From 4febbe5d0dc06f79e2ac8ea772197df70360cacb Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Mon, 21 Aug 2023 17:15:55 -0400 Subject: [PATCH] Fix #line directives --- cxxheaderparser/lexer.py | 15 +++++++-------- tests/test_misc.py | 16 ++++++++++++++++ 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/cxxheaderparser/lexer.py b/cxxheaderparser/lexer.py index 7003821..b62e660 100644 --- a/cxxheaderparser/lexer.py +++ b/cxxheaderparser/lexer.py @@ -19,7 +19,7 @@ if sys.version_info >= (3, 8): else: Protocol = object -_line_re = re.compile(r'^#line (\d+) "(.*)"') +_line_re = re.compile(r'^\#[\t ]*line (\d+) "(.*)"') _multicomment_re = re.compile("\n[\\s]+\\*") @@ -176,7 +176,6 @@ class PlyLexer: # Comments "COMMENT_SINGLELINE", "COMMENT_MULTILINE", - "LINE_DIRECTIVE", "PRAGMA_DIRECTIVE", "INCLUDE_DIRECTIVE", "PP_DIRECTIVE", @@ -438,12 +437,6 @@ class PlyLexer: t.type = t.value return t - @TOKEN(r'\#[\t ]*line (\d+) "(.*)"') - def t_LINE_DIRECTIVE(self, t: LexToken) -> None: - m = t.lexmatch - self.filename = m.group(2) - self.line_offset = 1 + self.lex.lineno - int(m.group(1)) - @TOKEN(r"\#[\t ]*pragma") def t_PRAGMA_DIRECTIVE(self, t: LexToken) -> LexToken: return t @@ -454,6 +447,12 @@ class PlyLexer: @TOKEN(r"\#(.*)") def t_PP_DIRECTIVE(self, t: LexToken): + # handle line macros + m = _line_re.match(t.value) + if m: + self.filename = m.group(2) + self.line_offset = 1 + self.lex.lineno - int(m.group(1)) + return None # ignore C++23 warning directive if t.value.startswith("#warning"): return diff --git a/tests/test_misc.py b/tests/test_misc.py index b8bd73c..f0bad01 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -1,5 +1,6 @@ # Note: testcases generated via `python -m cxxheaderparser.gentest` +from cxxheaderparser.errors import CxxParseError from cxxheaderparser.types import ( BaseClass, ClassDecl, @@ -22,6 +23,8 @@ from cxxheaderparser.simple import ( ParsedData, ) +import pytest + # # minimal preprocessor support # @@ -93,6 +96,19 @@ def test_pragma_more() -> None: ) +def test_line_and_define() -> None: + content = """ + // this should work + change line number of error + #line 40 "filename.h" + // this should fail + #define 1 + """ + with pytest.raises(CxxParseError) as e: + parse_string(content, cleandoc=True) + + assert "filename.h:41" in str(e.value) + + # # extern "C" #