1/* 2 * Copyright (c) 2025 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 16import * as arkts from '@koalaui/libarkts'; 17import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; 18import { PresetDecorators } from '../utils'; 19 20function recordStructWithLinkDecorators(item: arkts.AstNode, structName: string, linkMap: Map<string, string>): void { 21 if (!arkts.isClassProperty(item)) { 22 return; 23 } 24 item.annotations?.forEach((annotation) => { 25 const annotationName: string = annotation.expr?.dumpSrc() ?? ''; 26 if (annotationName === '') { 27 return; 28 } 29 // If the node has properties decorated with Link or ObjectLink, record this structure node 30 if (annotationName === PresetDecorators.LINK || annotationName === PresetDecorators.OBJECT_LINK) { 31 linkMap.set(structName, annotationName); 32 } 33 }); 34} 35 36function initMap(node: arkts.AstNode, linkMap: Map<string, string>): void { 37 // Check if the current node is the root node 38 if (arkts.nodeType(node) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { 39 return; 40 } 41 node.getChildren().forEach((member) => { 42 if (!arkts.isStructDeclaration(member)) { 43 return; 44 } 45 const structName: string = member.definition.ident?.name ?? ''; 46 if (structName === '') { 47 return; 48 } 49 member.definition?.body?.forEach((item) => { 50 recordStructWithLinkDecorators(item, structName, linkMap); 51 }); 52 }); 53} 54 55function checkInitializeWithLiteral(node: arkts.AstNode, context: UISyntaxRuleContext, 56 linkMap: Map<string, string> 57): void { 58 if (!arkts.isCallExpression(node)) { 59 return; 60 } 61 const componentName = node.expression.dumpSrc(); 62 // Only assignments to properties decorated with Link or ObjectLink trigger rule checks 63 if (!linkMap.has(componentName)) { 64 return; 65 } 66 node.arguments.forEach((member) => { 67 member.getChildren().forEach((property) => { 68 if (!arkts.isProperty(property)) { 69 return; 70 } 71 if (property.value === undefined) { 72 return; 73 } 74 const propertyType: arkts.Es2pandaAstNodeType = arkts.nodeType(property.value); 75 const key: string = property.key?.dumpSrc() ?? ''; 76 if (key === '') { 77 return; 78 } 79 const value = property.value?.dumpSrc() ? property.value.dumpSrc() : ''; 80 // If the assignment statement is not of type MemberExpression, throw an error 81 if (propertyType !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_MEMBER_EXPRESSION) { 82 context.report({ 83 node: property, 84 message: rule.messages.cannotInitializeWithLiteral, 85 data: { 86 value: value, 87 annotationName: linkMap.get(componentName)!, 88 key: key, 89 }, 90 }); 91 } 92 }); 93 }); 94} 95 96const rule: UISyntaxRule = { 97 name: 'construct-parameter-literal', 98 messages: { 99 cannotInitializeWithLiteral: `Assigning the attribute'{{value}}' to the '@{{annotationName}}' decorated attribute '{{key}}' is not allowed.`, 100 }, 101 setup(context) { 102 let linkMap: Map<string, string> = new Map(); 103 return { 104 parsed: (node): void => { 105 initMap(node, linkMap); 106 checkInitializeWithLiteral(node, context, linkMap); 107 }, 108 }; 109 }, 110}; 111 112export default rule;