Merge topic 'no-override-flow-control'

8aee7fdb32 cmState: Prohibit override of flow control commands

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !5409
diff --git a/Source/cmCommands.cxx b/Source/cmCommands.cxx
index 37be542..9e5b783 100644
--- a/Source/cmCommands.cxx
+++ b/Source/cmCommands.cxx
@@ -119,12 +119,19 @@
 
 void GetScriptingCommands(cmState* state)
 {
-  state->AddBuiltinCommand("break", cmBreakCommand);
+  state->AddFlowControlCommand("break", cmBreakCommand);
+  state->AddFlowControlCommand("continue", cmContinueCommand);
+  state->AddFlowControlCommand("foreach", cmForEachCommand);
+  state->AddFlowControlCommand("function", cmFunctionCommand);
+  state->AddFlowControlCommand("if", cmIfCommand);
+  state->AddFlowControlCommand("macro", cmMacroCommand);
+  state->AddFlowControlCommand("return", cmReturnCommand);
+  state->AddFlowControlCommand("while", cmWhileCommand);
+
   state->AddBuiltinCommand("cmake_minimum_required", cmCMakeMinimumRequired);
   state->AddBuiltinCommand("cmake_path", cmCMakePathCommand);
   state->AddBuiltinCommand("cmake_policy", cmCMakePolicyCommand);
   state->AddBuiltinCommand("configure_file", cmConfigureFileCommand);
-  state->AddBuiltinCommand("continue", cmContinueCommand);
   state->AddBuiltinCommand("exec_program", cmExecProgramCommand);
   state->AddBuiltinCommand("execute_process", cmExecuteProcessCommand);
   state->AddBuiltinCommand("file", cmFileCommand);
@@ -133,26 +140,21 @@
   state->AddBuiltinCommand("find_package", cmFindPackage);
   state->AddBuiltinCommand("find_path", cmFindPath);
   state->AddBuiltinCommand("find_program", cmFindProgram);
-  state->AddBuiltinCommand("foreach", cmForEachCommand);
-  state->AddBuiltinCommand("function", cmFunctionCommand);
   state->AddBuiltinCommand("get_cmake_property", cmGetCMakePropertyCommand);
   state->AddBuiltinCommand("get_directory_property",
                            cmGetDirectoryPropertyCommand);
   state->AddBuiltinCommand("get_filename_component",
                            cmGetFilenameComponentCommand);
   state->AddBuiltinCommand("get_property", cmGetPropertyCommand);
-  state->AddBuiltinCommand("if", cmIfCommand);
   state->AddBuiltinCommand("include", cmIncludeCommand);
   state->AddBuiltinCommand("include_guard", cmIncludeGuardCommand);
   state->AddBuiltinCommand("list", cmListCommand);
-  state->AddBuiltinCommand("macro", cmMacroCommand);
   state->AddBuiltinCommand("make_directory", cmMakeDirectoryCommand);
   state->AddBuiltinCommand("mark_as_advanced", cmMarkAsAdvancedCommand);
   state->AddBuiltinCommand("math", cmMathCommand);
   state->AddBuiltinCommand("message", cmMessageCommand);
   state->AddBuiltinCommand("option", cmOptionCommand);
   state->AddBuiltinCommand("cmake_parse_arguments", cmParseArgumentsCommand);
-  state->AddBuiltinCommand("return", cmReturnCommand);
   state->AddBuiltinCommand("separate_arguments", cmSeparateArgumentsCommand);
   state->AddBuiltinCommand("set", cmSetCommand);
   state->AddBuiltinCommand("set_directory_properties",
@@ -161,7 +163,6 @@
   state->AddBuiltinCommand("site_name", cmSiteNameCommand);
   state->AddBuiltinCommand("string", cmStringCommand);
   state->AddBuiltinCommand("unset", cmUnsetCommand);
-  state->AddBuiltinCommand("while", cmWhileCommand);
 
   state->AddUnexpectedCommand(
     "else",
diff --git a/Source/cmFunctionCommand.cxx b/Source/cmFunctionCommand.cxx
index 71c82d6..1359009 100644
--- a/Source/cmFunctionCommand.cxx
+++ b/Source/cmFunctionCommand.cxx
@@ -163,8 +163,11 @@
   f.FilePath = this->GetStartingContext().FilePath;
   f.Line = this->GetStartingContext().Line;
   mf.RecordPolicies(f.Policies);
-  mf.GetState()->AddScriptedCommand(this->Args.front(), std::move(f));
-  return true;
+  return mf.GetState()->AddScriptedCommand(
+    this->Args.front(),
+    BT<cmState::Command>(std::move(f),
+                         mf.GetBacktrace().Push(this->GetStartingContext())),
+    mf);
 }
 
 } // anonymous namespace
diff --git a/Source/cmLoadCommandCommand.cxx b/Source/cmLoadCommandCommand.cxx
index 5790e16..adebe02 100644
--- a/Source/cmLoadCommandCommand.cxx
+++ b/Source/cmLoadCommandCommand.cxx
@@ -24,6 +24,7 @@
 #include "cmCommand.h"
 #include "cmDynamicLoader.h"
 #include "cmExecutionStatus.h"
+#include "cmListFileCache.h"
 #include "cmLocalGenerator.h"
 #include "cmMakefile.h"
 #include "cmState.h"
@@ -36,8 +37,6 @@
 #  include <malloc.h> /* for malloc/free on QNX */
 #endif
 
-class cmListFileBacktrace;
-
 namespace {
 
 const char* LastName = nullptr;
@@ -256,10 +255,12 @@
   // if the symbol is found call it to set the name on the
   // function blocker
   if (initFunction) {
-    status.GetMakefile().GetState()->AddScriptedCommand(
+    return status.GetMakefile().GetState()->AddScriptedCommand(
       args[0],
-      cmLegacyCommandWrapper(cm::make_unique<cmLoadedCommand>(initFunction)));
-    return true;
+      BT<cmState::Command>(
+        cmLegacyCommandWrapper(cm::make_unique<cmLoadedCommand>(initFunction)),
+        status.GetMakefile().GetBacktrace()),
+      status.GetMakefile());
   }
   status.SetError("Attempt to load command failed. "
                   "No init function found.");
diff --git a/Source/cmMacroCommand.cxx b/Source/cmMacroCommand.cxx
index 98f88c1..8c4b2a7 100644
--- a/Source/cmMacroCommand.cxx
+++ b/Source/cmMacroCommand.cxx
@@ -171,8 +171,11 @@
   f.Functions = std::move(functions);
   f.FilePath = this->GetStartingContext().FilePath;
   mf.RecordPolicies(f.Policies);
-  mf.GetState()->AddScriptedCommand(this->Args[0], std::move(f));
-  return true;
+  return mf.GetState()->AddScriptedCommand(
+    this->Args[0],
+    BT<cmState::Command>(std::move(f),
+                         mf.GetBacktrace().Push(this->GetStartingContext())),
+    mf);
 }
 }
 
