mirror of
https://github.com/Kitware/CMake.git
synced 2026-05-08 23:29:55 -05:00
Fortran: Tolerate #include loops in dependency scanner
The function `cmFortranParser_FilePush` was always processing the provided (included) file, even though it may have been processed before. This lead to infinite recursion in cases where the include guards were not properly interpreted, e.g. `# if !defined` instead of `#ifndef`. This commit introduces a cache of paths to already processed files. These files are now ignored in `cmFortranParser_FilePush` (treated as non-existing). Fixes: #27238
This commit is contained in:
@@ -163,6 +163,9 @@ struct cmFortranParser_s
|
||||
// Lexical scanner instance.
|
||||
yyscan_t Scanner;
|
||||
|
||||
// List of full paths to already processed files.
|
||||
std::set<std::string> VisitedFilePaths;
|
||||
|
||||
// Stack of open files in the translation unit.
|
||||
std::stack<cmFortranFile> FileStack;
|
||||
|
||||
|
||||
@@ -87,17 +87,22 @@ std::string cmFortranParser_s::SModName(std::string const& mod_name,
|
||||
|
||||
bool cmFortranParser_FilePush(cmFortranParser* parser, char const* fname)
|
||||
{
|
||||
// Open the new file and push it onto the stack. Save the old
|
||||
// buffer with it on the stack.
|
||||
if (FILE* file = cmsys::SystemTools::Fopen(fname, "rb")) {
|
||||
YY_BUFFER_STATE current = cmFortranLexer_GetCurrentBuffer(parser->Scanner);
|
||||
std::string dir = cmSystemTools::GetParentDirectory(fname);
|
||||
cmFortranFile f(file, current, dir);
|
||||
YY_BUFFER_STATE buffer =
|
||||
cmFortran_yy_create_buffer(nullptr, 16384, parser->Scanner);
|
||||
cmFortran_yy_switch_to_buffer(buffer, parser->Scanner);
|
||||
parser->FileStack.push(f);
|
||||
return true;
|
||||
// Do not revisit already processed files; treat them as non-existing.
|
||||
// Avoids infinite recursion caused by misinterpreted include guards.
|
||||
if (parser->VisitedFilePaths.insert(fname).second) {
|
||||
// Open the new file and push it onto the stack. Save the old
|
||||
// buffer with it on the stack.
|
||||
if (FILE* file = cmsys::SystemTools::Fopen(fname, "rb")) {
|
||||
YY_BUFFER_STATE current =
|
||||
cmFortranLexer_GetCurrentBuffer(parser->Scanner);
|
||||
std::string dir = cmSystemTools::GetParentDirectory(fname);
|
||||
cmFortranFile f(file, current, dir);
|
||||
YY_BUFFER_STATE buffer =
|
||||
cmFortran_yy_create_buffer(nullptr, 16384, parser->Scanner);
|
||||
cmFortran_yy_switch_to_buffer(buffer, parser->Scanner);
|
||||
parser->FileStack.push(f);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -198,3 +198,9 @@ if(CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_OFF AND
|
||||
|
||||
set_property(SOURCE no_preprocess_source_upper.F no_preprocess_source_fpp.fpp PROPERTY Fortran_PREPROCESS OFF)
|
||||
endif()
|
||||
|
||||
# Test that we are safe from infinite include recursion in dependency scanner
|
||||
# (SunPro Fortran compiler does not support recursive inclusion)
|
||||
if (NOT CMAKE_Fortran_COMPILER_ID STREQUAL "SunPro")
|
||||
add_executable(test_include_loop test_include_loop.F)
|
||||
endif()
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
program test_include_loop
|
||||
#include "test_include_loop.fh"
|
||||
end program
|
||||
@@ -0,0 +1,14 @@
|
||||
! CMake Fortran parser does not understand the `#if !defined(...)`
|
||||
! syntax, so it does not guard against recursive inclusion. Unless
|
||||
! the parser keeps track of already visited files, this leads to
|
||||
! processing of around 2^N files, where N is the maximal number of
|
||||
! open files. Because N is typically of the order of 1000, without
|
||||
! bookkeeping this test hangs.
|
||||
|
||||
#if !defined(test_include_loop_fh)
|
||||
#define test_include_loop_fh
|
||||
|
||||
#include "test_include_loop.fh"
|
||||
#include "test_include_loop.fh"
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user