diff --git a/po/colobot.pot b/po/colobot.pot index 91bfcdbd..1f183eea 100644 --- a/po/colobot.pot +++ b/po/colobot.pot @@ -1730,6 +1730,9 @@ msgstr "" msgid "Expression expected after =" msgstr "" +msgid "Ambiguous call to overloaded function" +msgstr "" + msgid "Dividing by zero" msgstr "" diff --git a/po/de.po b/po/de.po index be31b511..05a31006 100644 --- a/po/de.po +++ b/po/de.po @@ -86,6 +86,9 @@ msgstr "Trägt schon etwas" msgid "Alternative camera mode\\Move sideways instead of rotating (in free camera)" msgstr "" +msgid "Ambiguous call to overloaded function" +msgstr "" + msgid "Analysis already performed" msgstr "Analyse schon durchgeführt" diff --git a/po/fr.po b/po/fr.po index 996fc6fb..9fb7a344 100644 --- a/po/fr.po +++ b/po/fr.po @@ -81,6 +81,9 @@ msgstr "Porte déjà quelque chose" msgid "Alternative camera mode\\Move sideways instead of rotating (in free camera)" msgstr "" +msgid "Ambiguous call to overloaded function" +msgstr "" + msgid "Analysis already performed" msgstr "Analyse déjà effectuée" diff --git a/po/pl.po b/po/pl.po index 1b0dc6d4..d9cde920 100644 --- a/po/pl.po +++ b/po/pl.po @@ -84,6 +84,9 @@ msgstr "Nie można nieść więcej przedmiotów" msgid "Alternative camera mode\\Move sideways instead of rotating (in free camera)" msgstr "Alternatywny tryb kamery\\Poruszaj na boki zamiast obracać (w kamerze swobodnej)" +msgid "Ambiguous call to overloaded function" +msgstr "Niejednoznaczne wywołanie przeciążonej funkcji" + msgid "Analysis already performed" msgstr "Analiza została już wykonana" diff --git a/po/ru.po b/po/ru.po index 2e375df6..a6c1a61f 100644 --- a/po/ru.po +++ b/po/ru.po @@ -84,6 +84,9 @@ msgstr "Уже что-то несу" msgid "Alternative camera mode\\Move sideways instead of rotating (in free camera)" msgstr "Альтернативный режим камеры\\Движение в стороны вместо поворачивания (в режиме свободной камеры)" +msgid "Ambiguous call to overloaded function" +msgstr "" + msgid "Analysis already performed" msgstr "Анализ уже выполнен" diff --git a/src/CBot/CBotClass.cpp b/src/CBot/CBotClass.cpp index a0ec6402..460ab9fa 100644 --- a/src/CBot/CBotClass.cpp +++ b/src/CBot/CBotClass.cpp @@ -639,7 +639,6 @@ bool CBotClass::CompileDefItem(CBotToken* &p, CBotCStack* pStack, bool bSecond) { // return a method precompiled in pass 1 CBotFunction* pf = m_pMethod; - CBotFunction* prev = nullptr; CBotToken* ppp = p; CBotCStack* pStk = pStack->TokenStack(nullptr, true); CBotDefParam* params = CBotDefParam::Compile(p, pStk ); @@ -648,7 +647,6 @@ bool CBotClass::CompileDefItem(CBotToken* &p, CBotCStack* pStack, bool bSecond) while ( pf != nullptr ) // search by name and parameters { if (pf->GetName() == pp && pf->CheckParam( params )) break; - prev = pf; pf = pf->Next(); } @@ -684,6 +682,7 @@ bool CBotClass::CompileDefItem(CBotToken* &p, CBotCStack* pStack, bool bSecond) initType = CBotVar::InitType::DEF; pcopy->SetInit(initType); pcopy->SetUniqNum(pv->GetUniqNum()); + pcopy->SetPrivate(pv->GetPrivate()); pile->AddVar(pcopy); pv = pv->GetNext(); } @@ -693,18 +692,12 @@ bool CBotClass::CompileDefItem(CBotToken* &p, CBotCStack* pStack, bool bSecond) // compiles a method p = pBase; CBotFunction* f = - CBotFunction::Compile(p, pile, nullptr/*, false*/); + CBotFunction::Compile(p, pile, pf/*, false*/); if ( f != nullptr ) { f->m_pProg = pStack->GetProgram(); f->m_bSynchro = bSynchro; - // replaces the element in the chain - f->m_next = pf->m_next; - pf->m_next = nullptr; - delete pf; - if (prev == nullptr) m_pMethod = f; - else prev->m_next = f; } pStack->Return(nullptr, pile); } diff --git a/src/CBot/CBotEnums.h b/src/CBot/CBotEnums.h index 24e0095b..da0dbea7 100644 --- a/src/CBot/CBotEnums.h +++ b/src/CBot/CBotEnums.h @@ -236,6 +236,7 @@ enum CBotError : int CBotErrPrivate = 5041, //!< protected item CBotErrNoPublic = 5042, //!< missing word "public" CBotErrNoExpression = 5043, //!< expression expected after = + CBotErrAmbiguousCall = 5044, //!< ambiguous call to overloaded function // Runtime errors CBotErrZeroDiv = 6000, //!< division by zero diff --git a/src/CBot/CBotInstr/CBotExprRetVar.cpp b/src/CBot/CBotInstr/CBotExprRetVar.cpp index 96ff03c0..643a4690 100644 --- a/src/CBot/CBotInstr/CBotExprRetVar.cpp +++ b/src/CBot/CBotInstr/CBotExprRetVar.cpp @@ -105,12 +105,12 @@ CBotInstr* CBotExprRetVar::Compile(CBotToken*& p, CBotCStack* pStack, bool bMeth CBotFieldExpr* i = new CBotFieldExpr(); i->SetToken(pp); inst->AddNext3(i); + CBotVar* preVar = var; var = var->GetItem(p->GetString()); if (var != nullptr) { i->SetUniqNum(var->GetUniqNum()); - if ( var->IsPrivate() && - !pStk->GetProgram()->m_bCompileClass) + if (CBotFieldExpr::CheckProtectionError(pStk, preVar, var)) { pStk->SetError(CBotErrPrivate, pp); goto err; diff --git a/src/CBot/CBotInstr/CBotExprVar.cpp b/src/CBot/CBotInstr/CBotExprVar.cpp index 3b774caf..a15c9c9a 100644 --- a/src/CBot/CBotInstr/CBotExprVar.cpp +++ b/src/CBot/CBotInstr/CBotExprVar.cpp @@ -67,8 +67,7 @@ CBotInstr* CBotExprVar::Compile(CBotToken*& p, CBotCStack* pStack, CBotVar::Prot if (ident > 0 && ident < 9000) { - if ( var->IsPrivate(privat) && - !pStk->GetProgram()->m_bCompileClass) + if (CBotFieldExpr::CheckProtectionError(pStk, nullptr, var, privat)) { pStk->SetError(CBotErrPrivate, p); goto err; @@ -133,12 +132,12 @@ CBotInstr* CBotExprVar::Compile(CBotToken*& p, CBotCStack* pStack, CBotVar::Prot CBotFieldExpr* i = new CBotFieldExpr(); // new element i->SetToken(pp); // keeps the name of the token inst->AddNext3(i); // add after + CBotVar* preVar = var; var = var->GetItem(p->GetString()); // get item correspondent if (var != nullptr) { i->SetUniqNum(var->GetUniqNum()); - if ( var->IsPrivate() && - !pStk->GetProgram()->m_bCompileClass) + if (CBotFieldExpr::CheckProtectionError(pStk, preVar, var, privat)) { pStk->SetError(CBotErrPrivate, pp); goto err; diff --git a/src/CBot/CBotInstr/CBotFieldExpr.cpp b/src/CBot/CBotInstr/CBotFieldExpr.cpp index 56ba987c..09ac5f8e 100644 --- a/src/CBot/CBotInstr/CBotFieldExpr.cpp +++ b/src/CBot/CBotInstr/CBotFieldExpr.cpp @@ -134,4 +134,57 @@ std::string CBotFieldExpr::GetDebugData() return ss.str(); } +//////////////////////////////////////////////////////////////////////////////// +bool CBotFieldExpr::CheckProtectionError(CBotCStack* pStack, CBotVar* pPrev, CBotVar* pVar, + CBotVar::ProtectionLevel privat) +{ + CBotVar::ProtectionLevel varPriv = pVar->GetPrivate(); + + if (privat == CBotVar::ProtectionLevel::ReadOnly && varPriv == privat) + return true; + + if (varPriv == CBotVar::ProtectionLevel::Public) return false; + + std::string prevName = (pPrev == nullptr) ? "" : pPrev->GetName(); + + // implicit 'this.'var, this.var, or super.var + if (pPrev == nullptr || prevName == "this" || prevName == "super") // member of the current class + { + if (varPriv == CBotVar::ProtectionLevel::Private) // var is private ? + { + CBotToken token("this"); + CBotVar* pThis = pStack->FindVar(token); + CBotClass* pClass = pThis->GetClass(); // the current class + + CBotVar* pVarList = pClass->GetVar(); + + int ident = pVar->GetUniqNum(); + // check if var is inherited from a parent class + if (pVarList == nullptr || ident < pVarList->GetUniqNum()) + return true; + } + } + else // any other context + { + if (pVar->IsPrivate()) // var is protected or private ? + { + CBotToken token("this"); + CBotVar* pThis = pStack->FindVar(token); + + if (pThis == nullptr) return true; // inside a function ? + if (pThis->GetType() != CBotTypPointer) return true; + + CBotClass* pClass = pThis->GetClass(); // the current class + + if (!pClass->IsChildOf(pPrev->GetClass())) // var is member of some other class ? + return true; + + if (varPriv == CBotVar::ProtectionLevel::Private && // private member of a parent class + pClass != pPrev->GetClass()) return true; + } + } + + return false; +} + } // namespace CBot diff --git a/src/CBot/CBotInstr/CBotFieldExpr.h b/src/CBot/CBotInstr/CBotFieldExpr.h index db48839b..67ab2f05 100644 --- a/src/CBot/CBotInstr/CBotFieldExpr.h +++ b/src/CBot/CBotInstr/CBotFieldExpr.h @@ -65,6 +65,21 @@ public: */ void RestoreStateVar(CBotStack* &pj, bool bMain) override; + /*! + * \brief Check if access to a variable is allowed or not depending on public/private/protected setting + * + * If this function returns true, the caller is responsible for failing the compilation with ::CBotErrPrivate error. + * This function doesn't set the error flag itself. + * + * \param pStack Current compilation stack frame + * \param pPrev Class instance which variable to check is part of, or nullptr if not part of a class + * \param pVar Variable to check + * \param privat CBotVar::ProtectionLevel::ReadOnly if requesting read-only access, anything else otherwise + * \return true if pVar is inaccessible in the current context, false if access should be allowed + */ + static bool CheckProtectionError(CBotCStack* pStack, CBotVar* pPrev, CBotVar* pVar, + CBotVar::ProtectionLevel privat = CBotVar::ProtectionLevel::Protected); + protected: virtual const std::string GetDebugName() override { return "CBotFieldExpr"; } virtual std::string GetDebugData() override; diff --git a/src/CBot/CBotInstr/CBotFunction.cpp b/src/CBot/CBotInstr/CBotFunction.cpp index d00e8ea6..0b1e05b5 100644 --- a/src/CBot/CBotInstr/CBotFunction.cpp +++ b/src/CBot/CBotInstr/CBotFunction.cpp @@ -224,10 +224,6 @@ CBotFunction* CBotFunction::Compile(CBotToken* &p, CBotCStack* pStack, CBotFunct func->m_closeblk = (p != nullptr && p->GetPrev() != nullptr) ? *(p->GetPrev()) : CBotToken(); if ( pStk->IsOk() ) { - if ( func->m_bPublic ) // public function, return known for all - { - CBotFunction::AddPublic(func); - } return pStack->ReturnFunc(func, pStk); } } @@ -466,8 +462,7 @@ CBotFunction* CBotFunction::FindLocalOrPublic(long& nIdent, const std::string& n if ( name.empty() ) return nullptr; - int delta = 99999; // seeks the lowest signature - CBotFunction* pFunc = nullptr; // the best function found + std::map funcMap; if ( this != nullptr ) { @@ -482,44 +477,48 @@ CBotFunction* CBotFunction::FindLocalOrPublic(long& nIdent, const std::string& n CBotVar* pw = ppVars[i++]; // provided list parameter while ( pv != nullptr && pw != nullptr) { - if (!TypesCompatibles(pv->GetTypResult(), pw->GetTypResult())) + CBotTypResult paramType = pv->GetTypResult(); + CBotTypResult argType = pw->GetTypResult(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC); + + if (!TypesCompatibles(paramType, argType)) { - if ( pFunc == nullptr ) TypeOrError = CBotErrBadParam; + if ( funcMap.empty() ) TypeOrError.SetType(CBotErrBadParam); break; } - int d = pv->GetType() - pw->GetType(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC); - alpha += d>0 ? d : -10*d; // quality loss, 10 times more expensive! + if (paramType.Eq(CBotTypPointer) && !argType.Eq(CBotTypNullPointer)) + { + CBotClass* c1 = paramType.GetClass(); + CBotClass* c2 = argType.GetClass(); + while (c2 != c1 && c2 != nullptr) // implicit cast + { + alpha += 10; + c2 = c2->GetParent(); + } + } + else + { + int d = pv->GetType() - pw->GetType(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC); + alpha += d>0 ? d : -10*d; // quality loss, 10 times more expensive! + } pv = pv->GetNext(); pw = ppVars[i++]; } if ( pw != nullptr ) { - if ( pFunc != nullptr ) continue; + if ( !funcMap.empty() ) continue; if ( TypeOrError.Eq(CBotErrLowParam) ) TypeOrError.SetType(CBotErrNbParam); if ( TypeOrError.Eq(CBotErrUndefCall)) TypeOrError.SetType(CBotErrOverParam); continue; // too many parameters } if ( pv != nullptr ) { - if ( pFunc != nullptr ) continue; + if ( !funcMap.empty() ) continue; if ( TypeOrError.Eq(CBotErrOverParam) ) TypeOrError.SetType(CBotErrNbParam); if ( TypeOrError.Eq(CBotErrUndefCall) ) TypeOrError.SetType(CBotErrLowParam); continue; // not enough parameters } - - if (alpha == 0) // perfect signature - { - nIdent = pt->m_nFuncIdent; - TypeOrError = pt->m_retTyp; - return pt; - } - - if ( alpha < delta ) // a better signature? - { - pFunc = pt; - delta = alpha; - } + funcMap.insert( std::pair(pt, alpha) ); } } } @@ -537,50 +536,72 @@ CBotFunction* CBotFunction::FindLocalOrPublic(long& nIdent, const std::string& n CBotVar* pw = ppVars[i++]; // list of provided parameters while ( pv != nullptr && pw != nullptr) { - if (!TypesCompatibles(pv->GetTypResult(), pw->GetTypResult())) + CBotTypResult paramType = pv->GetTypResult(); + CBotTypResult argType = pw->GetTypResult(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC); + + if (!TypesCompatibles(paramType, argType)) { - if ( pFunc == nullptr ) TypeOrError = CBotErrBadParam; + if ( funcMap.empty() ) TypeOrError.SetType(CBotErrBadParam); break; } - int d = pv->GetType() - pw->GetType(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC); - alpha += d>0 ? d : -10*d; // quality loss, 10 times more expensive! + if (paramType.Eq(CBotTypPointer) && !argType.Eq(CBotTypNullPointer)) + { + CBotClass* c1 = paramType.GetClass(); + CBotClass* c2 = argType.GetClass(); + while (c2 != c1 && c2 != nullptr) // implicit cast + { + alpha += 10; + c2 = c2->GetParent(); + } + } + else + { + int d = pv->GetType() - pw->GetType(CBotVar::GetTypeMode::CLASS_AS_INTRINSIC); + alpha += d>0 ? d : -10*d; // quality loss, 10 times more expensive! + } pv = pv->GetNext(); pw = ppVars[i++]; } if ( pw != nullptr ) { - if ( pFunc != nullptr ) continue; + if ( !funcMap.empty() ) continue; // previous useable function if ( TypeOrError.Eq(CBotErrLowParam) ) TypeOrError.SetType(CBotErrNbParam); if ( TypeOrError.Eq(CBotErrUndefCall)) TypeOrError.SetType(CBotErrOverParam); continue; // to many parameters } if ( pv != nullptr ) { - if ( pFunc != nullptr ) continue; + if ( !funcMap.empty() ) continue; // previous useable function if ( TypeOrError.Eq(CBotErrOverParam) ) TypeOrError.SetType(CBotErrNbParam); if ( TypeOrError.Eq(CBotErrUndefCall) ) TypeOrError.SetType(CBotErrLowParam); continue; // not enough parameters } - - if (alpha == 0) // perfect signature - { - nIdent = pt->m_nFuncIdent; - TypeOrError = pt->m_retTyp; - return pt; - } - - if ( alpha < delta ) // a better signature? - { - pFunc = pt; - delta = alpha; - } + funcMap.insert( std::pair(pt, alpha) ); } } } - if ( pFunc != nullptr ) + if ( !funcMap.empty() ) { + auto it = funcMap.begin(); + CBotFunction* pFunc = it->first; // the best function found + signed int delta = it->second; // seeks the lowest signature + + for (++it ; it != funcMap.end() ; it++) + { + if (it->second < delta) // a better signature? + { + TypeOrError.SetType(CBotNoErr); + pFunc = it->first; + delta = it->second; + continue; + } + + if (it->second == delta) TypeOrError.SetType(CBotErrAmbiguousCall); + } + + if (TypeOrError.Eq(CBotErrAmbiguousCall)) return nullptr; nIdent = pFunc->m_nFuncIdent; TypeOrError = pFunc->m_retTyp; return pFunc; diff --git a/src/CBot/CBotInstr/CBotInstrUtils.cpp b/src/CBot/CBotInstr/CBotInstrUtils.cpp index bbe80b27..067f443a 100644 --- a/src/CBot/CBotInstr/CBotInstrUtils.cpp +++ b/src/CBot/CBotInstr/CBotInstrUtils.cpp @@ -149,12 +149,20 @@ bool TypesCompatibles(const CBotTypResult& type1, const CBotTypResult& type2) if (max >= CBotTypBoolean) { + if (t1 == CBotTypPointer && t2 == CBotTypNullPointer) return true; if (t2 != t1) return false; + if (max == CBotTypPointer) + { + CBotClass* c1 = type1.GetClass(); + CBotClass* c2 = type2.GetClass(); + return c2->IsChildOf(c1); + } + if (max == CBotTypArrayPointer) return TypesCompatibles(type1.GetTypElem(), type2.GetTypElem()); - if (max == CBotTypClass || max == CBotTypPointer) + if (max == CBotTypClass) return type1.GetClass() == type2.GetClass() ; return true ; diff --git a/src/CBot/CBotInstr/CBotLeftExpr.cpp b/src/CBot/CBotInstr/CBotLeftExpr.cpp index 3a9fe155..4678ff8e 100644 --- a/src/CBot/CBotInstr/CBotLeftExpr.cpp +++ b/src/CBot/CBotInstr/CBotLeftExpr.cpp @@ -64,8 +64,7 @@ CBotLeftExpr* CBotLeftExpr::Compile(CBotToken* &p, CBotCStack* pStack) inst->m_nIdent = var->GetUniqNum(); if (inst->m_nIdent > 0 && inst->m_nIdent < 9000) { - if ( var->IsPrivate(CBotVar::ProtectionLevel::ReadOnly) && - !pStk->GetProgram()->m_bCompileClass) + if (CBotFieldExpr::CheckProtectionError(pStk, nullptr, var, CBotVar::ProtectionLevel::ReadOnly)) { pStk->SetError(CBotErrPrivate, p); goto err; @@ -125,11 +124,12 @@ CBotLeftExpr* CBotLeftExpr::Compile(CBotToken* &p, CBotCStack* pStack) if (p->GetType() == TokenTypVar) // must be a name { + CBotVar* preVar = var; var = var->GetItem(p->GetString()); // get item correspondent if (var != nullptr) { - if ( var->IsPrivate(CBotVar::ProtectionLevel::ReadOnly) && - !pStk->GetProgram()->m_bCompileClass) + if (CBotFieldExpr::CheckProtectionError(pStk, preVar, var, + CBotVar::ProtectionLevel::ReadOnly)) { pStk->SetError(CBotErrPrivate, pp); goto err; diff --git a/src/CBot/CBotProgram.cpp b/src/CBot/CBotProgram.cpp index 77dac399..81008297 100644 --- a/src/CBot/CBotProgram.cpp +++ b/src/CBot/CBotProgram.cpp @@ -124,14 +124,13 @@ bool CBotProgram::Compile(const std::string& program, std::vector& if ( p->GetType() == ID_CLASS || ( p->GetType() == ID_PUBLIC && p->GetNext()->GetType() == ID_CLASS )) { - m_bCompileClass = true; CBotClass::Compile(p, pStack.get()); // completes the definition of the class } else { - m_bCompileClass = false; CBotFunction::Compile(p, pStack.get(), next); if (next->IsExtern()) functions.push_back(next->GetName()/* + next->GetParams()*/); + if (next->IsPublic()) CBotFunction::AddPublic(next); next->m_pProg = this; // keeps pointers to the module next = next->Next(); } diff --git a/src/CBot/CBotProgram.h b/src/CBot/CBotProgram.h index 9f5cdc00..165defc1 100644 --- a/src/CBot/CBotProgram.h +++ b/src/CBot/CBotProgram.h @@ -332,13 +332,6 @@ public: */ CBotFunction* GetFunctions(); - /** - * \brief true while compiling class - * - * TODO: refactor this - */ - bool m_bCompileClass; - /** * \brief Returns static list of all registered external calls */ diff --git a/src/CBot/CBotTypResult.h b/src/CBot/CBotTypResult.h index cd8d7468..00683ca5 100644 --- a/src/CBot/CBotTypResult.h +++ b/src/CBot/CBotTypResult.h @@ -106,7 +106,7 @@ public: /** * \brief Returns ::CBotType or ::CBotError stored in this object - * \param mode Mode, see ::GetTypeMode enum + * \param mode Mode, see GetTypeMode enum */ int GetType(GetTypeMode mode = GetTypeMode::NORMAL) const; diff --git a/src/common/restext.cpp b/src/common/restext.cpp index 9770c32a..859a61f9 100644 --- a/src/common/restext.cpp +++ b/src/common/restext.cpp @@ -717,6 +717,7 @@ void InitializeRestext() stringsCbot[CBot::CBotErrPrivate] = TR("Private element"); stringsCbot[CBot::CBotErrNoPublic] = TR("Public required"); stringsCbot[CBot::CBotErrNoExpression] = TR("Expression expected after ="); + stringsCbot[CBot::CBotErrAmbiguousCall] = TR("Ambiguous call to overloaded function"); stringsCbot[CBot::CBotErrZeroDiv] = TR("Dividing by zero"); stringsCbot[CBot::CBotErrNotInit] = TR("Variable not initialized"); diff --git a/test/unit/CBot/CBot_test.cpp b/test/unit/CBot/CBot_test.cpp index 071abb94..797a674e 100644 --- a/test/unit/CBot/CBot_test.cpp +++ b/test/unit/CBot/CBot_test.cpp @@ -1786,3 +1786,270 @@ TEST_F(CBotUT, ClassNewConstructorMethodChain) "}\n" ); } + +TEST_F(CBotUT, PassNullAsArgument) +{ + auto publicProgram = ExecuteTest( + "public class BaseClass {}\n" + "public class SubClass extends BaseClass {}\n" + ); + + ExecuteTest( + "bool Test(BaseClass b) {\n" + " return (b == null);\n" + "}\n" + "extern void PassNullAsArgument() {\n" + " ASSERT(true == Test(null));\n" + "}\n" + ); + + ExecuteTest( + "void Test(BaseClass b) {}\n" + "void Test(SubClass s) {}\n" + "\n" + "extern void AmbiguousCallArgumentNull() {\n" + " Test(null);\n" + "}\n", + CBotErrAmbiguousCall + ); +} + +TEST_F(CBotUT, ClassImplicitCastArguments) +{ + auto publicProgram = ExecuteTest( + "public class BaseClass { int a = 360; }\n" + "public class SubClass extends BaseClass {}\n" + ); + + ExecuteTest( + "bool Test(BaseClass b) {\n" + " SubClass s = b;\n" + " return (360 == s.a);\n" + "}\n" + "extern void UpcastPassingArguments() {\n" + " ASSERT(true == Test(new SubClass()));\n" + "}\n" + ); + + ExecuteTest( + "void Test(BaseClass b, SubClass s) {}\n" + "void Test(SubClass s, BaseClass b) {}\n" + "\n" + "extern void UpcastAmbiguousCall() {\n" + " Test(new SubClass(), new SubClass());\n" + "}\n", + CBotErrAmbiguousCall + ); + + ExecuteTest( + "bool Test(BaseClass b, SubClass s) { return false; }\n" + "bool Test(SubClass s, BaseClass b) { return false; }\n" + "bool Test(SubClass s, SubClass s2) { return true; }\n" + "\n" + "extern void NoErrorMoreSpecific() {\n" + " ASSERT(true == Test(new SubClass(), new SubClass()));\n" + "}\n" + ); +} + +TEST_F(CBotUT, AmbiguousCallWithNumbers) +{ + ExecuteTest( + "void Test(int i, float f) {}\n" + "void Test(float f, int i) {}\n" + "\n" + "extern void AmbiguousCallNumbers() {\n" + " Test(1, 2);\n" + "}\n", + CBotErrAmbiguousCall + ); + + ExecuteTest( + "bool Test(int i, float f) { return false; }\n" + "bool Test(float f, int i) { return false; }\n" + "bool Test(int i, int ii) { return true; }\n" + "\n" + "extern void NoErrorMoreSpecific() {\n" + " ASSERT(true == Test(1, 2));\n" + "}\n" + ); +} + +TEST_F(CBotUT, ClassMethodWithPublicKeyword) +{ + auto publicProgram = ExecuteTest( + "public class TestClass {\n" + " public int Test() { return 1; }\n" + "}\n" + ); + + ExecuteTest( + "int Test() { return 2; }\n" + "\n" + "extern void DontCallMethodInTestClass()\n" + "{\n" + " ASSERT(2 == Test());\n" + "}\n" + ); + + ExecuteTest( + "int Test() { return 2; }\n" + "\n" + "public class OtherClass {}\n" + "\n" + "extern void OtherClass::TestCallWithThis()\n" + "{\n" + " this.Test();\n" + "}\n", + CBotErrUndefCall + ); +} + +TEST_F(CBotUT, ClassTestProtectedMember) +{ + auto publicProgram = ExecuteTest( + "public class BaseClass {\n" + " protected int a_protected = 1;\n" + " bool test() {\n" + " a_protected = 1;\n" + " int a = a_protected;\n" + " return true;\n" + " }\n" + "}\n" + "extern void Test() {\n" + " BaseClass b();\n" + " ASSERT(true == b.test());\n" + "}\n" + ); + + ExecuteTest( + "public class SubClass extends BaseClass {\n" + " bool testProtected() {\n" + " a_protected = 1;\n" + " int a = a_protected;\n" + " return true;\n" + " }\n" + "}\n" + "extern void TestSubClassAccessProtected() {\n" + " SubClass s();\n" + " ASSERT(true == s.test());\n" + " ASSERT(true == s.testProtected());\n" + "}\n" + ); + + ExecuteTest( + "extern void TestErrorProtected() {\n" + " BaseClass b();\n" + " int i = b.a_protected;\n" + "}\n", + CBotErrPrivate + ); + + ExecuteTest( + "extern void ErrorProtectedAssignment() {\n" + " BaseClass b();\n" + " b.a_protected = 1;\n" + "}\n", + CBotErrPrivate + ); + + ExecuteTest( + "public class SomeOtherClass {\n" + " void testErrorProtected() {\n" + " BaseClass b();\n" + " int i = b.a_protected;\n" + " }\n" + "}\n", + CBotErrPrivate + ); + + ExecuteTest( + "public class SomeOtherClass {\n" + " void testErrorProtectedAssignment() {\n" + " BaseClass b();\n" + " b.a_protected = 1;\n" + " }\n" + "}\n", + CBotErrPrivate + ); +} + +TEST_F(CBotUT, ClassTestPrivateMember) +{ + auto publicProgram = ExecuteTest( + "public class BaseClass {\n" + " private int a_private = 2;\n" + "\n" + " bool test() {\n" + " a_private = 2;\n" + " int a = a_private;\n" + " return true;\n" + " }\n" + " bool NoErrorPrivateSameClass() {\n" + " BaseClass b = new BaseClass();\n" + " int a = b.a_private;\n" + " b.a_private = 2;\n" + " return true;\n" + " }\n" + "}\n" + "extern void Test() {\n" + " BaseClass b();\n" + " ASSERT(true == b.test());\n" + " ASSERT(true == b.NoErrorPrivateSameClass());\n" + "}\n" + ); + + ExecuteTest( + "public class SubClass extends BaseClass {\n" + " void testErrorPrivate() {\n" + " int a = a_private;\n" + " }\n" + "}\n", + CBotErrPrivate + ); + + ExecuteTest( + "public class SubClass extends BaseClass {\n" + " void testErrorPrivateAssignment() {\n" + " a_private = 2;\n" + " }\n" + "}\n", + CBotErrPrivate + ); + + ExecuteTest( + "extern void TestErrorPrivate() {\n" + " BaseClass b();\n" + " int i = b.a_private;\n" + "}\n", + CBotErrPrivate + ); + + ExecuteTest( + "extern void ErrorPrivateAssignment() {\n" + " BaseClass b();\n" + " b.a_private = 2;\n" + "}\n", + CBotErrPrivate + ); + + ExecuteTest( + "public class SomeOtherClass {\n" + " void testErrorPrivate() {\n" + " BaseClass b();\n" + " int i = b.a_private;\n" + " }\n" + "}\n", + CBotErrPrivate + ); + + ExecuteTest( + "public class SomeOtherClass {\n" + " void testErrorPrivateAssignment() {\n" + " BaseClass b();\n" + " b.a_private = 1;\n" + " }\n" + "}\n", + CBotErrPrivate + ); +}