diff --git a/Source/cmState.cxx b/Source/cmState.cxx
index d5ac9ae..77665f1 100644
--- a/Source/cmState.cxx
+++ b/Source/cmState.cxx
@@ -436,6 +436,19 @@
     });
 }
 
+void cmState::AddFlowControlCommand(std::string const& name, Command command)
+{
+  this->FlowControlCommands.insert(name);
+  this->AddBuiltinCommand(name, std::move(command));
+}
+
+void cmState::AddFlowControlCommand(std::string const& name,
+                                    BuiltinCommand command)
+{
+  this->FlowControlCommands.insert(name);
+  this->AddBuiltinCommand(name, command);
+}
+
 void cmState::AddDisallowedCommand(std::string const& name,
                                    BuiltinCommand command,
                                    cmPolicies::PolicyID policy,
@@ -465,7 +478,7 @@
 
 void cmState::AddUnexpectedCommand(std::string const& name, const char* error)
 {
-  this->AddBuiltinCommand(
+  this->AddFlowControlCommand(
     name,
     [name, error](std::vector<cmListFileArgument> const&,
                   cmExecutionStatus& status) -> bool {
@@ -480,16 +493,28 @@
     });
 }
 
-void cmState::AddScriptedCommand(std::string const& name, Command command)
+bool cmState::AddScriptedCommand(std::string const& name, BT<Command> command,
+                                 cmMakefile& mf)
 {
   std::string sName = cmSystemTools::LowerCase(name);
 
+  if (this->FlowControlCommands.count(sName)) {
+    mf.GetCMakeInstance()->IssueMessage(
+      MessageType::FATAL_ERROR,
+      cmStrCat("Built-in flow control command \"", sName,
+               "\" cannot be overridden."),
+      command.Backtrace);
+    cmSystemTools::SetFatalErrorOccured();
+    return false;
+  }
+
   // if the command already exists, give a new name to the old command.
   if (Command oldCmd = this->GetCommandByExactName(sName)) {
     this->ScriptedCommands["_" + sName] = oldCmd;
   }
 
-  this->ScriptedCommands[sName] = std::move(command);
+  this->ScriptedCommands[sName] = std::move(command.Value);
+  return true;
 }
 
 cmState::Command cmState::GetCommand(std::string const& name) const
diff --git a/Source/cmState.h b/Source/cmState.h
index 2aa57e0..0744f64 100644
--- a/Source/cmState.h
+++ b/Source/cmState.h
@@ -9,6 +9,7 @@
 #include <set>
 #include <string>
 #include <unordered_map>
+#include <unordered_set>
 #include <vector>
 
 #include "cmDefinitions.h"
@@ -24,6 +25,7 @@
 class cmCacheManager;
 class cmCommand;
 class cmGlobVerificationManager;
+class cmMakefile;
 class cmStateSnapshot;
 class cmMessenger;
 class cmExecutionStatus;
@@ -157,10 +159,13 @@
                          std::unique_ptr<cmCommand> command);
   void AddBuiltinCommand(std::string const& name, Command command);
   void AddBuiltinCommand(std::string const& name, BuiltinCommand command);
+  void AddFlowControlCommand(std::string const& name, Command command);
+  void AddFlowControlCommand(std::string const& name, BuiltinCommand command);
   void AddDisallowedCommand(std::string const& name, BuiltinCommand command,
                             cmPolicies::PolicyID policy, const char* message);
   void AddUnexpectedCommand(std::string const& name, const char* error);
-  void AddScriptedCommand(std::string const& name, Command command);
+  bool AddScriptedCommand(std::string const& name, BT<Command> command,
+                          cmMakefile& mf);
   void RemoveBuiltinCommand(std::string const& name);
   void RemoveUserDefinedCommands();
   std::vector<std::string> GetCommandNames() const;
@@ -223,6 +228,7 @@
   std::vector<std::string> EnabledLanguages;
   std::unordered_map<std::string, Command> BuiltinCommands;
   std::unordered_map<std::string, Command> ScriptedCommands;
+  std::unordered_set<std::string> FlowControlCommands;
   cmPropertyMap GlobalProperties;
   std::unique_ptr<cmCacheManager> CacheManager;
   std::unique_ptr<cmGlobVerificationManager> GlobVerificationManager;
diff --git a/Tests/RunCMake/Syntax/Override.cmake b/Tests/RunCMake/Syntax/Override.cmake
new file mode 100644
index 0000000..af62db1
--- /dev/null
+++ b/Tests/RunCMake/Syntax/Override.cmake
@@ -0,0 +1,6 @@
+function(override)
+  function(${FUNCTION_NAME})
+  endfunction()
+endfunction()
+override()
+message(FATAL_ERROR "This shouldn't happen")
diff --git a/Tests/RunCMake/Syntax/OverrideBreak-result.txt b/Tests/RunCMake/Syntax/OverrideBreak-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/Syntax/OverrideBreak-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/Syntax/OverrideContinue-result.txt b/Tests/RunCMake/Syntax/OverrideContinue-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/Syntax/OverrideContinue-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/Syntax/OverrideElse-result.txt b/Tests/RunCMake/Syntax/OverrideElse-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/Syntax/OverrideElse-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/Syntax/OverrideElseIf-result.txt b/Tests/RunCMake/Syntax/OverrideElseIf-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/Syntax/OverrideElseIf-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/Syntax/OverrideEndForeach-result.txt b/Tests/RunCMake/Syntax/OverrideEndForeach-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/Syntax/OverrideEndForeach-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/Syntax/OverrideEndFunction-result.txt b/Tests/RunCMake/Syntax/OverrideEndFunction-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/Syntax/OverrideEndFunction-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/Syntax/OverrideEndIf-result.txt b/Tests/RunCMake/Syntax/OverrideEndIf-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/Syntax/OverrideEndIf-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/Syntax/OverrideEndMacro-result.txt b/Tests/RunCMake/Syntax/OverrideEndMacro-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/Syntax/OverrideEndMacro-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/Syntax/OverrideEndWhile-result.txt b/Tests/RunCMake/Syntax/OverrideEndWhile-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/Syntax/OverrideEndWhile-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/Syntax/OverrideForeach-result.txt b/Tests/RunCMake/Syntax/OverrideForeach-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/Syntax/OverrideForeach-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/Syntax/OverrideFunction-result.txt b/Tests/RunCMake/Syntax/OverrideFunction-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/Syntax/OverrideFunction-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/Syntax/OverrideIf-result.txt b/Tests/RunCMake/Syntax/OverrideIf-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/Syntax/OverrideIf-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/Syntax/OverrideMacro-result.txt b/Tests/RunCMake/Syntax/OverrideMacro-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/Syntax/OverrideMacro-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/Syntax/OverrideReturn-result.txt b/Tests/RunCMake/Syntax/OverrideReturn-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/Syntax/OverrideReturn-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/Syntax/OverrideWhile-result.txt b/Tests/RunCMake/Syntax/OverrideWhile-result.txt
new file mode 100644
index 0000000..d00491f
--- /dev/null
+++ b/Tests/RunCMake/Syntax/OverrideWhile-result.txt
@@ -0,0 +1 @@
+1
diff --git a/Tests/RunCMake/Syntax/RunCMakeTest.cmake b/Tests/RunCMake/Syntax/RunCMakeTest.cmake
index 34885b8..4d24657 100644
--- a/Tests/RunCMake/Syntax/RunCMakeTest.cmake
+++ b/Tests/RunCMake/Syntax/RunCMakeTest.cmake
@@ -123,3 +123,30 @@
 run_cmake(FunctionUnmatchedForeach)
 run_cmake(MacroUnmatched)
 run_cmake(MacroUnmatchedForeach)
+
+function(run_override name)
+  string(TOLOWER "${name}" lname)
+  set(RunCMake_DEFAULT_stderr "^CMake Error at [^
+]*/Tests/RunCMake/Syntax/Override\\.cmake:[0-9]+ \\(function\\):
+  Built-in flow control command \"${lname}\" cannot be overridden\\.
+Call Stack \\(most recent call first\\):
+  [^
+]*/Tests/RunCMake/Syntax/Override\\.cmake:[0-9]+ \\(override\\)$")
+  run_cmake_command(Override${name} "${CMAKE_COMMAND}" -DFUNCTION_NAME=${name} -P "${RunCMake_SOURCE_DIR}/Override.cmake")
+endfunction()
+
+run_override(Break)
+run_override(Continue)
+run_override(Else)
+run_override(ElseIf)
+run_override(EndForeach)
+run_override(EndFunction)
+run_override(EndIf)
+run_override(EndMacro)
+run_override(EndWhile)
+run_override(Foreach)
+run_override(Function)
+run_override(If)
+run_override(Macro)
+run_override(Return)
+run_override(While)