diff --git a/ets2panda/checker/ETSAnalyzer.cpp b/ets2panda/checker/ETSAnalyzer.cpp index 653bcf2c2e63453c04dd8f104585377b7d8b75a2..b21662a14af05d60446ef279ca7ff27e386f6da7 100644 --- a/ets2panda/checker/ETSAnalyzer.cpp +++ b/ets2panda/checker/ETSAnalyzer.cpp @@ -526,6 +526,29 @@ checker::Type *ETSAnalyzer::Check(ir::ETSNewArrayInstanceExpression *expr) const return expr->TsType(); } +ETSObjectType *ETSAnalyzer::CheckExprClassDefinitionHelper(ir::ETSNewClassInstanceExpression *expr, + ETSObjectType *calleeObj, ETSChecker *checker) const +{ + if (!calleeObj->HasObjectFlag(checker::ETSObjectFlags::ABSTRACT) && calleeObj->GetDeclNode()->IsFinal()) { + checker->ThrowTypeError({"Class ", calleeObj->Name(), " cannot be both 'abstract' and 'final'."}, + calleeObj->GetDeclNode()->Start()); + } + + bool fromInterface = calleeObj->HasObjectFlag(checker::ETSObjectFlags::INTERFACE); + auto *classType = checker->BuildAnonymousClassProperties( + expr->ClassDefinition(), fromInterface ? checker->GlobalETSObjectType() : calleeObj); + if (fromInterface) { + classType->AddInterface(calleeObj); + calleeObj = checker->GlobalETSObjectType(); + } + expr->ClassDefinition()->SetTsType(classType); + checker->CheckClassDefinition(expr->ClassDefinition()); + checker->CheckInnerClassMembers(classType); + expr->SetTsType(classType); + + return calleeObj; +} + checker::Type *ETSAnalyzer::Check(ir::ETSNewClassInstanceExpression *expr) const { ETSChecker *checker = GetETSChecker(); @@ -539,22 +562,7 @@ checker::Type *ETSAnalyzer::Check(ir::ETSNewClassInstanceExpression *expr) const expr->SetTsType(calleeObj); if (expr->ClassDefinition() != nullptr) { - if (!calleeObj->HasObjectFlag(checker::ETSObjectFlags::ABSTRACT) && calleeObj->GetDeclNode()->IsFinal()) { - checker->ThrowTypeError({"Class ", calleeObj->Name(), " cannot be both 'abstract' and 'final'."}, - calleeObj->GetDeclNode()->Start()); - } - - bool fromInterface = calleeObj->HasObjectFlag(checker::ETSObjectFlags::INTERFACE); - auto *classType = checker->BuildAnonymousClassProperties( - expr->ClassDefinition(), fromInterface ? checker->GlobalETSObjectType() : calleeObj); - if (fromInterface) { - classType->AddInterface(calleeObj); - calleeObj = checker->GlobalETSObjectType(); - } - expr->ClassDefinition()->SetTsType(classType); - checker->CheckClassDefinition(expr->ClassDefinition()); - checker->CheckInnerClassMembers(classType); - expr->SetTsType(classType); + calleeObj = CheckExprClassDefinitionHelper(expr, calleeObj, checker); } else if (calleeObj->HasObjectFlag(checker::ETSObjectFlags::ABSTRACT)) { checker->ThrowTypeError({calleeObj->Name(), " is abstract therefore cannot be instantiated."}, expr->Start()); } @@ -679,66 +687,76 @@ checker::Type *ETSAnalyzer::GetPreferredType(ir::ArrayExpression *expr) const return expr->preferredType_; } -checker::Type *ETSAnalyzer::Check(ir::ArrayExpression *expr) const +void ETSAnalyzer::CheckNonEmptyElemetsHelper(ir::ArrayExpression *expr, bool isArray, ETSChecker *checker) const { - ETSChecker *checker = GetETSChecker(); - if (expr->TsType() != nullptr) { - return expr->TsType(); + if (expr->preferredType_ == nullptr) { + expr->preferredType_ = expr->Elements()[0]->Check(checker); } - const bool isArray = (expr->preferredType_ != nullptr) && expr->preferredType_->IsETSArrayType() && - !expr->preferredType_->IsETSTupleType(); - if (isArray) { - expr->preferredType_ = expr->preferredType_->AsETSArrayType()->ElementType(); - } + const bool isPreferredTuple = expr->preferredType_->IsETSTupleType(); + auto *const targetElementType = + isPreferredTuple && !isArray ? expr->preferredType_->AsETSTupleType()->ElementType() : expr->preferredType_; - if (!expr->Elements().empty()) { - if (expr->preferredType_ == nullptr) { - expr->preferredType_ = expr->Elements()[0]->Check(checker); + for (std::size_t idx = 0; idx < expr->elements_.size(); ++idx) { + auto *const currentElement = expr->elements_[idx]; + + if (currentElement->IsArrayExpression()) { + expr->HandleNestedArrayExpression(checker, currentElement->AsArrayExpression(), isArray, isPreferredTuple, + idx); } - const bool isPreferredTuple = expr->preferredType_->IsETSTupleType(); - auto *const targetElementType = - isPreferredTuple && !isArray ? expr->preferredType_->AsETSTupleType()->ElementType() : expr->preferredType_; + if (currentElement->IsObjectExpression()) { + currentElement->AsObjectExpression()->SetPreferredType(expr->preferredType_); + } - for (std::size_t idx = 0; idx < expr->elements_.size(); ++idx) { - auto *const currentElement = expr->elements_[idx]; + checker::Type *elementType = currentElement->Check(checker); - if (currentElement->IsArrayExpression()) { - expr->HandleNestedArrayExpression(checker, currentElement->AsArrayExpression(), isArray, - isPreferredTuple, idx); - } + if (elementType->IsETSArrayType() || !isPreferredTuple) { + checker::AssignmentContext(checker->Relation(), currentElement, elementType, targetElementType, + currentElement->Start(), + {"Array element type '", elementType, "' is not assignable to explicit type '", + expr->GetPreferredType(), "'"}); + continue; + } - if (currentElement->IsObjectExpression()) { - currentElement->AsObjectExpression()->SetPreferredType(expr->preferredType_); - } + auto *const compareType = expr->preferredType_->AsETSTupleType()->GetTypeAtIndex(idx); - checker::Type *elementType = currentElement->Check(checker); + if (compareType == nullptr) { + checker->ThrowTypeError({"Too many elements in array initializer for tuple with size of ", + static_cast(expr->preferredType_->AsETSTupleType()->GetTupleSize())}, + currentElement->Start()); + } - if (!elementType->IsETSArrayType() && isPreferredTuple) { - auto *const compareType = expr->preferredType_->AsETSTupleType()->GetTypeAtIndex(idx); + const checker::CastingContext cast( + checker->Relation(), currentElement, elementType, compareType, currentElement->Start(), + {"Array initializer's type is not assignable to tuple type at index: ", idx}); - if (compareType == nullptr) { - checker->ThrowTypeError( - {"Too many elements in array initializer for tuple with size of ", - static_cast(expr->preferredType_->AsETSTupleType()->GetTupleSize())}, - currentElement->Start()); - } + elementType = compareType; - const checker::CastingContext cast( - checker->Relation(), currentElement, elementType, compareType, currentElement->Start(), - {"Array initializer's type is not assignable to tuple type at index: ", idx}); + checker::AssignmentContext(checker->Relation(), currentElement, elementType, targetElementType, + currentElement->Start(), + {"Array element type '", elementType, "' is not assignable to explicit type '", + expr->GetPreferredType(), "'"}); + } - elementType = compareType; - } + expr->SetPreferredType(targetElementType); +} - checker::AssignmentContext(checker->Relation(), currentElement, elementType, targetElementType, - currentElement->Start(), - {"Array element type '", elementType, "' is not assignable to explicit type '", - expr->GetPreferredType(), "'"}); - } +checker::Type *ETSAnalyzer::Check(ir::ArrayExpression *expr) const +{ + ETSChecker *checker = GetETSChecker(); + if (expr->TsType() != nullptr) { + return expr->TsType(); + } + + const bool isArray = (expr->preferredType_ != nullptr) && expr->preferredType_->IsETSArrayType() && + !expr->preferredType_->IsETSTupleType(); + if (isArray) { + expr->preferredType_ = expr->preferredType_->AsETSArrayType()->ElementType(); + } - expr->SetPreferredType(targetElementType); + if (!expr->Elements().empty()) { + CheckNonEmptyElemetsHelper(expr, isArray, checker); } if (expr->preferredType_ == nullptr) { @@ -807,34 +825,13 @@ checker::Type *ETSAnalyzer::Check(ir::ArrowFunctionExpression *expr) const return expr->TsType(); } -checker::Type *ETSAnalyzer::Check(ir::AssignmentExpression *expr) const +void ETSAnalyzer::CheckOperatorTypeHelper(ir::AssignmentExpression *expr) const { ETSChecker *checker = GetETSChecker(); - - if (expr->TsType() != nullptr) { - return expr->TsType(); - } - auto *leftType = expr->Left()->Check(checker); - if (expr->Left()->IsMemberExpression() && - expr->Left()->AsMemberExpression()->Object()->TsType()->IsETSArrayType() && - expr->Left()->AsMemberExpression()->Property()->IsIdentifier() && - expr->Left()->AsMemberExpression()->Property()->AsIdentifier()->Name().Is("length")) { - checker->ThrowTypeError("Setting the length of an array is not permitted", expr->Left()->Start()); - } - - if (expr->Left()->IsIdentifier()) { - expr->target_ = expr->Left()->AsIdentifier()->Variable(); - } else { - expr->target_ = expr->Left()->AsMemberExpression()->PropVar(); - } - - if (expr->target_ != nullptr) { - checker->ValidateUnaryOperatorOperand(expr->target_); - } - checker::Type *sourceType {}; ir::Expression *relationNode = expr->Right(); + switch (expr->OperatorType()) { case lexer::TokenType::PUNCTUATOR_MULTIPLY_EQUAL: case lexer::TokenType::PUNCTUATOR_EXPONENTIATION_EQUAL: @@ -874,9 +871,36 @@ checker::Type *ETSAnalyzer::Check(ir::AssignmentExpression *expr) const break; } } - checker::AssignmentContext(checker->Relation(), relationNode, sourceType, leftType, expr->Right()->Start(), {"Initializers type is not assignable to the target type"}); +} + +checker::Type *ETSAnalyzer::Check(ir::AssignmentExpression *expr) const +{ + ETSChecker *checker = GetETSChecker(); + + if (expr->TsType() != nullptr) { + return expr->TsType(); + } + + if (expr->Left()->IsMemberExpression() && + expr->Left()->AsMemberExpression()->Object()->TsType()->IsETSArrayType() && + expr->Left()->AsMemberExpression()->Property()->IsIdentifier() && + expr->Left()->AsMemberExpression()->Property()->AsIdentifier()->Name().Is("length")) { + checker->ThrowTypeError("Setting the length of an array is not permitted", expr->Left()->Start()); + } + + if (expr->Left()->IsIdentifier()) { + expr->target_ = expr->Left()->AsIdentifier()->Variable(); + } else { + expr->target_ = expr->Left()->AsMemberExpression()->PropVar(); + } + + if (expr->target_ != nullptr) { + checker->ValidateUnaryOperatorOperand(expr->target_); + } + + CheckOperatorTypeHelper(expr); expr->SetTsType(expr->Left()->TsType()); return expr->TsType(); @@ -1327,6 +1351,46 @@ checker::Type *ETSAnalyzer::PreferredType(ir::ObjectExpression *expr) const return expr->preferredType_; } +void ETSAnalyzer::CheckPropertiesHelper(ir::ObjectExpression *expr, checker::ETSObjectType *objType) const +{ + ETSChecker *checker = GetETSChecker(); + for (ir::Expression *propExpr : expr->Properties()) { + ASSERT(propExpr->IsProperty()); + ir::Property *prop = propExpr->AsProperty(); + ir::Expression *key = prop->Key(); + ir::Expression *value = prop->Value(); + + util::StringView pname; + if (key->IsStringLiteral()) { + pname = key->AsStringLiteral()->Str(); + } else if (key->IsIdentifier()) { + pname = key->AsIdentifier()->Name(); + } else { + checker->ThrowTypeError({"key in class composite should be either identifier or string literal"}, + expr->Start()); + } + varbinder::LocalVariable *lv = objType->GetProperty(pname, checker::PropertySearchFlags::SEARCH_INSTANCE_FIELD | + checker::PropertySearchFlags::SEARCH_IN_BASE); + if (lv == nullptr) { + checker->ThrowTypeError({"type ", objType->Name(), " has no property named ", pname}, propExpr->Start()); + } + checker->ValidatePropertyAccess(lv, objType, propExpr->Start()); + if (lv->HasFlag(varbinder::VariableFlags::READONLY)) { + checker->ThrowTypeError({"cannot assign to readonly property ", pname}, propExpr->Start()); + } + + auto *propType = checker->GetTypeOfVariable(lv); + key->SetTsType(propType); + + if (value->IsObjectExpression()) { + value->AsObjectExpression()->SetPreferredType(propType); + } + value->SetTsType(value->Check(checker)); + checker::AssignmentContext(checker->Relation(), value, value->TsType(), propType, value->Start(), + {"value type is not assignable to the property type"}); + } +} + checker::Type *ETSAnalyzer::Check(ir::ObjectExpression *expr) const { ETSChecker *checker = GetETSChecker(); @@ -1372,41 +1436,7 @@ checker::Type *ETSAnalyzer::Check(ir::ObjectExpression *expr) const checker->ThrowTypeError({"type ", objType->Name(), " has no parameterless constructor"}, expr->Start()); } - for (ir::Expression *propExpr : expr->Properties()) { - ASSERT(propExpr->IsProperty()); - ir::Property *prop = propExpr->AsProperty(); - ir::Expression *key = prop->Key(); - ir::Expression *value = prop->Value(); - - util::StringView pname; - if (key->IsStringLiteral()) { - pname = key->AsStringLiteral()->Str(); - } else if (key->IsIdentifier()) { - pname = key->AsIdentifier()->Name(); - } else { - checker->ThrowTypeError({"key in class composite should be either identifier or string literal"}, - expr->Start()); - } - varbinder::LocalVariable *lv = objType->GetProperty(pname, checker::PropertySearchFlags::SEARCH_INSTANCE_FIELD | - checker::PropertySearchFlags::SEARCH_IN_BASE); - if (lv == nullptr) { - checker->ThrowTypeError({"type ", objType->Name(), " has no property named ", pname}, propExpr->Start()); - } - checker->ValidatePropertyAccess(lv, objType, propExpr->Start()); - if (lv->HasFlag(varbinder::VariableFlags::READONLY)) { - checker->ThrowTypeError({"cannot assign to readonly property ", pname}, propExpr->Start()); - } - - auto *propType = checker->GetTypeOfVariable(lv); - key->SetTsType(propType); - - if (value->IsObjectExpression()) { - value->AsObjectExpression()->SetPreferredType(propType); - } - value->SetTsType(value->Check(checker)); - checker::AssignmentContext(checker->Relation(), value, value->TsType(), propType, value->Start(), - {"value type is not assignable to the property type"}); - } + CheckPropertiesHelper(expr, objType); expr->SetTsType(objType); return objType; @@ -2396,18 +2426,20 @@ checker::Type *ETSAnalyzer::Check(ir::TryStatement *st) const for (auto *catchClause : st->CatchClauses()) { auto exceptionType = catchClause->Check(checker); - if ((exceptionType != nullptr) && (catchClause->Param() != nullptr)) { - auto *clauseType = exceptionType->AsETSObjectType(); - for (auto *exception : exceptions) { - checker->Relation()->IsIdenticalTo(clauseType, exception); - if (checker->Relation()->IsTrue()) { - checker->ThrowTypeError("Redeclaration of exception type", catchClause->Start()); - } - } + if ((exceptionType == nullptr) || (catchClause->Param() == nullptr)) { + continue; + } + auto *clauseType = exceptionType->AsETSObjectType(); - exceptions.push_back(clauseType); + for (auto *exception : exceptions) { + checker->Relation()->IsIdenticalTo(clauseType, exception); + if (checker->Relation()->IsTrue()) { + checker->ThrowTypeError("Redeclaration of exception type", catchClause->Start()); + } } + + exceptions.push_back(clauseType); } bool defaultCatchFound = false; diff --git a/ets2panda/checker/ETSAnalyzer.h b/ets2panda/checker/ETSAnalyzer.h index db1f0439fbbfc3eb902175fde718597be403eb58..398d6a9ed23b5473bb23ced181d04ddc406d704e 100644 --- a/ets2panda/checker/ETSAnalyzer.h +++ b/ets2panda/checker/ETSAnalyzer.h @@ -29,6 +29,12 @@ public: AST_NODE_MAPPING(DECLARE_ETSANALYZER_CHECK_METHOD) #undef DECLARE_ETSANALYZER_CHECK_METHOD + void CheckOperatorTypeHelper(ir::AssignmentExpression *expr) const; + void CheckPropertiesHelper(ir::ObjectExpression *expr, checker::ETSObjectType *objType) const; + ETSObjectType *CheckExprClassDefinitionHelper(ir::ETSNewClassInstanceExpression *expr, ETSObjectType *calleeObj, + ETSChecker *checker) const; + void CheckNonEmptyElemetsHelper(ir::ArrayExpression *expr, bool isArray, ETSChecker *checker) const; + // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define DECLARE_ETSANALYZER_CHECK_METHOD(_, __, nodeType, ___) \ virtual checker::Type *Check(ir::nodeType *node) const override; diff --git a/ets2panda/parser/TypedParser.cpp b/ets2panda/parser/TypedParser.cpp index a3018356f2b0dc8c995306197b600d68d79f8d2f..cadd749f75c9acf0c95ff501eb1c601b93560f6f 100644 --- a/ets2panda/parser/TypedParser.cpp +++ b/ets2panda/parser/TypedParser.cpp @@ -887,6 +887,49 @@ ir::ClassDefinition *TypedParser::ParseClassDefinition(ir::ClassDefinitionModifi return classDefinition; } +ir::AstNode *TypedParser::CheckDescription(ClassElementDescriptor *desc, const ArenaVector &properties) +{ + ir::TypeNode *typeAnnotation = ParseClassKeyAnnotation(); + ir::AstNode *property = nullptr; + ir::Expression *propName = ParseClassKey(desc); + + if (desc->isIndexSignature) { + if (!desc->decorators.empty()) { + ThrowSyntaxError("Decorators are not valid here.", desc->decorators.front()->Start()); + } + + ValidateIndexSignatureTypeAnnotation(typeAnnotation); + + if (typeAnnotation == nullptr) { + ThrowSyntaxError("An index signature must have a type annotation"); + } + + if ((desc->modifiers & ir::ModifierFlags::DECLARE) != 0) { + ThrowSyntaxError("'declare' modifier cannot appear on an index signature."); + } + + property = + AllocNode(propName, typeAnnotation, desc->modifiers & ir::ModifierFlags::READONLY); + + property->SetRange({property->AsTSIndexSignature()->Param()->Start(), + property->AsTSIndexSignature()->TypeAnnotation()->End()}); + } else { + ValidateClassMethodStart(desc, typeAnnotation); + property = ParseClassProperty(desc, properties, propName, typeAnnotation); + + if (!desc->decorators.empty()) { + if (desc->isPrivateIdent) { + ThrowSyntaxError("Decorators are not valid here"); + } + + property->AddDecorators(std::move(desc->decorators)); + } + } + + ASSERT(property != nullptr); + return property; +} + // NOLINTNEXTLINE(google-default-arguments) ir::AstNode *TypedParser::ParseClassElement(const ArenaVector &properties, ir::ClassDefinitionModifiers modifiers, ir::ModifierFlags flags, @@ -920,8 +963,6 @@ ir::AstNode *TypedParser::ParseClassElement(const ArenaVector &pr GetContext().Status() |= ParserStatus::ALLOW_THIS_TYPE; } - ir::Expression *propName = ParseClassKey(&desc); - if (desc.methodKind == ir::MethodDefinitionKind::CONSTRUCTOR && !desc.decorators.empty()) { ThrowSyntaxError("Decorators are not valid here.", desc.decorators.front()->Start()); } @@ -937,44 +978,8 @@ ir::AstNode *TypedParser::ParseClassElement(const ArenaVector &pr Lexer()->NextToken(); } - ir::TypeNode *typeAnnotation = ParseClassKeyAnnotation(); - - ir::AstNode *property = nullptr; - - if (desc.isIndexSignature) { - if (!desc.decorators.empty()) { - ThrowSyntaxError("Decorators are not valid here.", desc.decorators.front()->Start()); - } + ir::AstNode *property = CheckDescription(&desc, properties); - ValidateIndexSignatureTypeAnnotation(typeAnnotation); - - if (typeAnnotation == nullptr) { - ThrowSyntaxError("An index signature must have a type annotation"); - } - - if ((desc.modifiers & ir::ModifierFlags::DECLARE) != 0) { - ThrowSyntaxError("'declare' modifier cannot appear on an index signature."); - } - - property = - AllocNode(propName, typeAnnotation, desc.modifiers & ir::ModifierFlags::READONLY); - - property->SetRange({property->AsTSIndexSignature()->Param()->Start(), - property->AsTSIndexSignature()->TypeAnnotation()->End()}); - } else { - ValidateClassMethodStart(&desc, typeAnnotation); - property = ParseClassProperty(&desc, properties, propName, typeAnnotation); - - if (!desc.decorators.empty()) { - if (desc.isPrivateIdent) { - ThrowSyntaxError("Decorators are not valid here"); - } - - property->AddDecorators(std::move(desc.decorators)); - } - } - - ASSERT(property != nullptr); if (Lexer()->GetToken().Type() != lexer::TokenType::PUNCTUATOR_SEMI_COLON && Lexer()->GetToken().Type() != lexer::TokenType::PUNCTUATOR_RIGHT_BRACE && ((Lexer()->GetToken().Flags() & lexer::TokenFlags::NEW_LINE) == 0) && @@ -1155,16 +1160,16 @@ ir::Expression *TypedParser::ParseQualifiedReference(ir::Expression *typeName, E } else if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_FORMAT) { propName = ParseIdentifierFormatPlaceholder(); } else if (Lexer()->GetToken().Type() != lexer::TokenType::LITERAL_IDENT) { - if ((flags & ExpressionParseFlags::POTENTIAL_CLASS_LITERAL) != 0) { - if (Lexer()->GetToken().Type() == lexer::TokenType::KEYW_CLASS) { - typeName->SetRange({startLoc, Lexer()->GetToken().End()}); - return typeName; - } - if (Lexer()->GetToken().Type() == lexer::TokenType::KEYW_THIS) { - return nullptr; - } + if ((flags & ExpressionParseFlags::POTENTIAL_CLASS_LITERAL) == 0) { + ThrowSyntaxError("Identifier expected"); + } + if (Lexer()->GetToken().Type() == lexer::TokenType::KEYW_CLASS) { + typeName->SetRange({startLoc, Lexer()->GetToken().End()}); + return typeName; + } + if (Lexer()->GetToken().Type() == lexer::TokenType::KEYW_THIS) { + return nullptr; } - ThrowSyntaxError("Identifier expected"); } else { propName = AllocNode(Lexer()->GetToken().Ident(), Allocator()); diff --git a/ets2panda/parser/TypedParser.h b/ets2panda/parser/TypedParser.h index fc04ce492f5562801b1d42faeab0df957580c7cf..9d9222bc604ae8343acb7040c526e315943aca88 100644 --- a/ets2panda/parser/TypedParser.h +++ b/ets2panda/parser/TypedParser.h @@ -60,6 +60,7 @@ protected: // NOLINTNEXTLINE(google-default-arguments) ir::ClassDefinition *ParseClassDefinition(ir::ClassDefinitionModifiers modifiers, ir::ModifierFlags flags = ir::ModifierFlags::NONE) override; + ir::AstNode *CheckDescription(ClassElementDescriptor *desc, const ArenaVector &properties); // NOLINTNEXTLINE(google-default-arguments) ir::AstNode *ParseClassElement(const ArenaVector &properties, ir::ClassDefinitionModifiers modifiers, ir::ModifierFlags flags = ir::ModifierFlags::NONE,