diff --git a/Core/GDCore/Events/CodeGeneration/ExpressionCodeGenerator.cpp b/Core/GDCore/Events/CodeGeneration/ExpressionCodeGenerator.cpp index 86aca52b5fc7..e0d327b6eba0 100644 --- a/Core/GDCore/Events/CodeGeneration/ExpressionCodeGenerator.cpp +++ b/Core/GDCore/Events/CodeGeneration/ExpressionCodeGenerator.cpp @@ -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()) { diff --git a/Core/GDCore/Extensions/Metadata/ValueTypeMetadata.h b/Core/GDCore/Extensions/Metadata/ValueTypeMetadata.h index e5c3deb650bd..83ad3aad75a2 100644 --- a/Core/GDCore/Extensions/Metadata/ValueTypeMetadata.h +++ b/Core/GDCore/Extensions/Metadata/ValueTypeMetadata.h @@ -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). diff --git a/Core/GDCore/IDE/Events/EventsBehaviorRenamer.cpp b/Core/GDCore/IDE/Events/EventsBehaviorRenamer.cpp index 6e97c89ea7ef..a450aa71acfb 100644 --- a/Core/GDCore/IDE/Events/EventsBehaviorRenamer.cpp +++ b/Core/GDCore/IDE/Events/EventsBehaviorRenamer.cpp @@ -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" diff --git a/Core/GDCore/IDE/Events/EventsContextAnalyzer.cpp b/Core/GDCore/IDE/Events/EventsContextAnalyzer.cpp index 58aac2059ce7..8c209dcbb1e9 100644 --- a/Core/GDCore/IDE/Events/EventsContextAnalyzer.cpp +++ b/Core/GDCore/IDE/Events/EventsContextAnalyzer.cpp @@ -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" diff --git a/Core/GDCore/IDE/Events/EventsParameterReplacer.cpp b/Core/GDCore/IDE/Events/EventsParameterReplacer.cpp index 9a3e800d383e..b6cab334436f 100644 --- a/Core/GDCore/IDE/Events/EventsParameterReplacer.cpp +++ b/Core/GDCore/IDE/Events/EventsParameterReplacer.cpp @@ -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" diff --git a/Core/GDCore/IDE/Events/EventsPropertyReplacer.cpp b/Core/GDCore/IDE/Events/EventsPropertyReplacer.cpp index 7783f7d74a1f..ac5ce5ce7cca 100644 --- a/Core/GDCore/IDE/Events/EventsPropertyReplacer.cpp +++ b/Core/GDCore/IDE/Events/EventsPropertyReplacer.cpp @@ -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" diff --git a/Core/GDCore/IDE/Events/EventsVariableInstructionTypeSwitcher.cpp b/Core/GDCore/IDE/Events/EventsVariableInstructionTypeSwitcher.cpp index 974c3f3cc094..73eb2fff7992 100644 --- a/Core/GDCore/IDE/Events/EventsVariableInstructionTypeSwitcher.cpp +++ b/Core/GDCore/IDE/Events/EventsVariableInstructionTypeSwitcher.cpp @@ -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" diff --git a/Core/GDCore/IDE/Events/EventsVariableReplacer.cpp b/Core/GDCore/IDE/Events/EventsVariableReplacer.cpp index cc3dabb4bb8e..ab18332ffcbe 100644 --- a/Core/GDCore/IDE/Events/EventsVariableReplacer.cpp +++ b/Core/GDCore/IDE/Events/EventsVariableReplacer.cpp @@ -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" diff --git a/Core/GDCore/IDE/Events/ExpressionValidator.cpp b/Core/GDCore/IDE/Events/ExpressionValidator.cpp index cacb35887fa4..7206d0251b51 100644 --- a/Core/GDCore/IDE/Events/ExpressionValidator.cpp +++ b/Core/GDCore/IDE/Events/ExpressionValidator.cpp @@ -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(); @@ -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. }, [&]() { @@ -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. } }, [&]() { @@ -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, @@ -448,6 +478,7 @@ ExpressionValidator::Type ExpressionValidator::ValidateFunction( } metadataIndex++; } + rootObjectName = parentRootObjectName; return returnType; } @@ -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: @@ -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; diff --git a/Core/GDCore/IDE/Events/ExpressionValidator.h b/Core/GDCore/IDE/Events/ExpressionValidator.h index c46f4724ef90..0b07e24bbbc4 100644 --- a/Core/GDCore/IDE/Events/ExpressionValidator.h +++ b/Core/GDCore/IDE/Events/ExpressionValidator.h @@ -40,10 +40,12 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker { ExpressionValidator(const gd::Platform &platform_, const gd::ProjectScopedContainers & projectScopedContainers_, const gd::String &rootType_, + const gd::String &rootObjectName_ = emptyParameterExtraInfo, const gd::String &extraInfo_ = emptyParameterExtraInfo) : platform(platform_), projectScopedContainers(projectScopedContainers_), parentType(StringToType(gd::ValueTypeMetadata::GetExpressionPrimitiveValueType(rootType_))), + rootObjectName(rootObjectName_), childType(Type::Unknown), forbidsUsageOfBracketsBecauseParentIsObject(false), currentParameterExtraInfo(&extraInfo_), @@ -127,11 +129,12 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker { _("Operators (+, -, /, *) can't be used with an object name. Remove " "the operator."), node.rightHandSide->location); - } else if (leftType == Type::Variable || leftType == Type::LegacyVariable) { + } else if (leftType == Type::Variable || leftType == Type::ObjectVariable || + leftType == Type::LegacyVariable) { RaiseOperatorError( _("Operators (+, -, /, *) can't be used in variable names. Remove " "the operator from the variable name."), - node.rightHandSide->location); + node.rightHandSide->location); } // The "required" type ("parentType") of the second operator is decided by: @@ -174,7 +177,9 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker { _("Operators (+, -) can't be used with an object name. Remove the " "operator."), node.location); - } else if (rightType == Type::Variable || rightType == Type::LegacyVariable) { + } else if (rightType == Type::Variable || + rightType == Type::ObjectVariable || + rightType == Type::LegacyVariable) { RaiseTypeError( _("Operators (+, -) can't be used in variable names. Remove " "the operator from the variable name."), @@ -210,13 +215,55 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker { } void OnVisitVariableNode(VariableNode& node) override { ReportAnyError(node); + parentVariable = nullptr; + variableChildDepth = 0; if (parentType == Type::Variable || parentType == Type::VariableOrProperty || parentType == Type::VariableOrPropertyOrParameter) { childType = parentType; - CheckVariableExistence(node.location, node.name, node.child != nullptr); + bool isRootVariableDeclared = CheckVariableExistence( + node.location, node.name, node.child != nullptr); + if (node.child) { + if (isRootVariableDeclared) { + const auto &variable = + projectScopedContainers.GetVariablesContainersList().Get( + node.name); + if (node.child) { + parentVariable = &variable; + } else { + ValidateLastChildVariable(variable, node.location); + } + } + node.child->Visit(*this); + } + } else if (parentType == Type::ObjectVariable) { + childType = parentType; + + if (!rootObjectName.empty()) { + ValidateObjectVariableOrVariableOrProperty( + rootObjectName, node.nameLocation, node.name, node.nameLocation, + false, !!node.child); + + const auto &objectsContainersList = + projectScopedContainers.GetObjectsContainersList(); + auto variableExistence = + objectsContainersList.HasObjectOrGroupWithVariableNamed( + rootObjectName, node.name); + if (variableExistence == gd::ObjectsContainersList::Exists) { + const auto &objectVariable = + objectsContainersList + .GetObjectOrGroupVariablesContainer(rootObjectName) + ->Get(node.name); + if (node.child) { + parentVariable = &objectVariable; + } else { + ValidateLastChildVariable(objectVariable, node.nameLocation); + } + } + rootObjectName = ""; + } if (node.child) { node.child->Visit(*this); } @@ -246,6 +293,14 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker { forbidsUsageOfBracketsBecauseParentIsObject = true; }, [&]() { // This is a variable. + const auto &variable = + projectScopedContainers.GetVariablesContainersList().Get( + node.name); + if (node.child) { + parentVariable = &variable; + } else { + ValidateLastChildVariable(variable, node.location); + } }, [&]() { // This is a property. // Being in this node implies that there is at least a child - which is not supported for properties. @@ -281,10 +336,48 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker { ReportAnyError(node); // TODO Also check child-variables existence on a path with only VariableAccessor to raise non-fatal errors. if (!variableObjectName.empty()) { - ValidateObjectVariableOrVariableOrProperty(variableObjectName, - variableObjectNameLocation, - node.name, node.nameLocation); + ValidateObjectVariableOrVariableOrProperty( + variableObjectName, variableObjectNameLocation, node.name, + node.nameLocation, true, !!node.child); + + const auto &objectsContainersList = + projectScopedContainers.GetObjectsContainersList(); + auto variableExistence = + objectsContainersList.HasObjectOrGroupWithVariableNamed( + variableObjectName, node.name); + if (variableExistence == gd::ObjectsContainersList::Exists) { + const auto &objectVariable = + objectsContainersList + .GetObjectOrGroupVariablesContainer(variableObjectName) + ->Get(node.name); + if (node.child) { + parentVariable = &objectVariable; + } else { + parentVariable = nullptr; + } + } else { + parentVariable = nullptr; + } + variableChildDepth = 0; variableObjectName = ""; + } else if (parentVariable) { + const bool isChildVariableDeclared = ValidateChildVariable( + *parentVariable, node.name, node.nameLocation, false); + if (isChildVariableDeclared) { + const auto &childVariable = parentVariable->GetChild(node.name); + if (node.child) { + parentVariable = &childVariable; + variableChildDepth++; + } else { + ValidateLastChildVariable(childVariable, node.nameLocation); + parentVariable = nullptr; + variableChildDepth = 0; + } + } + else { + parentVariable = nullptr; + variableChildDepth = 0; + } } // In the case we accessed an object variable (`MyObject.MyVariable`), // brackets can now be used (`MyObject.MyVariable["MyChildVariable"]` is now valid). @@ -299,6 +392,8 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker { ReportAnyError(node); variableObjectName = ""; + parentVariable = nullptr; + variableChildDepth = 0; if (forbidsUsageOfBracketsBecauseParentIsObject) { RaiseError(gd::ExpressionParserError::ErrorType::BracketsNotAllowedForObjects, _("You can't use the brackets to access an object variable. " @@ -350,7 +445,46 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker { } else if (parentType == Type::Variable || parentType == Type::VariableOrProperty || parentType == Type::VariableOrPropertyOrParameter) { - CheckVariableExistence(node.location, node.identifierName, !node.childIdentifierName.empty()); + bool isRootVariableDeclared = + CheckVariableExistence(node.location, node.identifierName, + !node.childIdentifierName.empty()); + if (isRootVariableDeclared && !node.childIdentifierName.empty()) { + ValidateObjectVariableOrVariableOrProperty( + node.identifierName, node.identifierNameLocation, + node.childIdentifierName, node.childIdentifierNameLocation, false); + } + } else if (parentType == Type::ObjectVariable) { + childType = parentType; + if (!rootObjectName.empty()) { + ValidateObjectVariableOrVariableOrProperty( + rootObjectName, node.identifierNameLocation, node.identifierName, + node.identifierNameLocation, false, + !node.childIdentifierName.empty()); + + const auto &objectsContainersList = + projectScopedContainers.GetObjectsContainersList(); + auto variableExistence = + objectsContainersList.HasObjectOrGroupWithVariableNamed( + rootObjectName, node.identifierName); + if (variableExistence == gd::ObjectsContainersList::Exists) { + const auto &objectVariable = + objectsContainersList + .GetObjectOrGroupVariablesContainer(rootObjectName) + ->Get(node.identifierName); + if (!node.childIdentifierName.empty()) { + const bool isChildVariableDeclared = + ValidateChildVariable(objectVariable, node.childIdentifierName, + node.childIdentifierNameLocation, false); + if (isChildVariableDeclared) { + const auto &childVariable = + objectVariable.GetChild(node.childIdentifierName); + ValidateLastChildVariable(childVariable, + node.childIdentifierNameLocation); + } + } + } + rootObjectName = ""; + } } else if (parentType != Type::Object && parentType != Type::LegacyVariable) { // It can't happen. @@ -376,7 +510,9 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker { } else if (parentType == Type::String) { message = _( "You must enter a text (between quotes) or a valid expression call."); - } else if (parentType == Type::Variable || parentType == Type::LegacyVariable) { + } else if (parentType == Type::Variable || + parentType == Type::ObjectVariable || + parentType == Type::LegacyVariable) { message = _("You must enter a variable name."); } else if (parentType == Type::Object) { message = _("You must enter a valid object name."); @@ -395,6 +531,7 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker { String, NumberOrString, Variable, + ObjectVariable, LegacyVariable, Object, Empty, @@ -407,12 +544,22 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker { const gd::String &identifierName, const gd::ExpressionParserLocation identifierNameLocation, const gd::String &childIdentifierName, - const gd::ExpressionParserLocation childIdentifierNameLocation); - - void CheckVariableExistence(const ExpressionParserLocation &location, + const gd::ExpressionParserLocation childIdentifierNameLocation, + const bool isUndeclaredVariableFatal, + const bool hasMoreChildren = false); + bool ValidateChildVariable( + const gd::Variable &parentVariable, const gd::String &childVariableName, + const gd::ExpressionParserLocation childNameLocation, + const bool isUndeclaredVariableFatal); + void ValidateLastChildVariable( + const gd::Variable &lastChildVariable, + const gd::ExpressionParserLocation childNameLocation); + + bool CheckVariableExistence(const ExpressionParserLocation &location, const gd::String &name, bool hasChild) { if (!currentParameterExtraInfo || *currentParameterExtraInfo != "AllowUndeclaredVariable") { + bool isRootVariableDeclared = false; projectScopedContainers.MatchIdentifierWithName( name, [&]() { @@ -424,6 +571,7 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker { }, [&]() { // This is a variable. + isRootVariableDeclared = true; }, [&]() { // This is a property. @@ -456,7 +604,9 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker { _("No variable with this name found."), location, name); }); + return isRootVariableDeclared; } + return false; } void ReportAnyError(const ExpressionNode& node, bool isFatal = true) { @@ -497,9 +647,10 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker { void RaiseUndeclaredVariableError(const gd::String &message, const ExpressionParserLocation &location, const gd::String &variableName, - const gd::String &objectName = "") { + const gd::String &objectName = "", + const bool isUndeclaredVariableFatal = true) { RaiseError(gd::ExpressionParserError::ErrorType::UndeclaredVariable, - message, location, true, variableName, objectName); + message, location, isUndeclaredVariableFatal, variableName, objectName); } void RaiseVariableNameCollisionError(const gd::String &message, @@ -566,9 +717,13 @@ class GD_CORE_API ExpressionValidator : public ExpressionParser2NodeWorker { std::vector> supplementalErrors; Type childType; ///< The type "discovered" down the tree and passed up. Type parentType; ///< The type "required" by the top of the tree. + /** The root object name of the expression or a function call. */ + gd::String rootObjectName; bool forbidsUsageOfBracketsBecauseParentIsObject; gd::String variableObjectName; gd::ExpressionParserLocation variableObjectNameLocation; + const gd::Variable *parentVariable = nullptr; + size_t variableChildDepth = 0; const gd::String *currentParameterExtraInfo; const gd::Platform &platform; const gd::ProjectScopedContainers &projectScopedContainers; diff --git a/Core/GDCore/IDE/Events/ExpressionsParameterMover.cpp b/Core/GDCore/IDE/Events/ExpressionsParameterMover.cpp index 7eb6e23c1cd3..bf2a6d848bb8 100644 --- a/Core/GDCore/IDE/Events/ExpressionsParameterMover.cpp +++ b/Core/GDCore/IDE/Events/ExpressionsParameterMover.cpp @@ -14,7 +14,6 @@ #include "GDCore/Events/Parsers/ExpressionParser2NodePrinter.h" #include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h" #include "GDCore/Extensions/Metadata/MetadataProvider.h" -#include "GDCore/IDE/Events/ExpressionValidator.h" #include "GDCore/Project/Layout.h" #include "GDCore/Project/Project.h" #include "GDCore/String.h" diff --git a/Core/GDCore/IDE/Events/ExpressionsRenamer.cpp b/Core/GDCore/IDE/Events/ExpressionsRenamer.cpp index b0382ad32287..e973d2589482 100644 --- a/Core/GDCore/IDE/Events/ExpressionsRenamer.cpp +++ b/Core/GDCore/IDE/Events/ExpressionsRenamer.cpp @@ -14,7 +14,6 @@ #include "GDCore/Events/Parsers/ExpressionParser2NodePrinter.h" #include "GDCore/Events/Parsers/ExpressionParser2NodeWorker.h" #include "GDCore/Extensions/Metadata/MetadataProvider.h" -#include "GDCore/IDE/Events/ExpressionValidator.h" #include "GDCore/Project/Layout.h" #include "GDCore/Project/Project.h" #include "GDCore/String.h" diff --git a/Core/GDCore/IDE/Events/ProjectElementRenamer.cpp b/Core/GDCore/IDE/Events/ProjectElementRenamer.cpp index a8fc2d4115b7..6374da7b17a0 100644 --- a/Core/GDCore/IDE/Events/ProjectElementRenamer.cpp +++ b/Core/GDCore/IDE/Events/ProjectElementRenamer.cpp @@ -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" diff --git a/Core/GDCore/IDE/InstructionValidator.cpp b/Core/GDCore/IDE/InstructionValidator.cpp index 5608df5af254..1f7c35446e0f 100644 --- a/Core/GDCore/IDE/InstructionValidator.cpp +++ b/Core/GDCore/IDE/InstructionValidator.cpp @@ -65,10 +65,23 @@ ParameterValidationResult InstructionValidator::ValidateParameter( if (gd::ParameterMetadata::IsExpression("number", parameterType) || gd::ParameterMetadata::IsExpression("string", parameterType) || gd::ParameterMetadata::IsExpression("variable", parameterType)) { + + // New object variable instructions require the variable to be + // declared while legacy ones don't. + // For legacy variable instruction, we pass an empty object name. + gd::String rootObjectName = ""; + if (parameterType == "objectvar" && + gd::VariableInstructionSwitcher::IsSwitchableVariableInstruction( + instruction.GetType())) { + const auto &objectsContainersList = + projectScopedContainers.GetObjectsContainersList(); + rootObjectName = instruction.GetParameter(0).GetPlainString(); + } auto &expressionNode = *instruction.GetParameter(parameterIndex).GetRootNode(); ExpressionValidator expressionValidator(platform, projectScopedContainers, parameterType, + rootObjectName, parameterMetadata.GetExtraInfo()); expressionNode.Visit(expressionValidator); @@ -78,27 +91,6 @@ ParameterValidationResult InstructionValidator::ValidateParameter( if (!expressionValidator.GetDeprecationWarnings().empty()) { result.hasDeprecationWarning = true; } - - // 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 (result.isValid && parameterType == "objectvar" && - gd::VariableInstructionSwitcher::IsSwitchableVariableInstruction( - instruction.GetType())) { - // Check at least the name of the root variable, it's the best we can - // do. - const auto &objectsContainersList = - projectScopedContainers.GetObjectsContainersList(); - const auto &objectName = instruction.GetParameter(0).GetPlainString(); - const auto &variableName = - instruction.GetParameter(parameterIndex).GetPlainString(); - if (objectsContainersList.HasObjectOrGroupWithVariableNamed( - objectName, - gd::InstructionValidator::GetRootVariableName(variableName)) == - gd::ObjectsContainersList::DoesNotExist) { - result.isValid = false; - } - } } else if (gd::ParameterMetadata::IsObject(parameterType)) { const auto &objectOrGroupName = instruction.GetParameter(parameterIndex).GetPlainString(); diff --git a/Core/tests/DummyPlatform.cpp b/Core/tests/DummyPlatform.cpp index 9e888804d4d7..56a0ee8e51d0 100644 --- a/Core/tests/DummyPlatform.cpp +++ b/Core/tests/DummyPlatform.cpp @@ -5,7 +5,6 @@ */ #include "GDCore/Extensions/Platform.h" #include "GDCore/Extensions/PlatformExtension.h" -#include "GDCore/IDE/Events/ExpressionValidator.h" #include "GDCore/Project/Behavior.h" #include "GDCore/Project/ObjectConfiguration.h" #include "GDCore/Extensions/Builtin/SpriteExtension/SpriteObject.h" diff --git a/Core/tests/ExpressionParser2.cpp b/Core/tests/ExpressionParser2.cpp index f8267f2556ea..da8784d56ca5 100644 --- a/Core/tests/ExpressionParser2.cpp +++ b/Core/tests/ExpressionParser2.cpp @@ -77,10 +77,9 @@ TEST_CASE("ExpressionParser2", "[common][events]") { layout1.GetObjects().InsertNewObject(project, "", "MyObject", 0); myObject.AddNewBehavior(project, "MyExtension::MyBehavior", "MyBehavior"); myObject.GetVariables().InsertNew("MyObjectVariable"); - myObject.GetVariables() - .InsertNew("MyObjectStructureVariable") - .GetChild("MyChildStructure") - .GetChild("MyChild"); + myObject.GetVariables().InsertNew("MyObjectStructureVariable").GetChild("MyChild"); + myObject.GetVariables().Get("MyObjectStructureVariable").GetChild("MyChildStructure").GetChild("MyChild"); + myObject.GetVariables().Get("MyObjectStructureVariable").GetChild("MyChildStructure").GetChild("MyChildStructure2").GetChild("MyChild"); auto &myGroup = layout1.GetObjects().GetObjectGroups().InsertNew("MyGroup"); @@ -2099,7 +2098,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") { } SECTION("in object variable parameter") { auto node = - parser.ParseExpression("MySceneStructureVariable.MyChild"); + parser.ParseExpression("MyObjectStructureVariable.MyChild"); gd::ExpressionValidator validator(platform, projectScopedContainers, "objectvar", "MyObject"); @@ -2238,7 +2237,9 @@ TEST_CASE("ExpressionParser2", "[common][events]") { gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string"); node->Visit(validator); RequireNoFatalError(validator); - // TODO Add a non-fatal error + RequireAllErrorsCount(validator, 1); + REQUIRE(validator.GetAllErrors()[0]->GetMessage() == + "No child variable with this name found."); } SECTION("Invalid scene variables (2 levels, child does not exist)") { @@ -2248,7 +2249,9 @@ TEST_CASE("ExpressionParser2", "[common][events]") { gd::ExpressionValidator validator(platform, projectScopedContainers, "variable"); node->Visit(validator); RequireNoFatalError(validator); - // TODO Add a non-fatal error + RequireAllErrorsCount(validator, 1); + REQUIRE(validator.GetAllErrors()[0]->GetMessage() == + "No child variable with this name found."); } SECTION("Invalid scene variables (3 levels, child does not exist)") { @@ -2259,7 +2262,9 @@ TEST_CASE("ExpressionParser2", "[common][events]") { gd::ExpressionValidator validator(platform, projectScopedContainers, "variable"); node->Visit(validator); RequireNoFatalError(validator); - // TODO Add a non-fatal error + RequireAllErrorsCount(validator, 1); + REQUIRE(validator.GetAllErrors()[0]->GetMessage() == + "No child variable with this name found."); } SECTION("Undeclared legacy scene variables (2 levels, child does not exist)") { @@ -2308,7 +2313,9 @@ TEST_CASE("ExpressionParser2", "[common][events]") { "objectvar", "MyObject"); node->Visit(validator); RequireNoFatalError(validator); - // TODO Add a non-fatal error + RequireAllErrorsCount(validator, 1); + REQUIRE(validator.GetAllErrors()[0]->GetMessage() == + "No child variable with this name found."); } SECTION("Undeclared object variables (3 levels, child does not exist)") { @@ -2319,7 +2326,9 @@ TEST_CASE("ExpressionParser2", "[common][events]") { "objectvar", "MyObject"); node->Visit(validator); RequireNoFatalError(validator); - // TODO Add a non-fatal error + RequireAllErrorsCount(validator, 1); + REQUIRE(validator.GetAllErrors()[0]->GetMessage() == + "No child variable with this name found."); } SECTION("Undeclared object variables in expressions (2 levels, child does not exist)") { @@ -2330,7 +2339,9 @@ TEST_CASE("ExpressionParser2", "[common][events]") { "number|string"); node->Visit(validator); RequireNoFatalError(validator); - // TODO Add a non-fatal error + RequireAllErrorsCount(validator, 1); + REQUIRE(validator.GetAllErrors()[0]->GetMessage() == + "No child variable with this name found."); } SECTION("Undeclared object variables in expressions (3 levels, child does not exist)") { @@ -2342,7 +2353,9 @@ TEST_CASE("ExpressionParser2", "[common][events]") { "number|string"); node->Visit(validator); RequireNoFatalError(validator); - // TODO Add a non-fatal error + RequireAllErrorsCount(validator, 1); + REQUIRE(validator.GetAllErrors()[0]->GetMessage() == + "No child variable with this name found."); } SECTION("Invalid scene variables (2 levels, variable and child do not exist)") { @@ -2362,9 +2375,8 @@ TEST_CASE("ExpressionParser2", "[common][events]") { "You must enter a number or a text, wrapped inside double quotes (example: \"Hello world\"), or a variable name."); } - SECTION("Invalid scene variables type in expression (1 level)") { - auto node = - parser.ParseExpression("MySceneStructureVariable"); + SECTION("Invalid scene variables structure in expression (1 level)") { + auto node = parser.ParseExpression("MySceneStructureVariable"); gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string"); @@ -2374,48 +2386,122 @@ TEST_CASE("ExpressionParser2", "[common][events]") { "You need to specify the name of the child variable to access. For example: `MyVariable.child`."); } - SECTION("Invalid scene variables type in expression (2 levels)") { + SECTION("Valid scene variable structure in variable parameter (1 level)") { + auto node = parser.ParseExpression("MySceneStructureVariable"); + + gd::ExpressionValidator validator(platform, projectScopedContainers, + "variable"); + node->Visit(validator); + RequireNoError(validator); + } + + SECTION("Invalid scene variables structure in expression (2 levels)") { auto node = parser.ParseExpression("MySceneStructureVariable.MyChildStructure"); gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string"); node->Visit(validator); + RequireFatalErrorsCount(validator, 1); + REQUIRE(validator.GetFatalErrors()[0]->GetMessage() == + "You need to specify the name of the child variable to access. For example: `MyVariable.child`."); + } + + SECTION("Valid scene variables structure in variable parameter (2 levels)") { + auto node = + parser.ParseExpression("MySceneStructureVariable.MyChildStructure"); + + gd::ExpressionValidator validator(platform, projectScopedContainers, + "variable"); + node->Visit(validator); RequireNoError(validator); - // TODO Add a fatal error } - SECTION("Invalid scene variables type in expression (3 levels)") { + SECTION("Invalid scene variables structure in expression (3 levels)") { auto node = parser.ParseExpression("MySceneStructureVariable.MyChildStructure.MyChildStructure2"); gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string"); node->Visit(validator); + RequireFatalErrorsCount(validator, 1); + REQUIRE(validator.GetFatalErrors()[0]->GetMessage() == + "You need to specify the name of the child variable to access. For example: `MyVariable.child`."); + } + + SECTION("Valid scene variables structure in variable parameter (3 levels)") { + auto node = + parser.ParseExpression("MySceneStructureVariable.MyChildStructure.MyChildStructure2"); + + gd::ExpressionValidator validator(platform, projectScopedContainers, + "variable"); + node->Visit(validator); RequireNoError(validator); - // TODO Add a fatal error } - SECTION("Invalid object variables type in expression (1 level)") { + SECTION("Invalid object variables structure in expression (1 level)") { auto node = parser.ParseExpression("MyObject.MyObjectStructureVariable"); gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string"); node->Visit(validator); + RequireFatalErrorsCount(validator, 1); + REQUIRE(validator.GetFatalErrors()[0]->GetMessage() == + "You need to specify the name of the child variable to access. For example: `MyVariable.child`."); + } + + SECTION("Valid object variables structure in variable parameter (1 level)") { + auto node = parser.ParseExpression("MyObjectStructureVariable"); + + gd::ExpressionValidator validator(platform, projectScopedContainers, + "objectvar"); + node->Visit(validator); RequireNoError(validator); - // TODO Add a fatal error } - SECTION("Invalid object variables type in expression (2 levels)") { + SECTION("Invalid object variables structure in expression (2 levels)") { auto node = parser.ParseExpression("MyObject.MyObjectStructureVariable.MyChildStructure"); gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string"); node->Visit(validator); + RequireFatalErrorsCount(validator, 1); + REQUIRE(validator.GetFatalErrors()[0]->GetMessage() == + "You need to specify the name of the child variable to access. For example: `MyVariable.child`."); + } + + SECTION("Valid object variables structure in variable parameter (2 levels)") { + auto node = + parser.ParseExpression("MyObjectStructureVariable.MyChildStructure"); + + gd::ExpressionValidator validator(platform, projectScopedContainers, + "objectvar"); + node->Visit(validator); + RequireNoError(validator); + } + + SECTION("Invalid object variables structure in expression (3 levels)") { + auto node = + parser.ParseExpression("MyObject.MyObjectStructureVariable.MyChildStructure.MyChildStructure2"); + + gd::ExpressionValidator validator(platform, projectScopedContainers, + "number|string"); + node->Visit(validator); + RequireFatalErrorsCount(validator, 1); + REQUIRE(validator.GetFatalErrors()[0]->GetMessage() == + "You need to specify the name of the child variable to access. For example: `MyVariable.child`."); + } + + SECTION("Valid object variables structure in variable parameter (3 levels)") { + auto node = + parser.ParseExpression("MyObjectStructureVariable.MyChildStructure.MyChildStructure2"); + + gd::ExpressionValidator validator(platform, projectScopedContainers, + "objectvar"); + node->Visit(validator); RequireNoError(validator); - // TODO Add a fatal error } SECTION("Valid object variables (1 level)") { @@ -2459,7 +2545,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") { SECTION("Valid object variables (2 levels)") { { auto node = - parser.ParseExpression("MySpriteObject.MyVariable.MyChild"); + parser.ParseExpression("MyObject.MyObjectStructureVariable.MyChild"); gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string"); node->Visit(validator); @@ -2467,7 +2553,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") { } { auto node = - parser.ParseExpression("MySpriteObject.MyVariable.MyChild + MySpriteObject.MyVariable2"); + parser.ParseExpression("MyObject.MyObjectStructureVariable.MyChild + MySpriteObject.MyVariable"); gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string"); node->Visit(validator); @@ -2475,7 +2561,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") { } { auto node = - parser.ParseExpression("MySpriteObject.MyVariable[\"MyChild\"] + MySpriteObject.MyVariable2"); + parser.ParseExpression("MyObject.MyObjectStructureVariable[\"MyChild\"] + MySpriteObject.MyVariable"); gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string"); node->Visit(validator); @@ -2499,8 +2585,10 @@ TEST_CASE("ExpressionParser2", "[common][events]") { gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string"); node->Visit(validator); - RequireNoError(validator); - // TODO Add a non-fatal error + RequireNoFatalError(validator); + RequireAllErrorsCount(validator, 1); + REQUIRE(validator.GetAllErrors()[0]->GetMessage() == + "No child variable with this name found."); } SECTION("Invalid object variables (1 level, non existing object)") { @@ -3929,7 +4017,9 @@ TEST_CASE("ExpressionParser2", "[common][events]") { "objectvar", "MyObject"); node->Visit(validator); RequireNoFatalError(validator); - // TODO Add a non-fatal error + RequireAllErrorsCount(validator, 1); + REQUIRE(validator.GetAllErrors()[0]->GetMessage() == + "This variable does not exist on this object or group."); } SECTION("Undeclared object variable with children") { @@ -3941,7 +4031,9 @@ TEST_CASE("ExpressionParser2", "[common][events]") { "objectvar", "MyObject"); node->Visit(validator); RequireNoFatalError(validator); - // TODO Add a non-fatal error + RequireAllErrorsCount(validator, 1); + REQUIRE(validator.GetAllErrors()[0]->GetMessage() == + "This variable does not exist on this object or group."); } SECTION("Undeclared object variable in expression") { @@ -4324,7 +4416,6 @@ TEST_CASE("ExpressionParser2", "[common][events]") { gd::ExpressionValidator validator(platform, projectScopedContainers, "string"); node->Visit(validator); - RequireNoError(validator); } // A string concatenated with a number variable (will have to be casted to a string in code generation) @@ -4335,7 +4426,6 @@ TEST_CASE("ExpressionParser2", "[common][events]") { gd::ExpressionValidator validator(platform, projectScopedContainers, "string"); node->Visit(validator); - RequireNoError(validator); } // A string concatenated with an unknown variable (will have to be casted to a string in code generation) @@ -4346,8 +4436,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") { gd::ExpressionValidator validator(platform, projectScopedContainers, "string"); node->Visit(validator); - - RequireNoError(validator); + RequireNoFatalError(validator); } } SECTION("Expression/parent type is 'number'") { @@ -4367,7 +4456,6 @@ TEST_CASE("ExpressionParser2", "[common][events]") { gd::ExpressionValidator validator(platform, projectScopedContainers, "number"); node->Visit(validator); - RequireNoError(validator); } // A number concatenated with a string variable (will have to be casted to a number in code generation) @@ -4378,7 +4466,6 @@ TEST_CASE("ExpressionParser2", "[common][events]") { gd::ExpressionValidator validator(platform, projectScopedContainers, "number"); node->Visit(validator); - RequireNoError(validator); } // A number concatenated with an unknown variable (will have to be casted to a number in code generation) @@ -4389,8 +4476,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") { gd::ExpressionValidator validator(platform, projectScopedContainers, "number"); node->Visit(validator); - - RequireNoError(validator); + RequireNoFatalError(validator); } } SECTION("Expression/parent type is 'number|string'") { @@ -4411,7 +4497,6 @@ TEST_CASE("ExpressionParser2", "[common][events]") { gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string"); node->Visit(validator); - RequireNoError(validator); } // A string concatenated with a number variable (will have to be casted to a string in code generation) @@ -4422,7 +4507,6 @@ TEST_CASE("ExpressionParser2", "[common][events]") { gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string"); node->Visit(validator); - RequireNoError(validator); } // A string concatenated with an unknown variable (will have to be casted to a string in code generation) @@ -4433,8 +4517,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") { gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string"); node->Visit(validator); - - RequireNoError(validator); + RequireNoFatalError(validator); } } SECTION("Expression/parent inferred type is 'number'") { @@ -4454,7 +4537,6 @@ TEST_CASE("ExpressionParser2", "[common][events]") { gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string"); node->Visit(validator); - RequireNoError(validator); } // A number concatenated with a string variable (will have to be casted to a number in code generation) @@ -4465,7 +4547,6 @@ TEST_CASE("ExpressionParser2", "[common][events]") { gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string"); node->Visit(validator); - RequireNoError(validator); } // A number concatenated with an unknown variable (will have to be casted to a number in code generation) @@ -4476,8 +4557,7 @@ TEST_CASE("ExpressionParser2", "[common][events]") { gd::ExpressionValidator validator(platform, projectScopedContainers, "number|string"); node->Visit(validator); - - RequireNoError(validator); + RequireNoFatalError(validator); } } } diff --git a/Core/tests/ExpressionParser2NaugtyStrings.cpp b/Core/tests/ExpressionParser2NaugtyStrings.cpp index 3a44004e5e15..5fe6ab2ced49 100644 --- a/Core/tests/ExpressionParser2NaugtyStrings.cpp +++ b/Core/tests/ExpressionParser2NaugtyStrings.cpp @@ -8,7 +8,6 @@ #include "DummyPlatform.h" #include "GDCore/Extensions/Platform.h" #include "GDCore/Extensions/PlatformExtension.h" -#include "GDCore/IDE/Events/ExpressionValidator.h" #include "GDCore/Project/Layout.h" #include "GDCore/Project/Project.h" #include "catch.hpp" diff --git a/Core/tests/ExpressionParser2NodePrinter.cpp b/Core/tests/ExpressionParser2NodePrinter.cpp index 5638a7b38758..7fe9d0a9a8a2 100644 --- a/Core/tests/ExpressionParser2NodePrinter.cpp +++ b/Core/tests/ExpressionParser2NodePrinter.cpp @@ -8,7 +8,6 @@ #include "GDCore/Events/Parsers/ExpressionParser2.h" #include "GDCore/Extensions/Platform.h" #include "GDCore/Extensions/PlatformExtension.h" -#include "GDCore/IDE/Events/ExpressionValidator.h" #include "GDCore/Project/Layout.h" #include "GDCore/Project/Project.h" #include "catch.hpp"