summaryrefslogtreecommitdiffstats
path: root/Source/cmListFileCache.cxx
diff options
context:
space:
mode:
authorKyle Edwards <kyle.edwards@kitware.com>2020-10-20 14:47:38 (GMT)
committerKyle Edwards <kyle.edwards@kitware.com>2020-10-22 15:40:48 (GMT)
commit12f6e37eb79ad66c30269a3f19dfc28a9cb834e2 (patch)
tree056b3662153bfdae6859fd5030582ced45880eb0 /Source/cmListFileCache.cxx
parent67383725bd40a92db85738f8d0c120e2c9bb2100 (diff)
downloadCMake-12f6e37eb79ad66c30269a3f19dfc28a9cb834e2.zip
CMake-12f6e37eb79ad66c30269a3f19dfc28a9cb834e2.tar.gz
CMake-12f6e37eb79ad66c30269a3f19dfc28a9cb834e2.tar.bz2
cmListFileCache: Enforce proper nesting of flow control statements
Fixes: #19153
Diffstat (limited to 'Source/cmListFileCache.cxx')
-rw-r--r--Source/cmListFileCache.cxx118
1 files changed, 118 insertions, 0 deletions
diff --git a/Source/cmListFileCache.cxx b/Source/cmListFileCache.cxx
index 70ef5af..3658d11 100644
--- a/Source/cmListFileCache.cxx
+++ b/Source/cmListFileCache.cxx
@@ -30,6 +30,7 @@ struct cmListFileParser
bool ParseFunction(const char* name, long line);
bool AddArgument(cmListFileLexer_Token* token,
cmListFileArgument::Delimiter delim);
+ cm::optional<cmListFileContext> CheckNesting();
cmListFile* ListFile;
cmListFileBacktrace Backtrace;
cmMessenger* Messenger;
@@ -158,6 +159,17 @@ bool cmListFileParser::Parse()
return false;
}
}
+
+ // Check if all functions are nested properly.
+ if (auto badNesting = this->CheckNesting()) {
+ this->Messenger->IssueMessage(
+ MessageType::FATAL_ERROR,
+ "Flow control statements are not properly nested.",
+ this->Backtrace.Push(*badNesting));
+ cmSystemTools::SetFatalErrorOccured();
+ return false;
+ }
+
return true;
}
@@ -317,6 +329,112 @@ bool cmListFileParser::AddArgument(cmListFileLexer_Token* token,
return true;
}
+namespace {
+enum class NestingStateEnum
+{
+ If,
+ Else,
+ While,
+ Foreach,
+ Function,
+ Macro,
+};
+
+struct NestingState
+{
+ NestingStateEnum State;
+ cmListFileContext Context;
+};
+
+bool TopIs(std::vector<NestingState>& stack, NestingStateEnum state)
+{
+ return !stack.empty() && stack.back().State == state;
+}
+}
+
+cm::optional<cmListFileContext> cmListFileParser::CheckNesting()
+{
+ std::vector<NestingState> stack;
+
+ for (auto const& func : this->ListFile->Functions) {
+ auto const& name = func.LowerCaseName();
+ if (name == "if") {
+ stack.push_back({
+ NestingStateEnum::If,
+ cmListFileContext::FromCommandContext(func, this->FileName),
+ });
+ } else if (name == "elseif") {
+ if (!TopIs(stack, NestingStateEnum::If)) {
+ return cmListFileContext::FromCommandContext(func, this->FileName);
+ }
+ stack.back() = {
+ NestingStateEnum::If,
+ cmListFileContext::FromCommandContext(func, this->FileName),
+ };
+ } else if (name == "else") {
+ if (!TopIs(stack, NestingStateEnum::If)) {
+ return cmListFileContext::FromCommandContext(func, this->FileName);
+ }
+ stack.back() = {
+ NestingStateEnum::Else,
+ cmListFileContext::FromCommandContext(func, this->FileName),
+ };
+ } else if (name == "endif") {
+ if (!TopIs(stack, NestingStateEnum::If) &&
+ !TopIs(stack, NestingStateEnum::Else)) {
+ return cmListFileContext::FromCommandContext(func, this->FileName);
+ }
+ stack.pop_back();
+ } else if (name == "while") {
+ stack.push_back({
+ NestingStateEnum::While,
+ cmListFileContext::FromCommandContext(func, this->FileName),
+ });
+ } else if (name == "endwhile") {
+ if (!TopIs(stack, NestingStateEnum::While)) {
+ return cmListFileContext::FromCommandContext(func, this->FileName);
+ }
+ stack.pop_back();
+ } else if (name == "foreach") {
+ stack.push_back({
+ NestingStateEnum::Foreach,
+ cmListFileContext::FromCommandContext(func, this->FileName),
+ });
+ } else if (name == "endforeach") {
+ if (!TopIs(stack, NestingStateEnum::Foreach)) {
+ return cmListFileContext::FromCommandContext(func, this->FileName);
+ }
+ stack.pop_back();
+ } else if (name == "function") {
+ stack.push_back({
+ NestingStateEnum::Function,
+ cmListFileContext::FromCommandContext(func, this->FileName),
+ });
+ } else if (name == "endfunction") {
+ if (!TopIs(stack, NestingStateEnum::Function)) {
+ return cmListFileContext::FromCommandContext(func, this->FileName);
+ }
+ stack.pop_back();
+ } else if (name == "macro") {
+ stack.push_back({
+ NestingStateEnum::Macro,
+ cmListFileContext::FromCommandContext(func, this->FileName),
+ });
+ } else if (name == "endmacro") {
+ if (!TopIs(stack, NestingStateEnum::Macro)) {
+ return cmListFileContext::FromCommandContext(func, this->FileName);
+ }
+ stack.pop_back();
+ }
+ }
+
+ if (!stack.empty()) {
+ return stack.back().Context;
+ }
+
+ return cm::nullopt;
+}
+
// We hold either the bottom scope of a directory or a call/file context.
// Discriminate these cases via the parent pointer.
struct cmListFileBacktrace::Entry