1/** 2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16#include "varbinder/variableFlags.h" 17#include "checker/checker.h" 18#include "checker/checkerContext.h" 19#include "checker/types/ets/etsObjectType.h" 20#include "checker/types/ets/etsTupleType.h" 21#include "ir/astNode.h" 22#include "lexer/token/tokenType.h" 23#include "ir/base/catchClause.h" 24#include "ir/expression.h" 25#include "ir/typeNode.h" 26#include "ir/base/scriptFunction.h" 27#include "ir/base/classProperty.h" 28#include "ir/base/methodDefinition.h" 29#include "ir/statements/variableDeclarator.h" 30#include "ir/statements/switchCaseStatement.h" 31#include "ir/expressions/identifier.h" 32#include "ir/expressions/arrayExpression.h" 33#include "ir/expressions/callExpression.h" 34#include "ir/expressions/memberExpression.h" 35#include "ir/expressions/binaryExpression.h" 36#include "ir/expressions/assignmentExpression.h" 37#include "ir/statements/labelledStatement.h" 38#include "ir/ets/etsFunctionType.h" 39#include "ir/ets/etsNewClassInstanceExpression.h" 40#include "ir/ts/tsTypeAliasDeclaration.h" 41#include "ir/ts/tsEnumMember.h" 42#include "ir/ts/tsTypeParameter.h" 43#include "ir/ets/etsTypeReference.h" 44#include "ir/ets/etsTypeReferencePart.h" 45#include "varbinder/variable.h" 46#include "varbinder/scope.h" 47#include "varbinder/declaration.h" 48#include "checker/ETSchecker.h" 49#include "checker/ets/typeRelationContext.h" 50#include "util/helpers.h" 51 52namespace ark::es2panda::checker { 53void ETSChecker::ValidatePropertyAccess(varbinder::Variable *var, ETSObjectType *obj, const lexer::SourcePosition &pos) 54{ 55 if ((Context().Status() & CheckerStatus::IGNORE_VISIBILITY) != 0U) { 56 return; 57 } 58 if (var->HasFlag(varbinder::VariableFlags::METHOD)) { 59 return; 60 } 61 62 if (var->HasFlag(varbinder::VariableFlags::PRIVATE) || var->HasFlag(varbinder::VariableFlags::PROTECTED)) { 63 if ((Context().ContainingClass() == obj || 64 Context().ContainingClass()->GetOriginalBaseType() == obj->GetOriginalBaseType()) && 65 obj->IsPropertyInherited(var)) { 66 return; 67 } 68 69 if (var->HasFlag(varbinder::VariableFlags::PROTECTED) && Context().ContainingClass()->IsDescendantOf(obj) && 70 obj->IsPropertyInherited(var)) { 71 return; 72 } 73 74 auto *currentOutermost = Context().ContainingClass()->OutermostClass(); 75 auto *objOutermost = obj->OutermostClass(); 76 77 if (currentOutermost != nullptr && objOutermost != nullptr && currentOutermost == objOutermost && 78 obj->IsPropertyInherited(var)) { 79 return; 80 } 81 82 LogTypeError({"Property ", var->Name(), " is not visible here."}, pos); 83 var->SetTsType(GlobalTypeError()); 84 } 85} 86 87void ETSChecker::ValidateCallExpressionIdentifier(ir::Identifier *const ident, varbinder::Variable *const resolved, 88 Type *const type) 89{ 90 if (resolved->HasFlag(varbinder::VariableFlags::CLASS_OR_INTERFACE) && 91 ident->Parent()->AsCallExpression()->Callee() != ident) { 92 LogTypeError({"Class or interface '", ident->Name(), "' cannot be used as object"}, ident->Start()); 93 ident->Variable()->SetTsType(GlobalTypeError()); 94 } 95 96 if (ident->Parent()->AsCallExpression()->Callee() != ident) { 97 return; 98 } 99 if (ident->Variable() != nullptr && // It should always be true! 100 ident->Variable()->Declaration()->Node() != nullptr && 101 ident->Variable()->Declaration()->Node()->IsImportNamespaceSpecifier()) { 102 LogTypeError({"Namespace style identifier ", ident->Name(), " is not callable."}, ident->Start()); 103 ident->Variable()->SetTsType(GlobalTypeError()); 104 } 105 if (type->IsETSFunctionType() || type->IsETSDynamicType() || 106 (type->IsETSObjectType() && type->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::FUNCTIONAL))) { 107 return; 108 } 109 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) 110 if (TryTransformingToStaticInvoke(ident, type)) { 111 return; 112 } 113 114 if (type->IsETSUnionType()) { 115 for (auto it : type->AsETSUnionType()->ConstituentTypes()) { 116 if (it->IsETSFunctionType() || it->IsETSDynamicType() || 117 (it->IsETSObjectType() && it->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::FUNCTIONAL))) { 118 return; 119 } 120 } 121 } 122 123 LogTypeError({"This expression is not callable."}, ident->Start()); 124 ident->Variable()->SetTsType(GlobalTypeError()); 125} 126 127void ETSChecker::ValidateNewClassInstanceIdentifier(ir::Identifier *const ident, varbinder::Variable *const resolved) 128{ 129 if (ident->Parent()->AsETSNewClassInstanceExpression()->GetTypeRef() == ident && (resolved != nullptr) && 130 !resolved->HasFlag(varbinder::VariableFlags::CLASS_OR_INTERFACE)) { 131 LogUnResolvedError(ident); 132 return; 133 } 134} 135 136void ETSChecker::ValidateMemberIdentifier(ir::Identifier *const ident, varbinder::Variable *const resolved, 137 Type *const type) 138{ 139 if (resolved->Declaration()->Node()->IsTSEnumDeclaration() && 140 ident->Parent()->AsMemberExpression()->HasMemberKind(ir::MemberExpressionKind::ELEMENT_ACCESS)) { 141 return; 142 } 143 if (ident->Parent()->AsMemberExpression()->IsComputed()) { 144 if ((resolved != nullptr) && !resolved->Declaration()->PossibleTDZ()) { 145 WrongContextErrorClassifyByType(ident, resolved); 146 } 147 148 return; 149 } 150 151 if (!IsReferenceType(type) && !type->IsETSEnumType() && !type->HasTypeFlag(TypeFlag::ETS_PRIMITIVE)) { 152 LogUnResolvedError(ident); 153 return; 154 } 155} 156 157void ETSChecker::ValidatePropertyOrDeclaratorIdentifier(ir::Identifier *const ident, 158 varbinder::Variable *const resolved) 159{ 160 const auto [target_ident, typeAnnotation] = GetTargetIdentifierAndType(ident); 161 162 if ((resolved != nullptr) && resolved->TsType()->IsETSFunctionType()) { 163 CheckEtsFunctionType(ident, target_ident); 164 return; 165 } 166 167 if ((resolved != nullptr) && !resolved->Declaration()->PossibleTDZ()) { 168 LogUnResolvedError(ident); 169 return; 170 } 171} 172 173void ETSChecker::ValidateAssignmentIdentifier(ir::Identifier *const ident, varbinder::Variable *const resolved, 174 Type *const type) 175{ 176 const auto *const assignmentExpr = ident->Parent()->AsAssignmentExpression(); 177 if (assignmentExpr->Left() == ident && (resolved != nullptr) && !resolved->Declaration()->PossibleTDZ()) { 178 WrongContextErrorClassifyByType(ident, resolved); 179 return; 180 } 181 182 if (assignmentExpr->Right() == ident && (resolved != nullptr) && 183 (!resolved->Declaration()->PossibleTDZ() && !type->IsETSFunctionType())) { 184 WrongContextErrorClassifyByType(ident, resolved); 185 return; 186 } 187} 188 189bool ETSChecker::ValidateBinaryExpressionIdentifier(ir::Identifier *const ident, Type *const type) 190{ 191 const auto *const binaryExpr = ident->Parent()->AsBinaryExpression(); 192 bool isFinished = false; 193 if (binaryExpr->OperatorType() == lexer::TokenType::KEYW_INSTANCEOF && binaryExpr->Right() == ident) { 194 if (!IsReferenceType(type)) { 195 LogTypeError({R"(Using the "instance of" operator with non-object type ")", ident->Name(), "\""}, 196 ident->Start()); 197 ident->Variable()->SetTsType(GlobalTypeError()); 198 } 199 isFinished = true; 200 } 201 return isFinished; 202} 203 204void ETSChecker::ValidateResolvedIdentifier(ir::Identifier *const ident, varbinder::Variable *const resolved) 205{ 206 auto *smartType = Context().GetSmartCast(resolved); 207 auto *const resolvedType = GetApparentType(smartType != nullptr ? smartType : GetTypeOfVariable(resolved)); 208 209 switch (ident->Parent()->Type()) { 210 case ir::AstNodeType::CALL_EXPRESSION: { 211 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) 212 ValidateCallExpressionIdentifier(ident, resolved, resolvedType); 213 break; 214 } 215 case ir::AstNodeType::ETS_NEW_CLASS_INSTANCE_EXPRESSION: { 216 ValidateNewClassInstanceIdentifier(ident, resolved); 217 break; 218 } 219 case ir::AstNodeType::MEMBER_EXPRESSION: { 220 ValidateMemberIdentifier(ident, resolved, resolvedType); 221 break; 222 } 223 case ir::AstNodeType::BINARY_EXPRESSION: { 224 if (ValidateBinaryExpressionIdentifier(ident, resolvedType)) { 225 return; 226 } 227 228 [[fallthrough]]; 229 } 230 case ir::AstNodeType::UPDATE_EXPRESSION: 231 case ir::AstNodeType::UNARY_EXPRESSION: { 232 if (resolved != nullptr && !resolved->Declaration()->PossibleTDZ()) { 233 WrongContextErrorClassifyByType(ident, resolved); 234 } 235 break; 236 } 237 case ir::AstNodeType::CLASS_PROPERTY: 238 case ir::AstNodeType::VARIABLE_DECLARATOR: { 239 ValidatePropertyOrDeclaratorIdentifier(ident, resolved); 240 break; 241 } 242 case ir::AstNodeType::ASSIGNMENT_EXPRESSION: { 243 ValidateAssignmentIdentifier(ident, resolved, resolvedType); 244 break; 245 } 246 default: { 247 if (resolved != nullptr && !resolved->Declaration()->PossibleTDZ() && !resolvedType->IsETSFunctionType()) { 248 WrongContextErrorClassifyByType(ident, resolved); 249 } 250 break; 251 } 252 } 253} 254 255void ETSChecker::ValidateUnaryOperatorOperand(varbinder::Variable *variable) 256{ 257 if (variable == nullptr || IsVariableGetterSetter(variable) || variable->Declaration() == nullptr) { 258 return; 259 } 260 261 if (variable->Declaration()->IsConstDecl() || variable->Declaration()->IsReadonlyDecl()) { 262 std::string_view fieldType = variable->Declaration()->IsConstDecl() ? "constant" : "readonly"; 263 if (HasStatus(CheckerStatus::IN_CONSTRUCTOR | CheckerStatus::IN_STATIC_BLOCK) && 264 !variable->HasFlag(varbinder::VariableFlags::EXPLICIT_INIT_REQUIRED)) { 265 LogTypeError({"Cannot reassign ", fieldType, " ", variable->Name()}, 266 variable->Declaration()->Node()->Start()); 267 variable->SetTsType(GlobalTypeError()); 268 return; 269 } 270 if (!HasStatus(CheckerStatus::IN_CONSTRUCTOR | CheckerStatus::IN_STATIC_BLOCK)) { 271 LogTypeError({"Cannot assign to a ", fieldType, " variable ", variable->Name()}, 272 variable->Declaration()->Node()->Start()); 273 variable->SetTsType(GlobalTypeError()); 274 } 275 } 276} 277 278void ETSChecker::ValidateGenericTypeAliasForClonedNode(ir::TSTypeAliasDeclaration *const typeAliasNode, 279 const ir::TSTypeParameterInstantiation *const exactTypeParams) 280{ 281 static std::string_view const TRANSFORMATION_NAME = __func__; 282 283 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) 284 auto *const clonedNode = typeAliasNode->TypeAnnotation()->Clone(Allocator(), typeAliasNode); 285 286 // Basic check, we really don't want to change the original type nodes, more precise checking should be made 287 ASSERT(clonedNode != typeAliasNode->TypeAnnotation()); 288 289 // Currently only reference types are checked. This should be extended for other types in a follow up patch, but for 290 // complete usability, if the type isn't a simple reference type, then doN't check type alias declaration at all. 291 bool checkTypealias = true; 292 293 // Only transforming a temporary cloned node, so no modification is made in the AST 294 clonedNode->TransformChildrenRecursively( 295 [this, &checkTypealias, &exactTypeParams, typeAliasNode](ir::AstNode *const node) -> ir::AstNode * { 296 if (!node->IsETSTypeReference()) { 297 return node; 298 } 299 300 const auto *const nodeIdent = node->AsETSTypeReference()->Part()->Name()->AsIdentifier(); 301 302 size_t typeParamIdx = 0; 303 for (const auto *const typeParam : typeAliasNode->TypeParams()->Params()) { 304 if (typeParam->Name()->AsIdentifier()->Variable() == nodeIdent->Variable()) { 305 break; 306 } 307 typeParamIdx++; 308 } 309 310 if (typeParamIdx == typeAliasNode->TypeParams()->Params().size()) { 311 return node; 312 } 313 314 auto *const typeParamType = exactTypeParams->Params().at(typeParamIdx); 315 316 if (!typeParamType->IsETSTypeReference()) { 317 checkTypealias = false; 318 return node; 319 } 320 321 return typeParamType->Clone(Allocator(), nullptr); 322 }, 323 TRANSFORMATION_NAME); 324 325 if (checkTypealias) { 326 clonedNode->Check(this); 327 } 328} 329 330bool ETSChecker::ValidateTupleMinElementSize(ir::ArrayExpression *const arrayExpr, ETSTupleType *tuple) 331{ 332 if (arrayExpr->Elements().size() < static_cast<size_t>(tuple->GetMinTupleSize())) { 333 LogTypeError({"Few elements in array initializer for tuple with size of ", 334 static_cast<size_t>(tuple->GetMinTupleSize())}, 335 arrayExpr->Start()); 336 return false; 337 } 338 return true; 339} 340} // namespace ark::es2panda::checker 341