diff --git a/arkui-plugins/ui-syntax-plugins/rules/componentV2-state-usage-validation.ts b/arkui-plugins/ui-syntax-plugins/rules/componentV2-state-usage-validation.ts index b60587aeece7799fc34f3f48b37c5dc0aad66a0c..36abf5792dc7631f590377f52cc672b18d9a6878 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/componentV2-state-usage-validation.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/componentV2-state-usage-validation.ts @@ -14,9 +14,12 @@ */ import * as arkts from '@koalaui/libarkts'; -import { getClassPropertyAnnotationNames, PresetDecorators, getAnnotationUsage } from '../utils'; +import { + getClassPropertyAnnotationNames, PresetDecorators, getAnnotationUsage, getClassPropertyName, getIdentifierName +} from '../utils'; import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; +const builtInDecorators = [PresetDecorators.LOCAL, PresetDecorators.PARAM, PresetDecorators.EVENT]; // Helper functions for rules const hasisComponentV2 = (node: arkts.StructDeclaration): boolean => !!getAnnotationUsage(node, PresetDecorators.COMPONENT_V2); @@ -25,12 +28,13 @@ const hasComponent = (node: arkts.StructDeclaration): boolean => !!getAnnotation PresetDecorators.COMPONENT_V1); function checkMultipleBuiltInDecorators(context: UISyntaxRuleContext, member: arkts.ClassProperty, propertyDecorators: string[]): void { - const builtInDecorators = [PresetDecorators.LOCAL, PresetDecorators.PARAM, PresetDecorators.EVENT]; const appliedBuiltInDecorators = propertyDecorators.filter(d => builtInDecorators.includes(d)); if (appliedBuiltInDecorators.length > 1) { member.annotations?.forEach(annotation => { - const annotationsName = annotation.expr?.dumpSrc(); - reportMultipleBuiltInDecoratorsError(context, annotation, annotationsName, builtInDecorators); + if (annotation.expr && arkts.isIdentifier(annotation.expr)) { + const annotationsName = annotation.expr.name; + reportMultipleBuiltInDecoratorsError(context, annotation, annotationsName, builtInDecorators); + } }); } }; @@ -42,8 +46,8 @@ function reportMultipleBuiltInDecoratorsError(context: UISyntaxRuleContext, anno node: annotation, message: rule.messages.multipleBuiltInDecorators, fix: (annotation) => { - const startPosition = arkts.getStartPosition(annotation); - const endPosition = arkts.getEndPosition(annotation); + const startPosition = annotation.startPosition; + const endPosition = annotation.endPosition; return { range: [startPosition, endPosition], code: '', @@ -55,11 +59,12 @@ function reportMultipleBuiltInDecoratorsError(context: UISyntaxRuleContext, anno function checkDecoratorOnlyInisComponentV2(context: UISyntaxRuleContext, member: arkts.ClassProperty, node: arkts.StructDeclaration, hasisComponentV2: boolean, hasComponent: boolean): void { - const builtInDecorators = [PresetDecorators.LOCAL, PresetDecorators.PARAM, PresetDecorators.EVENT]; member.annotations?.forEach(annotation => { - const annotationsName = annotation.expr?.dumpSrc(); - if (annotationsName && builtInDecorators.includes(annotationsName) && !hasisComponentV2 && !hasComponent) { - reportDecoratorOnlyInisComponentV2Error(context, annotation, annotationsName, node); + if (annotation.expr && arkts.isIdentifier(annotation.expr)) { + const annotationsName = annotation.expr?.name; + if (annotationsName && builtInDecorators.includes(annotationsName) && !hasisComponentV2 && !hasComponent) { + reportDecoratorOnlyInisComponentV2Error(context, annotation, annotationsName, node); + } } }); }; @@ -71,7 +76,7 @@ function reportDecoratorOnlyInisComponentV2Error(context: UISyntaxRuleContext, a message: rule.messages.decoratorOnlyInisComponentV2, data: { annotationsName }, fix: (annotation) => { - const startPosition = arkts.getStartPosition(node); + const startPosition = node.definition.startPosition; return { range: [startPosition, startPosition], code: `@${PresetDecorators.COMPONENT_V2}\n`, @@ -89,7 +94,7 @@ function checkParamRequiresRequire(context: UISyntaxRuleContext, member: arkts.C node: memberKey, message: rule.messages.paramRequiresRequire, fix: (memberKey) => { - const startPosition = arkts.getStartPosition(memberKey); + const startPosition = memberKey.startPosition; return { range: [startPosition, startPosition], code: `@${PresetDecorators.REQUIRE} `, @@ -100,17 +105,17 @@ function checkParamRequiresRequire(context: UISyntaxRuleContext, member: arkts.C }; function checkRequireOnlyWithParam(context: UISyntaxRuleContext, member: arkts.ClassProperty, - propertyDecorators: string[]): void { + propertyDecorators: string[], isComponentV2: boolean): void { const requireDecorator = member.annotations?.find(annotation => - annotation.expr && annotation.expr.dumpSrc() === PresetDecorators.REQUIRE + annotation.expr && arkts.isIdentifier(annotation.expr) && annotation.expr.name === PresetDecorators.REQUIRE ); - if (requireDecorator && !propertyDecorators.includes(PresetDecorators.PARAM)) { + if (isComponentV2 && requireDecorator && !propertyDecorators.includes(PresetDecorators.PARAM)) { context.report({ node: requireDecorator, message: rule.messages.requireOnlyWithParam, fix: (requireDecorator) => { - const startPosition = arkts.getStartPosition(requireDecorator); - const endPosition = arkts.getEndPosition(requireDecorator); + const startPosition = requireDecorator.startPosition; + const endPosition = requireDecorator.endPosition; return { range: [startPosition, endPosition], code: '', @@ -120,6 +125,138 @@ function checkRequireOnlyWithParam(context: UISyntaxRuleContext, member: arkts.C } }; +function checkuseStateDecoratorsWithProperty(context: UISyntaxRuleContext, method: arkts.MethodDefinition): void { + method.scriptFunction.annotations?.forEach(annotation => { + if (annotation.expr && arkts.isIdentifier(annotation.expr) && builtInDecorators.includes(annotation.expr.name)) { + const annotationName = annotation.expr.name; + reportInvalidDecoratorOnMethod(context, annotation, annotationName); + } + }); +} + +function reportInvalidDecoratorOnMethod(context: UISyntaxRuleContext, annotation: arkts.AnnotationUsage, + annotationName: string): void { + context.report({ + node: annotation, + message: rule.messages.useStateDecoratorsWithProperty, + data: { annotationName }, + fix: (annotation) => { + const startPosition = annotation.startPosition; + const endPosition = annotation.endPosition; + return { + range: [startPosition, endPosition], + code: '', + }; + }, + }); +} + +function checkMustInitialize( + node: arkts.AstNode, + context: UISyntaxRuleContext, + mustInitMap: Map> +): void { + if (!arkts.isIdentifier(node)) { + return; + } + const structName: string = getIdentifierName(node); + if (!mustInitMap.has(structName)) { + return; + } + const parentNode: arkts.AstNode = node.parent; + if (!arkts.isCallExpression(parentNode)) { + return; + } + // Get all the properties of a record via StructName + const mustInitName: Map = mustInitMap.get(structName)!; + parentNode.arguments?.forEach((member) => { + const childkeyNameArray: string[] = getChildKeyNameArray(member); + mustInitName.forEach((value, key) => { + // If an attribute that must be initialized is not initialized, an error is reported + if (!childkeyNameArray.includes(key)) { + context.report({ + node: parentNode, + message: rule.messages.paramNeedInit, + data: { + name: key, + }, + }); + } + }); + }); +} + +function getChildKeyNameArray(member: arkts.AstNode): string[] { + const childkeyNameArray: string[] = []; + member.getChildren().forEach((property) => { + if (!arkts.isProperty(property) || !property.key || !arkts.isIdentifier(property.key)) { + return; + } + const childkeyName = property.key.name; + if (childkeyName !== '') { + childkeyNameArray.push(childkeyName); + } + }); + return childkeyNameArray; +} + +function initMap(node: arkts.AstNode, mustInitMap: Map>, mustInitArray: string[][], +): void { + if (arkts.nodeType(node) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + return; + } + node.getChildren().forEach((member) => { + if (!(arkts.isStructDeclaration(member) || + (arkts.isClassDeclaration(member) && !!member.definition && + arkts.classDefinitionIsFromStructConst(member.definition)) + )) { + return; + } + const structName: string = member.definition!.ident?.name ?? ''; + if (structName === '') { + return; + } + member.definition?.body.forEach((item) => { + checkPropertyByAnnotations(item, structName, mustInitMap, mustInitArray); + }); + }); +} + +function checkPropertyByAnnotations( + item: arkts.AstNode, + structName: string, + mustInitMap: Map>, + mustInitArray: string[][] +): void { + if (!arkts.isClassProperty(item) || !item.key || !arkts.isIdentifier(item.key)) { + return; + } + const propertyName: string = item.key.name; + if (item.annotations.length === 0 || propertyName === '') { + return; + } + const annotationArray: string[] = getClassPropertyAnnotationNames(item); + // If the member variable is decorated, it is added to the corresponding map + mustInitArray.forEach(arr => { + if (arr.every(annotation => annotationArray.includes(annotation))) { + const annotationName: string = arr[0]; + addProperty(mustInitMap, structName, propertyName, annotationName); + } + }); +} + +// Define a function to add property data to the property map +function addProperty(propertyMap: Map>, structName: string, + propertyName: string, annotationName: string): void { + if (!propertyMap.has(structName)) { + propertyMap.set(structName, new Map()); + } + const structProperties = propertyMap.get(structName); + if (structProperties) { + structProperties.set(propertyName, annotationName); + } +} + function validateClassPropertyDecorators(context: UISyntaxRuleContext, node: arkts.StructDeclaration): void { const isComponentV2 = hasisComponentV2(node); const isComponent = hasComponent(node); @@ -128,7 +265,6 @@ function validateClassPropertyDecorators(context: UISyntaxRuleContext, node: ark return; } const propertyDecorators = getClassPropertyAnnotationNames(member); - // Rule 1: Multiple built-in decorators checkMultipleBuiltInDecorators(context, member, propertyDecorators); @@ -139,23 +275,204 @@ function validateClassPropertyDecorators(context: UISyntaxRuleContext, node: ark checkParamRequiresRequire(context, member, propertyDecorators); // Rule 4: @Require must be used together with @Param - checkRequireOnlyWithParam(context, member, propertyDecorators); + checkRequireOnlyWithParam(context, member, propertyDecorators, isComponentV2); + }); +} + +function checkDecorator(annoArray: readonly arkts.AnnotationUsage[], decorator: string): boolean { + let flag = false; + annoArray.forEach((anno) => { + if (!anno.expr) { + return; + } + const annoName = getIdentifierName(anno.expr); + if (annoName === decorator) { + flag = true; + } }); + return flag; +} + +// Define a function to add property data to the property map +function addPropertyMap( + structName: string, + propertyName: string, + annotationName: string, + propertyMap: Map> +): void { + if (!propertyMap.has(structName)) { + propertyMap.set(structName, new Map()); + } + const structProperties = propertyMap.get(structName); + if (structProperties) { + structProperties.set(propertyName, annotationName); + } +} + +// Iterate through the incoming componentv2 node to see if there are any state variables for decorator decorations +function initComponentV2PropertyMap( + node: arkts.AstNode, + componentV2PropertyMap: Map> +): void { + if (arkts.nodeType(node) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + return; + } + node.getChildren().forEach((member) => { + if (!arkts.isStructDeclaration(member) || !member.definition.ident || + !checkDecorator(member?.definition.annotations, PresetDecorators.COMPONENT_V2)) { + return; + } + let structName: string = member.definition.ident?.name ?? ''; + member.definition?.body?.forEach((item) => { + processClassPropertyAnnotations(item, structName, componentV2PropertyMap); + }); + }); +} + +function processClassPropertyAnnotations( + item: arkts.AstNode, + structName: string, + componentV2PropertyMap: Map> +): void { + if (!arkts.isClassProperty(item) || !item.key) { + return; + } + let propertyName: string = getIdentifierName(item.key); + // If there is no decorator, it is a regular type + if (item.annotations.length === 0) { + let annotationName: string = PresetDecorators.REGULAR; + addPropertyMap(structName, propertyName, annotationName, componentV2PropertyMap); + } + item.annotations.forEach((annotation) => { + if (!annotation.expr) { + return; + } + let annotationName: string = getIdentifierName(annotation.expr); + if (annotationName === PresetDecorators.LOCAL) { + addPropertyMap(structName, propertyName, annotationName, componentV2PropertyMap); + } + }); +} + + +function checkInitializeRule( + node: arkts.AstNode, + context: UISyntaxRuleContext, + componentV2PropertyMap: Map>, +): void { + if (!arkts.isIdentifier(node) || !componentV2PropertyMap.has(getIdentifierName(node))) { + return; + } + let structName: string = getIdentifierName(node); + let parentNode: arkts.AstNode = node.parent; + if (!arkts.isCallExpression(parentNode)) { + return; + } + let structNode = node.parent; + while (!arkts.isStructDeclaration(structNode)) { + if (!structNode.parent) { + return; + } + structNode = structNode.parent; + } + let parentPropertyMap: Map = new Map(); + structNode.definition.body.forEach((property) => { + if (!arkts.isClassProperty(property)) { + return; + } + let propertyArray: string[] = []; + property.annotations.forEach((annotation) => { + if (!annotation.expr || !arkts.isIdentifier(annotation.expr)) { + return; + } + propertyArray.push(annotation.expr.name); + }); + parentPropertyMap.set(getClassPropertyName(property), propertyArray); + }); + // Gets all the properties recorded by the struct + const childPropertyName: Map = componentV2PropertyMap.get(structName)!; + parentNode.arguments.forEach((argument) => { + if (!arkts.isObjectExpression(argument)) { + return; + } + argument.getChildren().forEach((property) => { + if (!arkts.isProperty(property) || !property.key) { + return; + } + const childkeyName = getIdentifierName(property.key); + if (!childPropertyName.has(childkeyName)) { + return; + } + reportLocalNeedInit(childPropertyName, childkeyName, context, property, structName); + }); + }); +} + +function reportLocalNeedInit(childPropertyName: Map, childkeyName: string, context: UISyntaxRuleContext, + property: arkts.Property, structName: string): void { + if (childPropertyName.get(childkeyName) === PresetDecorators.LOCAL) { + context.report({ + node: property, + message: rule.messages.localNeedNoInit, + data: { + decoratorName: '@' + childPropertyName.get(childkeyName)!, + key: childkeyName, + componentName: structName, + }, + fix: (property) => { + return { + range: [property.startPosition, property.endPosition], + code: '', + }; + } + }); + } + if (childPropertyName.get(childkeyName) === PresetDecorators.REGULAR) { + context.report({ + node: property, + message: rule.messages.localNeedNoInit, + data: { + decoratorName: PresetDecorators.REGULAR, + key: childkeyName, + componentName: structName, + }, + fix: (property) => { + return { + range: [property.startPosition, property.endPosition], + code: '', + }; + } + }); + } } const rule: UISyntaxRule = { - name: 'iscomponentV2-state-usage-validation', + name: 'componentV2-state-usage-validation', messages: { multipleBuiltInDecorators: `The member property or method cannot be decorated by multiple built-in decorators.`, - decoratorOnlyInisComponentV2: `The '@{{annotationsName}}' decorator can only be used in a 'struct' decorated with '@isComponentV2'.`, - paramRequiresRequire: `When a variable decorated with @Param is not assigned a default value, it must also be decorated with @Require.`, - requireOnlyWithParam: `In a struct decorated with @isComponentV2, @Require can only be used with @Param. ` + decoratorOnlyInisComponentV2: `The '@{{annotationsName}}' decorator can only be used in a 'struct' decorated with '@ComponentV2'.`, + paramRequiresRequire: `When a variable decorated with '@Param' is not assigned a default value, it must also be decorated with '@Require'.`, + requireOnlyWithParam: `In a struct decorated with '@ComponentV2', '@Require' can only be used with @Param.`, + localNeedNoInit: `The '{{decoratorName}}' property '{{key}}' in the custom component '{{componentName}}' cannot be initialized here (forbidden to specify).`, + paramNeedInit: `Property '{{name}}' must be initialized through the component constructor.`, + useStateDecoratorsWithProperty: `'@{{annotationName}}' can only decorate member property.`, }, setup(context) { + let mustInitMap: Map> = new Map(); + const mustInitArray: string[][] = [[PresetDecorators.REQUIRE, PresetDecorators.PARAM]]; + let componentV2PropertyMap: Map> = new Map(); return { parsed: (node): void => { - + initComponentV2PropertyMap(node, componentV2PropertyMap); + checkInitializeRule(node, context, componentV2PropertyMap); + initMap(node, mustInitMap, mustInitArray); + // Rule 6: Property with require and Param must be initialized through the component constructor + checkMustInitialize(node, context, mustInitMap); + if (arkts.isMethodDefinition(node)) { + // Rule 7: Local, Param, Event decorators must be used with Property + checkuseStateDecoratorsWithProperty(context, node); + } if (!arkts.isStructDeclaration(node)) { return; } diff --git a/arkui-plugins/ui-syntax-plugins/rules/decorators-in-ui-component-only.ts b/arkui-plugins/ui-syntax-plugins/rules/decorators-in-ui-component-only.ts index b01e0d40a1f5677f0ffb5a5e25cbe87dc665aa57..fe7fb6ae0dadda285919f5c69680773fc7c8faf2 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/decorators-in-ui-component-only.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/decorators-in-ui-component-only.ts @@ -17,56 +17,84 @@ import * as arkts from '@koalaui/libarkts'; import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; import { PresetDecorators } from '../utils/index'; -// Helper function to find the '@Component' decorator in a ClassDeclaration and report errors. -function findComponentDecorator(context: UISyntaxRuleContext, node: arkts.ClassDeclaration): void { - const componentDecorator = node.definition?.annotations?.find( - (annotation) => - annotation.expr && - arkts.isIdentifier(annotation.expr) && - annotation.expr.name === PresetDecorators.COMPONENT_V1 - ); - if (componentDecorator) { - reportDecoratorError(context, componentDecorator, rule.messages.invalidComponentDecorator); - } +// Constants for allowed decorators on struct and within struct using PresetDecorators +const DECORATORS_ALLOWED_ON_STRUCT = [ + PresetDecorators.COMPONENT_V1, + PresetDecorators.ENTRY, + PresetDecorators.PREVIEW, + PresetDecorators.CUSTOM_DIALOG, + PresetDecorators.REUSABLE_V2, +]; + +const DECORATORS_ALLOWED_IN_STRUCT = [ + PresetDecorators.STATE, + PresetDecorators.PROP, + PresetDecorators.LINK, + PresetDecorators.PROVIDE, + PresetDecorators.CONSUME, + PresetDecorators.OBJECT_LINK, + PresetDecorators.STORAGE_LINK, + PresetDecorators.STORAGE_PROP, + PresetDecorators.LOCAL_STORAGE_LINK, + PresetDecorators.LOCAL_STORAGE_PROP, + PresetDecorators.WATCH, + PresetDecorators.BUILDER_PARAM, +]; + +// Helper function to find the decorator in a ClassDeclaration and report errors. +function findInvalidDecorator(context: UISyntaxRuleContext, node: arkts.ClassDeclaration): void { + node.definition!.annotations?.forEach(annotation => { + if (annotation.expr && arkts.isIdentifier(annotation.expr) && + DECORATORS_ALLOWED_ON_STRUCT.includes(annotation.expr.name)) { + reportDecoratorError(context, annotation, rule.messages.invalidDecoratorOnStruct); + } + }); } -// Helper function to find the '@Prop' decorator in a MethodDefinition or ClassProperty. -const findPropDecorator = (node: arkts.MethodDefinition | arkts.ClassProperty): arkts.AnnotationUsage | undefined => { - const annotations = 'scriptFunction' in node ? node.scriptFunction.annotations : node.annotations; - return annotations?.find( - (annotation) => - annotation.expr && annotation.expr.dumpSrc() === PresetDecorators.PROP - ); +// Rule 2: Check for 'decorator' on MethodDefinition +function checkinvalidDecoratorInStruct(context: UISyntaxRuleContext, node: arkts.MethodDefinition): void { + node.scriptFunction.annotations?.forEach(annotation => { + if (annotation.expr && arkts.isIdentifier(annotation.expr) && + DECORATORS_ALLOWED_IN_STRUCT.includes(annotation.expr.name)) { + reportDecoratorError(context, annotation, rule.messages.invalidDecoratorInStruct); + } + }); }; -// Rule 2: Check for '@Prop' on MethodDefinition -function checkPropOnMethod(context: UISyntaxRuleContext, node: arkts.MethodDefinition): void { - const propDecorator = findPropDecorator(node); - if (propDecorator) { - reportDecoratorError(context, propDecorator, rule.messages.propOnMethod); - } +// Rule 3: Check for 'decorator' on ClassProperty within a ClassDeclaration +function checkDecoratorOnClassProperty(context: UISyntaxRuleContext, node: arkts.ClassProperty, + currentNode: arkts.AstNode): void { + node.annotations?.forEach(annotation => { + if (annotation.expr && arkts.isIdentifier(annotation.expr) && + DECORATORS_ALLOWED_IN_STRUCT.includes(annotation.expr.name)) { + reportIfDecoratorInClassDeclaration(context, annotation, currentNode); + } + }); }; -// Rule 3: Check for '@Prop' on ClassProperty within a ClassDeclaration -function checkPropOnClassProperty(context: UISyntaxRuleContext, node: arkts.ClassProperty, currentNode: arkts.AstNode) - : void { - const propDecorator = findPropDecorator(node); +function reportIfDecoratorInClassDeclaration(context: UISyntaxRuleContext, annotation: arkts.AnnotationUsage, + currentNode: arkts.AstNode): void { while (arkts.nodeType(currentNode) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { currentNode = currentNode.parent; - if (propDecorator && arkts.isClassDeclaration(currentNode)) { - reportDecoratorError(context, propDecorator, rule.messages.propOnMethod); + if (annotation && arkts.isClassDeclaration(currentNode)) { + reportDecoratorError(context, annotation, rule.messages.invalidDecoratorInStruct); } } -}; +} -function reportDecoratorError(context: UISyntaxRuleContext, Decorator: arkts.AnnotationUsage, message: string +function reportDecoratorError(context: UISyntaxRuleContext, decorator: arkts.AnnotationUsage, message: string ): void { + if (!decorator.expr || !arkts.isIdentifier(decorator.expr)) { + return; + } + const decoratorName = decorator.expr.name; context.report({ - node: Decorator, + node: decorator, message: message, + data: { decoratorName }, fix: () => { - const startPosition = arkts.getStartPosition(Decorator); - const endPosition = arkts.getEndPosition(Decorator); + const startPosition = decorator.startPosition; + const endPosition = decorator.endPosition; return { range: [startPosition, endPosition], code: '', @@ -75,24 +103,40 @@ function reportDecoratorError(context: UISyntaxRuleContext, Decorator: arkts.Ann }); } +function checkDecoratorWithFunctionDeclaration(node: arkts.FunctionDeclaration, context: UISyntaxRuleContext): void { + node.annotations?.forEach(annotation => { + if (annotation.expr && arkts.isIdentifier(annotation.expr) && + DECORATORS_ALLOWED_ON_STRUCT.includes(annotation.expr.name)) { + reportDecoratorError(context, annotation, rule.messages.invalidDecoratorOnStruct); + } + if (annotation.expr && arkts.isIdentifier(annotation.expr) && + DECORATORS_ALLOWED_IN_STRUCT.includes(annotation.expr.name)) { + reportDecoratorError(context, annotation, rule.messages.invalidDecoratorInStruct); + } + }); +} + const rule: UISyntaxRule = { - name: 'no-prop-on-method', + name: 'decorators-in-ui-component-only', messages: { - invalidComponentDecorator: `'@Component' can decorate only custom components.`, - propOnMethod: `'@Prop' can decorate only member variables of custom components.`, + invalidDecoratorOnStruct: `The '@{{decoratorName}}' decorator can only be used with 'struct'.`, + invalidDecoratorInStruct: `'@{{decoratorName}}' can not decorate the method.`, }, setup(context) { return { parsed: (node: arkts.AstNode): void => { + if (arkts.isFunctionDeclaration(node)) { + checkDecoratorWithFunctionDeclaration(node, context); + } if (arkts.isClassDeclaration(node)) { - findComponentDecorator(context, node); + findInvalidDecorator(context, node); } if (arkts.isMethodDefinition(node)) { - checkPropOnMethod(context, node); + checkinvalidDecoratorInStruct(context, node); } let currentNode = node; if (arkts.isClassProperty(node)) { - checkPropOnClassProperty(context, node, currentNode); + checkDecoratorOnClassProperty(context, node, currentNode); } }, }; diff --git a/arkui-plugins/ui-syntax-plugins/rules/no-child-in-button.ts b/arkui-plugins/ui-syntax-plugins/rules/no-child-in-button.ts index 6cf999a272d61eb0c3610d9d70ff3b55a6551a46..e6a93e3d7005be56b0d21b509ecd152c77b70a2b 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/no-child-in-button.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/no-child-in-button.ts @@ -43,8 +43,8 @@ function reportNoChildInButtonError(parentNode: arkts.AstNode, context: UISyntax node: parentNode, message: rule.messages.noChildInButton, fix: (parentNode) => { - const startPosition = arkts.getStartPosition(siblings[2]); - const endPosition = arkts.getEndPosition(siblings[2]); + const startPosition = siblings[2].startPosition; + const endPosition = siblings[2].endPosition; return { range: [startPosition, endPosition], code: '', diff --git a/arkui-plugins/ui-syntax-plugins/rules/no-same-as-built-in-attribute.ts b/arkui-plugins/ui-syntax-plugins/rules/no-same-as-built-in-attribute.ts index e61af0b5682a58405b0de144d1331314fd665d4d..7afe1f354604a63edda8d668cf38a63c1192f73d 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/no-same-as-built-in-attribute.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/no-same-as-built-in-attribute.ts @@ -15,28 +15,34 @@ import * as arkts from '@koalaui/libarkts'; import { UISyntaxRule } from './ui-syntax-rule'; +import { isBuiltInAttribute } from '../utils'; const rule: UISyntaxRule = { name: 'no-same-as-built-in-attribute', messages: { - duplicateName: `The struct name '{{structName}}' should not have the same name as a built-in attribute.`, + duplicateName: `The struct '{{structName}}' cannot have the same name as the built-in attribute '{{builtInName}}'.`, }, setup(context) { - const builtInAttributes = ['fontColor', 'width', 'height', 'size', 'border', 'backgroundColor', 'margin', - 'padding']; return { parsed: (node): void => { if (!arkts.isStructDeclaration(node)) { return; } - const structName = node.definition.ident?.name ?? ' '; + if (!node.definition) { + return; + } + if (!arkts.isClassDefinition(node.definition)) { + return; + } const structIdent = node.definition.ident; + const structName = node.definition.ident?.name ?? ' '; // If the struct name matches any built-in attribute, report an error - if (builtInAttributes.includes(structName) && structIdent) { + if (structIdent && isBuiltInAttribute(context, structName)) { + const builtInName = structName; context.report({ node: structIdent, message: rule.messages.duplicateName, - data: { structName } + data: { structName, builtInName } }); } }, @@ -44,4 +50,4 @@ const rule: UISyntaxRule = { }, }; -export default rule; +export default rule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/observed-heritage-compatible-check.ts b/arkui-plugins/ui-syntax-plugins/rules/observed-heritage-compatible-check.ts index 95b605f2cfdae926f518c06a859a21cd57c1f4c5..b32a86f730779a506fcb2c0d300182f4b1b2713a 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/observed-heritage-compatible-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/observed-heritage-compatible-check.ts @@ -15,7 +15,7 @@ import * as arkts from '@koalaui/libarkts'; import { UISyntaxRule, UISyntaxRuleContext } from './ui-syntax-rule'; -import { PresetDecorators } from '../utils/index'; +import { getIdentifierName, PresetDecorators } from '../utils/index'; function getObservedDecorator(node: arkts.AstNode, observedClasses: string[], observedV2Classes: string[]): void { for (const child of node.getChildren()) { @@ -57,30 +57,45 @@ function checkInheritanceCompatibility(context: UISyntaxRuleContext, node: arkts ); //Get the name of the superClass - const superClassName = node.definition?.super?.dumpSrc(); - if (!superClassName) { + if (!node.definition?.super) { return; } - // Verify that the inheritance relationship is compatible - if (observedV1Decorator && observedV2Classes.includes(superClassName)) { - context.report({ - node: observedV1Decorator, - message: rule.messages.incompatibleHeritageObservedToObservedV2, - }); - } - if (observedV2Decorator && observedClasses.includes(superClassName)) { - context.report({ - node: observedV2Decorator, - message: rule.messages.incompatibleHeritageObservedV2ToObserved, - }); + if (arkts.isETSTypeReference(node.definition?.super)) { + if (!node.definition.super.part) { + return; + } + if (!arkts.isETSTypeReferencePart(node.definition?.super.part)) { + return; + } + if (!node.definition?.super.part.name) { + return; + } + const superClassName = getIdentifierName(node.definition?.super.part.name); + + if (!superClassName) { + return; + } + // Verify that the inheritance relationship is compatible + if (observedV1Decorator && observedV2Classes.includes(superClassName)) { + context.report({ + node: observedV1Decorator, + message: rule.messages.incompatibleHeritageObservedToObservedV2, + }); + } + if (observedV2Decorator && observedClasses.includes(superClassName)) { + context.report({ + node: observedV2Decorator, + message: rule.messages.incompatibleHeritageObservedV2ToObserved, + }); + } } } const rule: UISyntaxRule = { name: 'observed-heritage-compatible-check', messages: { - incompatibleHeritageObservedToObservedV2: `The current class is decorated by '@Observed', it cannot inherit a class decorated by '@ObservedV2'.`, - incompatibleHeritageObservedV2ToObserved: `The current class is decorated by '@ObservedV2', it cannot inherit a class decorated by '@Observed'.`, + incompatibleHeritageObservedToObservedV2: `A class decorated by '@Observed' cannot inherit from a class decorated by '@ObservedV2'.`, + incompatibleHeritageObservedV2ToObserved: `A class decorated by '@ObservedV2' cannot inherit from a class decorated by '@Observed'.`, }, setup(context) { // Record the class name decorated with @Observed and @ObservedV2 diff --git a/arkui-plugins/ui-syntax-plugins/rules/observed-observedV2-check.ts b/arkui-plugins/ui-syntax-plugins/rules/observed-observedV2-check.ts index e02304b3ceb27c0a1b36ee159889a7f672c27144..808438ed090f74e1cb97a7bc3b062a7885a8c002 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/observed-observedV2-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/observed-observedV2-check.ts @@ -29,9 +29,11 @@ const rule: UISyntaxRule = { return; } const hasObservedDecorator = node.definition?.annotations?.find(annotations => annotations.expr && - annotations.expr.dumpSrc() === PresetDecorators.OBSERVED_V1); + arkts.isIdentifier(annotations.expr) && + annotations.expr.name === PresetDecorators.OBSERVED_V1); const hasObservedV2Decorator = node.definition?.annotations?.find(annotations => annotations.expr && - annotations.expr.dumpSrc() === PresetDecorators.OBSERVED_V2); + arkts.isIdentifier(annotations.expr) && + annotations.expr.name === PresetDecorators.OBSERVED_V2); // If the current class is decorated by @Observed and @ObservedV2, an error is reported if (hasObservedDecorator && hasObservedV2Decorator) { context.report({ @@ -44,4 +46,4 @@ const rule: UISyntaxRule = { }, }; -export default rule; +export default rule; \ No newline at end of file