diff options
| author | Yong He <yonghe@outlook.com> | 2025-10-10 17:31:01 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-10-11 00:31:01 +0000 |
| commit | fb34bafd37e3509d51686ee2a5392d2d8e29d7c5 (patch) | |
| tree | ca16f2f53595d730319396320b21ad15fada7a06 | |
| parent | c99addbf2e8a0210b97dad2827045dad95765d08 (diff) | |
Add diagnostic for cyclic #include. (#8679)
| -rw-r--r-- | source/slang/slang-diagnostic-defs.h | 1 | ||||
| -rw-r--r-- | source/slang/slang-preprocessor.cpp | 34 | ||||
| -rw-r--r-- | tests/preprocessor/circular-include/a.slang | 7 | ||||
| -rw-r--r-- | tests/preprocessor/circular-include/b.slang | 1 | ||||
| -rw-r--r-- | tests/preprocessor/duplicate-include/a.slang | 13 | ||||
| -rw-r--r-- | tests/preprocessor/duplicate-include/b.slang | 3 |
6 files changed, 51 insertions, 8 deletions
diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h index d31247623..840192a50 100644 --- a/source/slang/slang-diagnostic-defs.h +++ b/source/slang/slang-diagnostic-defs.h @@ -383,6 +383,7 @@ DIAGNOSTIC(-1, Note, seeOpeningToken, "see opening '$0'") // 153xx - #include DIAGNOSTIC(15300, Error, includeFailed, "failed to find include file '$0'") DIAGNOSTIC(15301, Error, importFailed, "failed to find imported file '$0'") +DIAGNOSTIC(15302, Error, cyclicInclude, "cyclic `#include` of file '$0'") DIAGNOSTIC(-1, Error, noIncludeHandlerSpecified, "no `#include` handler was specified") DIAGNOSTIC( 15302, diff --git a/source/slang/slang-preprocessor.cpp b/source/slang/slang-preprocessor.cpp index a0e80473d..ff3e5294e 100644 --- a/source/slang/slang-preprocessor.cpp +++ b/source/slang/slang-preprocessor.cpp @@ -1288,6 +1288,10 @@ struct Preprocessor /// stop them from being included again. HashSet<String> pragmaOnceUniqueIdentities; + /// The unique identities of any paths that have been included already. + /// This is used to detect cycles in #includes. + HashSet<String> includedFiles; + WarningStateTracker* warningStateTracker = nullptr; /// Name pool to use when creating `Name`s from strings @@ -1316,7 +1320,7 @@ struct Preprocessor SourceLoc::RawValue absoluteSourceLocCounter = 0; /// Push a new input file onto the input stack of the preprocessor - void pushInputFile(InputFile* inputFile, SourceLoc location); + void pushInputFile(InputFile* inputFile, SourceLoc location, String fileIdentity); /// Pop the inner-most input file from the stack of input files void popInputFile(); @@ -3479,7 +3483,7 @@ static SlangResult readFile( return SLANG_OK; } -void Preprocessor::pushInputFile(InputFile* inputFile, SourceLoc loc) +void Preprocessor::pushInputFile(InputFile* inputFile, SourceLoc loc, String fileIdentity) { if (m_currentInputFile) { @@ -3488,13 +3492,12 @@ void Preprocessor::pushInputFile(InputFile* inputFile, SourceLoc loc) absoluteSourceLocCounter += offset; } - { - SourceView* sourceView = inputFile->getLexer()->m_sourceView; - sourceView->setAbsoluteLocationBase(absoluteSourceLocCounter); - } + SourceView* sourceView = inputFile->getLexer()->m_sourceView; + sourceView->setAbsoluteLocationBase(absoluteSourceLocCounter); inputFile->m_parent = m_currentInputFile; m_currentInputFile = inputFile; + includedFiles.add(fileIdentity); } // Handle a `#include` directive @@ -3600,6 +3603,17 @@ static void HandleIncludeDirective(PreprocessorDirectiveContext* context) sourceManager->addSourceFile(filePathInfo.uniqueIdentity, sourceFile); } + auto fileIdentity = sourceFile->getPathInfo().getMostUniqueIdentity(); + if (context->m_preprocessor->includedFiles.contains(fileIdentity)) + { + // This file has already been included, we should diagnose an error and return. + GetSink(context)->diagnose( + pathToken.loc, + Diagnostics::cyclicInclude, + pathToken.getContent()); + return; + } + // If we are running the preprocessor as part of compiling a // specific module, then we must keep track of the file we've // read as yet another file that the module will depend on. @@ -3615,7 +3629,7 @@ static void HandleIncludeDirective(PreprocessorDirectiveContext* context) InputFile* inputFile = new InputFile(context->m_preprocessor, sourceView); - context->m_preprocessor->pushInputFile(inputFile, directiveLoc); + context->m_preprocessor->pushInputFile(inputFile, directiveLoc, fileIdentity); } static void _parseMacroOps( @@ -4624,6 +4638,7 @@ void Preprocessor::popInputFile() auto lastSegment = sourceView->getLastSegment(); absoluteSourceLocCounter += SourceRange(lastSegment.begin, sourceView->getRange().end).getSize(); + includedFiles.remove(sourceView->getSourceFile()->getPathInfo().getMostUniqueIdentity()); } // We will update the current file to the parent of whatever @@ -4957,7 +4972,10 @@ TokenList preprocessSource( // create an initial input stream based on the provided buffer InputFile* primaryInputFile = new InputFile(&preprocessor, sourceView); - preprocessor.pushInputFile(primaryInputFile, sourceView->getRange().begin); + preprocessor.pushInputFile( + primaryInputFile, + sourceView->getRange().begin, + file->getPathInfo().getMostUniqueIdentity()); } TokenList tokens = ReadAllTokens(&preprocessor); diff --git a/tests/preprocessor/circular-include/a.slang b/tests/preprocessor/circular-include/a.slang new file mode 100644 index 000000000..fe519dd12 --- /dev/null +++ b/tests/preprocessor/circular-include/a.slang @@ -0,0 +1,7 @@ +//TEST:SIMPLE(filecheck=CHECK): -target spirv + +// Test that we can diagnose an error when there is a circular include. + +// CHECK: error 15302: cyclic `#include` of file + +#include "b.slang"
\ No newline at end of file diff --git a/tests/preprocessor/circular-include/b.slang b/tests/preprocessor/circular-include/b.slang new file mode 100644 index 000000000..1f259de9a --- /dev/null +++ b/tests/preprocessor/circular-include/b.slang @@ -0,0 +1 @@ +#include "a.slang"
\ No newline at end of file diff --git a/tests/preprocessor/duplicate-include/a.slang b/tests/preprocessor/duplicate-include/a.slang new file mode 100644 index 000000000..ce2c22b61 --- /dev/null +++ b/tests/preprocessor/duplicate-include/a.slang @@ -0,0 +1,13 @@ +//TEST:INTERPRET(filecheck=CHECK): + +// Test that it is OK to include the same file multiple times. + +#include "b.slang" + +#include "b.slang" + +void main() +{ + // CHECK: result: 23 + printf("result: %d\n", foo()); +} diff --git a/tests/preprocessor/duplicate-include/b.slang b/tests/preprocessor/duplicate-include/b.slang new file mode 100644 index 000000000..c3769f038 --- /dev/null +++ b/tests/preprocessor/duplicate-include/b.slang @@ -0,0 +1,3 @@ +#pragma once + +int foo() { return 23; }
\ No newline at end of file |
