Repository: 4ian/GDevelop
File: Core/GDCore/IDE/InstructionValidator.cpp (C++)

Code before:

#include "GDCore/Extensions/Metadata/AbstractFunctionMetadata.h"
#include "GDCore/Extensions/Metadata/BehaviorMetadata.h"
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/IDE/Events/ExpressionValidator.h"
#include "GDCore/IDE/VariableInstructionSwitcher.h"
#include "GDCore/Project/InitialInstancesContainer.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Project/ObjectGroup.h"
#include "GDCore/Project/ObjectsContainer.h"
#include "GDCore/Project/ObjectsContainersList.h"
#include "GDCore/Project/ProjectScopedContainers.h"
#include "GDCore/Project/Variable.h"
#include "GDCore/Project/VariablesContainer.h"
#include "GDCore/String.h"

namespace gd {

bool InstructionValidator::IsParameterValid(
    const gd::Platform &platform,
    const gd::ProjectScopedContainers projectScopedContainers,
    const gd::Instruction &instruction, const InstructionMetadata &metadata,
    std::size_t parameterIndex, const gd::String &value) {
  auto &parameterMetadata = metadata.GetParameter(parameterIndex);
  // TODO Remove the ternary when any parameter declaration uses
  // "number" instead of "expression".
  auto &parameterType = parameterMetadata.GetType() == "expression"
                            ? "number"
                            : parameterMetadata.GetType();
  bool shouldNotBeValidated = parameterType == "layer" && value.empty();
  if (shouldNotBeValidated) {
    return true;
  }
  if (gd::ParameterMetadata::IsExpression("number", parameterType) ||
      gd::ParameterMetadata::IsExpression("string", parameterType) ||
      gd::ParameterMetadata::IsExpression("variable", parameterType)) {
    auto &expressionNode =
        *instruction.GetParameter(parameterIndex).GetRootNode();
    ExpressionValidator expressionValidator(platform, projectScopedContainers,
                                            parameterType,
                                            parameterMetadata.GetExtraInfo());
    expressionNode.Visit(expressionValidator);
    if (!expressionValidator.GetAllErrors().empty()) {
      return false;
    }
    // New object variable instructions require the variable to be
    // declared while legacy ones don"t.
    // This is why it"s done here instead of in the parser directly.
    if (parameterType == "objectvar" &&

Review comment (bug):
GetParameter does not do any bound checking nor return a "null object", so let's check the bound to avoid a crash in case a wrong parameterIndex is passed. Better be safe.

Code after:

#include "GDCore/Extensions/Metadata/AbstractFunctionMetadata.h"
#include "GDCore/Extensions/Metadata/BehaviorMetadata.h"
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Platform.h"
#include "GDCore/IDE/Events/ExpressionValidator.h"
#include "GDCore/IDE/VariableInstructionSwitcher.h"
#include "GDCore/Project/InitialInstancesContainer.h"
#include "GDCore/Project/Object.h"
#include "GDCore/Project/ObjectGroup.h"
#include "GDCore/Project/ObjectsContainer.h"
#include "GDCore/Project/ObjectsContainersList.h"
#include "GDCore/Project/ProjectScopedContainers.h"
#include "GDCore/Project/Variable.h"
#include "GDCore/Project/VariablesContainer.h"
#include "GDCore/String.h"

namespace gd {

bool InstructionValidator::IsParameterValid(
    const gd::Platform &platform,
    const gd::ProjectScopedContainers projectScopedContainers,
    const gd::Instruction &instruction, const InstructionMetadata &metadata,
    std::size_t parameterIndex, const gd::String &value) {
  if (parameterIndex >= instruction.GetParametersCount() ||
      parameterIndex >= metadata.GetParametersCount()) {
    return false;
  }
  const auto &parameterMetadata = metadata.GetParameter(parameterIndex);
  // TODO Remove the ternary when all parameter declarations use
  // "number" instead of "expression".
  const auto &parameterType = parameterMetadata.GetType() == "expression"
                                  ? "number"
                                  : parameterMetadata.GetType();
  bool shouldNotBeValidated = parameterType == "layer" && value.empty();
  if (shouldNotBeValidated) {
    return true;
  }
  if (gd::ParameterMetadata::IsExpression("number", parameterType) ||
      gd::ParameterMetadata::IsExpression("string", parameterType) ||
      gd::ParameterMetadata::IsExpression("variable", parameterType)) {
    auto &expressionNode =
        *instruction.GetParameter(parameterIndex).GetRootNode();
    ExpressionValidator expressionValidator(platform, projectScopedContainers,
                                            parameterType,
                                            parameterMetadata.GetExtraInfo());
    expressionNode.Visit(expressionValidator);
    if (!expressionValidator.GetAllErrors().empty()) {
      return false;
    }