Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ gd::String ExpressionCodeGenerator::GenerateExpressionCode(
gd::ExpressionValidator validator(codeGenerator.GetPlatform(),
codeGenerator.GetProjectScopedContainers(),
rootType,
rootObjectName,
extraInfo);
node->Visit(validator);
if (!validator.GetFatalErrors().empty()) {
Expand Down
4 changes: 4 additions & 0 deletions Core/GDCore/Extensions/Metadata/ValueTypeMetadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,10 @@ class GD_CORE_API ValueTypeMetadata {
return type == "scenevar" || type == "globalvar" || type == "objectvar";
}

static bool IsTypeObjectVariable(const gd::String &type) {
return type == "objectvar";
}

/**
* \brief Return true if the type is representing one object
* (or more, i.e: an object group).
Expand Down
1 change: 0 additions & 1 deletion Core/GDCore/IDE/Events/EventsBehaviorRenamer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
#include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Metadata/ParameterMetadataTools.h"
#include "GDCore/IDE/Events/ExpressionValidator.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/String.h"
Expand Down
1 change: 0 additions & 1 deletion Core/GDCore/IDE/Events/EventsContextAnalyzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
#include "GDCore/Extensions/Metadata/InstructionMetadata.h"
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Metadata/ParameterMetadataTools.h"
#include "GDCore/IDE/Events/ExpressionValidator.h"
#include "GDCore/IDE/Events/ExpressionTypeFinder.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
Expand Down
1 change: 0 additions & 1 deletion Core/GDCore/IDE/Events/EventsParameterReplacer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Metadata/ParameterMetadata.h"
#include "GDCore/Extensions/Metadata/ParameterMetadataTools.h"
#include "GDCore/IDE/Events/ExpressionValidator.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/ProjectScopedContainers.h"
Expand Down
1 change: 0 additions & 1 deletion Core/GDCore/IDE/Events/EventsPropertyReplacer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Metadata/ParameterMetadata.h"
#include "GDCore/Extensions/Metadata/ParameterMetadataTools.h"
#include "GDCore/IDE/Events/ExpressionValidator.h"
#include "GDCore/Project/Layout.h"
#include "GDCore/Project/Project.h"
#include "GDCore/Project/ProjectScopedContainers.h"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Metadata/ParameterMetadata.h"
#include "GDCore/Extensions/Metadata/ParameterMetadataTools.h"
#include "GDCore/IDE/Events/ExpressionValidator.h"
#include "GDCore/IDE/Events/ExpressionVariableOwnerFinder.h"
#include "GDCore/IDE/Events/ExpressionVariableNameFinder.h"
#include "GDCore/IDE/VariableInstructionSwitcher.h"
Expand Down
1 change: 0 additions & 1 deletion Core/GDCore/IDE/Events/EventsVariableReplacer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
#include "GDCore/Extensions/Metadata/MetadataProvider.h"
#include "GDCore/Extensions/Metadata/ParameterMetadata.h"
#include "GDCore/Extensions/Metadata/ParameterMetadataTools.h"
#include "GDCore/IDE/Events/ExpressionValidator.h"
#include "GDCore/IDE/Events/ExpressionVariableOwnerFinder.h"
#include "GDCore/IDE/Events/ExpressionVariableNameFinder.h"
#include "GDCore/IDE/VariableInstructionSwitcher.h"
Expand Down
126 changes: 80 additions & 46 deletions Core/GDCore/IDE/Events/ExpressionValidator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,34 +66,57 @@ size_t GetMaximumParametersNumber(

} // namespace

bool ExpressionValidator::ValidateChildVariable(
const gd::Variable &parentVariable, const gd::String &childVariableName,
const gd::ExpressionParserLocation childNameLocation,
const bool isUndeclaredVariableFatal) {
// A child variable is accessed, check it can be used in an expression.
if (!parentVariable.HasChild(childVariableName)) {
RaiseTypeError(_("No child variable with this name found."),
childNameLocation, isUndeclaredVariableFatal);
return false;
}
return true;
}

void ExpressionValidator::ValidateLastChildVariable(
const gd::Variable &lastChildVariable,
const gd::ExpressionParserLocation childNameLocation) {
const auto type = lastChildVariable.GetType();

if (parentType == Type::String || parentType == Type::Number ||
parentType == Type::NumberOrString) {
// Collections type can't be used directly in expressions, a child
// must be accessed.
if (type == Variable::Structure) {
RaiseTypeError(_("You need to specify the name of the child variable "
"to access. For example: `MyVariable.child`."),
childNameLocation);
} else if (type == Variable::Array) {
RaiseTypeError(_("You need to specify the name of the child variable "
"to access. For example: `MyVariable[0]`."),
childNameLocation);
}
}
// Number, string or boolean variables can be used in expressions.
ReadChildTypeFromVariable(type);
}

bool ExpressionValidator::ValidateObjectVariableOrVariableOrProperty(
const gd::IdentifierNode& identifier) {
return ValidateObjectVariableOrVariableOrProperty(identifier.identifierName, identifier.identifierNameLocation, identifier.childIdentifierName, identifier.childIdentifierNameLocation);
const gd::IdentifierNode &identifier) {
return ValidateObjectVariableOrVariableOrProperty(
identifier.identifierName, identifier.identifierNameLocation,
identifier.childIdentifierName, identifier.childIdentifierNameLocation,
true);
}

bool ExpressionValidator::ValidateObjectVariableOrVariableOrProperty(
const gd::String &identifierName,
const gd::ExpressionParserLocation identifierNameLocation,
const gd::String &childIdentifierName,
const gd::ExpressionParserLocation childIdentifierNameLocation) {
auto validateVariableTypeForExpression =
[this, &identifierNameLocation](gd::Variable::Type type) {
// Collections type can't be used directly in expressions, a child
// must be accessed.
if (type == Variable::Structure) {
RaiseTypeError(_("You need to specify the name of the child variable "
"to access. For example: `MyVariable.child`."),
identifierNameLocation);
} else if (type == Variable::Array) {
RaiseTypeError(_("You need to specify the name of the child variable "
"to access. For example: `MyVariable[0]`."),
identifierNameLocation);
} else {
// Number, string or boolean variables can be used in expressions.
return;
}
};

const gd::ExpressionParserLocation childIdentifierNameLocation,
const bool isUndeclaredVariableFatal,
const bool hasMoreChildren) {
const auto& variablesContainersList = projectScopedContainers.GetVariablesContainersList();
const auto& objectsContainersList = projectScopedContainers.GetObjectsContainersList();
const auto& propertiesContainersList = projectScopedContainers.GetPropertiesContainersList();
Expand All @@ -118,27 +141,34 @@ bool ExpressionValidator::ValidateObjectVariableOrVariableOrProperty(
identifierName, childIdentifierName);

if (variableExistence == gd::ObjectsContainersList::DoesNotExist) {
RaiseUndeclaredVariableError(_("This variable does not exist on this object or group."),
childIdentifierNameLocation, childIdentifierName, identifierName);
RaiseUndeclaredVariableError(
_("This variable does not exist on this object or group."),
childIdentifierNameLocation, childIdentifierName, identifierName,
isUndeclaredVariableFatal);

return true; // We should have found a variable.
}
else if (variableExistence == gd::ObjectsContainersList::ExistsOnlyOnSomeObjectsOfTheGroup) {
RaiseUndeclaredVariableError(_("This variable only exists on some objects of the group. It must be declared for all objects."),
childIdentifierNameLocation, childIdentifierName, identifierName);
} else if (variableExistence ==
gd::ObjectsContainersList::ExistsOnlyOnSomeObjectsOfTheGroup) {
RaiseUndeclaredVariableError(
_("This variable only exists on some objects of the group. It must be declared for all objects."),
childIdentifierNameLocation, childIdentifierName, identifierName,
isUndeclaredVariableFatal);

return true; // We should have found a variable.
}
else if (variableExistence == gd::ObjectsContainersList::GroupIsEmpty) {
} else if (variableExistence == gd::ObjectsContainersList::GroupIsEmpty) {
RaiseUndeclaredVariableError(_("This group is empty. Add an object to this group first."),
identifierNameLocation, childIdentifierName, identifierName);

return true; // We should have found a variable.
}

auto variableType = objectsContainersList.GetTypeOfObjectOrGroupVariable(
identifierName, childIdentifierName);
ReadChildTypeFromVariable(variableType);
if (!hasMoreChildren) {
const auto &objectVariable =
objectsContainersList
.GetObjectOrGroupVariablesContainer(identifierName)
->Get(childIdentifierName);
ValidateLastChildVariable(objectVariable, childIdentifierNameLocation);
}

return true; // We found a variable.
}, [&]() {
Expand All @@ -152,22 +182,16 @@ bool ExpressionValidator::ValidateObjectVariableOrVariableOrProperty(
if (childIdentifierName.empty()) {
// Just the root variable is accessed, check it can be used in an
// expression.
validateVariableTypeForExpression(variable.GetType());
ReadChildTypeFromVariable(variable.GetType());

ValidateLastChildVariable(variable, identifierNameLocation);
return true; // We found a variable.
} else {
// A child variable is accessed, check it can be used in an expression.
if (!variable.HasChild(childIdentifierName)) {
RaiseTypeError(_("No child variable with this name found."),
childIdentifierNameLocation);

return true; // We should have found a variable.
ValidateChildVariable(variable, childIdentifierName,
childIdentifierNameLocation,
isUndeclaredVariableFatal);
if (variable.HasChild(childIdentifierName)) {
ValidateLastChildVariable(variable.GetChild(childIdentifierName),
identifierNameLocation);
}

const gd::Variable& childVariable =
variable.GetChild(childIdentifierName);
ReadChildTypeFromVariable(childVariable.GetType());
return true; // We found a variable.
}
}, [&]() {
Expand Down Expand Up @@ -388,6 +412,12 @@ ExpressionValidator::Type ExpressionValidator::ValidateFunction(
return returnType;
}

const gd::String parentRootObjectName = rootObjectName;
// We don't check objectvar parameters since they are only used by the legacy
// functions like: `Object.Variable(MyVariable)` which allow undeclared
// variables.
rootObjectName = emptyParameterExtraInfo;

// TODO: reverse the order of diagnostic?
size_t writtenParametersFirstIndex =
ExpressionParser2::WrittenParametersFirstIndex(function.objectName,
Expand Down Expand Up @@ -448,6 +478,7 @@ ExpressionValidator::Type ExpressionValidator::ValidateFunction(
}
metadataIndex++;
}
rootObjectName = parentRootObjectName;
return returnType;
}

Expand Down Expand Up @@ -478,6 +509,7 @@ const gd::String& ExpressionValidator::TypeToString(Type type) {
// if it allows properties and parameters.
case Type::VariableOrProperty:
case Type::VariableOrPropertyOrParameter:
case Type::ObjectVariable:
case Type::LegacyVariable:
return variableTypeString;
case Type::Object:
Expand Down Expand Up @@ -506,7 +538,9 @@ ExpressionValidator::Type ExpressionValidator::StringToType(
if (type == ExpressionValidator::variableTypeString ||
gd::ParameterMetadata::IsExpression(
ExpressionValidator::variableTypeString, type)) {
if (gd::ValueTypeMetadata::IsTypeLegacyPreScopedVariable(type)) {
if (gd::ValueTypeMetadata::IsTypeObjectVariable(type)) {
return Type::ObjectVariable;
} else if (gd::ValueTypeMetadata::IsTypeLegacyPreScopedVariable(type)) {
return Type::LegacyVariable;
} else if (type == "variableOrProperty") {
return Type::VariableOrProperty;
Expand Down
Loading
Loading