diff --git a/codebasin/file_source.py b/codebasin/file_source.py index 94ac5cb..dc6a189 100644 --- a/codebasin/file_source.py +++ b/codebasin/file_source.py @@ -460,17 +460,14 @@ def logical_result(self): ) -def c_file_source(fp, relaxed=False, directives_only=False): +def c_file_source(fp, directives_only=False): """ Process file fp in terms of logical (sloc) and physical lines of C code. Yield blocks of logical lines of code with physical extents. Return total lines at exit. - Relaxed allows for inconsistent state at the end of parsing, usefule for - special composition cases. directives_only sets up parser to only process directive lines such that the output can be fed to another file source (i.e. Fortran). """ - current_physical_line = one_space_line() cleaner = c_cleaner(current_physical_line, directives_only) @@ -479,6 +476,7 @@ def c_file_source(fp, relaxed=False, directives_only=False): total_sloc = 0 physical_line_num = 0 + continued = False for physical_line_num, line in enumerate(fp, start=1): current_physical_line.__init__() end = len(line) @@ -513,22 +511,28 @@ def c_file_source(fp, relaxed=False, directives_only=False): yield curr_line total_sloc += curr_line.physical_reset() - if not relaxed and not cleaner.state == ["TOPLEVEL"]: + + # Even if code is technically wrong, we should only fail when necessary. + parsing_failed = not cleaner.state == ["TOPLEVEL"] + if continued: + log.warning("backslash-newline at end of file") + parsing_failed = False + + if parsing_failed: raise RuntimeError( - "Parser must end at top level without 'relaxed' mode.", + "Parsing failed. Please open a bug report at: " + "https://github.com/intel/code-base-investigator/issues/new?template=bug_report.yml", # noqa: E501 ) return (total_sloc, total_physical_lines) -def fortran_file_source(fp, relaxed=False): +def fortran_file_source(fp): """ Process file fp in terms of logical (sloc) and physical lines of fixed-form Fortran code. Yield blocks of logical lines of code with physical extents. Return total lines at exit. - Relaxed allows for inconsistent state at the end of parsing, usefule for - special composition cases. """ current_physical_line = one_space_line() @@ -593,9 +597,12 @@ def fortran_file_source(fp, relaxed=False): yield curr_line total_sloc += curr_line.physical_reset() - if not relaxed and not cleaner.state == ["TOPLEVEL"]: + + parsing_failed = not cleaner.state == ["TOPLEVEL"] + if parsing_failed: raise RuntimeError( - "Parser must end at top level without 'relaxed' mode.", + "Parsing failed. Please open a bug report at: " + "https://github.com/intel/code-base-investigator/issues/new?template=bug_report.yml", # noqa: E501 ) return (total_sloc, total_physical_lines) @@ -642,7 +649,7 @@ def process(self, lineiter): pass -def asm_file_source(fp, relaxed=False): +def asm_file_source(fp): """ Process file fp in terms of logical (sloc) and physical lines of ASM code. Yield blocks of logical lines of code with physical extents. diff --git a/codebasin/finder.py b/codebasin/finder.py index 5c376bc..8986974 100644 --- a/codebasin/finder.py +++ b/codebasin/finder.py @@ -176,6 +176,7 @@ def find( leave=False, disable=not show_progress, ): + log.debug(f"Parsing {f}") state.insert_file(f) # Process each tree, by associating nodes with platforms diff --git a/tests/preprocessor/__init__.py b/tests/preprocessor/__init__.py new file mode 100644 index 0000000..93af6d4 --- /dev/null +++ b/tests/preprocessor/__init__.py @@ -0,0 +1,2 @@ +# Copyright (C) 2019 Intel Corporation +# SPDX-License-Identifier: BSD-3-Clause diff --git a/tests/preprocessor/test_warnings.py b/tests/preprocessor/test_warnings.py new file mode 100644 index 0000000..77da6e9 --- /dev/null +++ b/tests/preprocessor/test_warnings.py @@ -0,0 +1,42 @@ +# Copyright (C) 2019-2024 Intel Corporation +# SPDX-License-Identifier: BSD-3-Clause + +import logging +import os +import tempfile +import unittest +from pathlib import Path + +from codebasin import file_parser + + +class TestPreprocessorWarnings(unittest.TestCase): + """ + Test that preprocessor generates warnings for weird corner cases. + """ + + def setUp(self): + self.cwd = os.getcwd() + + def tearDown(self): + os.chdir(self.cwd) + + def test_backslash_eof(self): + """Check backslash-newline at EOF is only a warning""" + tmp = tempfile.TemporaryDirectory() + path = Path(tmp.name) + os.chdir(tmp.name) + + with open(path / "test.hpp", mode="w") as f: + f.write("#define BAD_MACRO \\\n") + + parser = file_parser.FileParser(path / "test.hpp") + + logging.disable(logging.NOTSET) + logger = logging.getLogger("codebasin") + with self.assertLogs(logger, level="WARNING") as cm: + _ = parser.parse_file() + logging.disable() + self.assertRegex(cm.output[0], "backslash-newline at end of file") + + tmp.cleanup()