From 15c9854177b16fc2a45f9a847d9406628b8d75ce Mon Sep 17 00:00:00 2001 From: wangweiyuan2 Date: Tue, 15 Jul 2025 15:34:42 +0800 Subject: [PATCH] ui-syntax-plugins-0715 Signed-off-by: wangweiyuan2 --- .../componentV2-state-usage-validation.ts | 564 +++++++++--------- .../rules/computed-decorator-check.ts | 513 ++++++++-------- .../rules/construct-parameter-literal.ts | 43 +- .../consumer-provider-decorator-check.ts | 510 ++++++++-------- .../rules/monitor-decorator-check.ts | 390 ++++++------ .../rules/no-duplicate-entry.ts | 14 +- .../rules/no-duplicate-id.ts | 70 +-- .../rules/no-duplicate-preview.ts | 4 +- .../rules/no-prop-link-objectlink-in-entry.ts | 5 +- .../observedV2-trace-usage-validation.ts | 17 +- .../rules/old-new-decorator-mix-use-check.ts | 2 +- .../rules/once-decorator-check.ts | 7 +- .../rules/one-decorator-on-function-method.ts | 3 +- .../ui-syntax-plugins/rules/property-type.ts | 3 +- .../rules/track-decorator-check.ts | 19 +- 15 files changed, 1091 insertions(+), 1073 deletions(-) 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 3aec12a85..95ddd8b42 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 @@ -15,327 +15,325 @@ import * as arkts from '@koalaui/libarkts'; import { - getClassPropertyAnnotationNames, PresetDecorators, getAnnotationUsage, getClassPropertyName, getIdentifierName + getClassPropertyAnnotationNames, PresetDecorators, getAnnotationUsage, getClassPropertyName, getIdentifierName } from '../utils'; import { AbstractUISyntaxRule } from './ui-syntax-rule'; const builtInDecorators = [PresetDecorators.LOCAL, PresetDecorators.PARAM, PresetDecorators.EVENT]; class ComponentV2StateUsageValidationRule extends AbstractUISyntaxRule { - private componentV2PropertyMap: Map> = new Map(); - - public beforeTransform(): void { - this.componentV2PropertyMap = new Map(); - } - - public setup(): Record { - return { - multipleBuiltInDecorators: `The member property or method cannot be decorated by multiple built-in annotations.`, - 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' or '@BuilderParam'.`, - localNeedNoInit: `The '{{decoratorName}}' property '{{key}}' in the custom component '{{componentName}}' cannot be initialized here (forbidden to specify).`, - useStateDecoratorsWithProperty: `'@{{annotationName}}' can only decorate member property.`, - }; - } - - public parsed(node: arkts.AstNode): void { - this.initComponentV2PropertyMap(node, this.componentV2PropertyMap); - this.checkInitializeRule(node, this.componentV2PropertyMap); - if (arkts.isMethodDefinition(node)) { - // Rule 5: Local, Param, Event decorators must be used with Property - this.checkuseStateDecoratorsWithProperty(node); - } - if (!arkts.isStructDeclaration(node)) { - return; + private componentV2PropertyMap: Map> = new Map(); + public setup(): Record { + return { + multipleBuiltInDecorators: `The member property or method cannot be decorated by multiple built-in annotations.`, + 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' or '@BuilderParam'.`, + localNeedNoInit: `The '{{decoratorName}}' property '{{key}}' in the custom component '{{componentName}}' cannot be initialized here (forbidden to specify).`, + useStateDecoratorsWithProperty: `'@{{annotationName}}' can only decorate member property.`, + }; } - this.validateClassPropertyDecorators(node); - } - private hasisComponentV2 = (node: arkts.StructDeclaration): boolean => !!getAnnotationUsage(node, - PresetDecorators.COMPONENT_V2); - - private hasComponent = (node: arkts.StructDeclaration): boolean => !!getAnnotationUsage(node, - PresetDecorators.COMPONENT_V1); + public beforeTransform(): void { + this.componentV2PropertyMap = new Map(); + } - private checkMultipleBuiltInDecorators(member: arkts.ClassProperty, - propertyDecorators: string[]): void { - const appliedBuiltInDecorators = propertyDecorators.filter(d => builtInDecorators.includes(d)); - if (appliedBuiltInDecorators.length > 1) { - member.annotations.forEach(annotation => { - if (annotation.expr && arkts.isIdentifier(annotation.expr)) { - const annotationsName = annotation.expr.name; - this.reportMultipleBuiltInDecoratorsError(annotation, annotationsName, builtInDecorators); + public parsed(node: arkts.AstNode): void { + this.initComponentV2PropertyMap(node, this.componentV2PropertyMap); + this.checkInitializeRule(node, this.componentV2PropertyMap); + if (arkts.isMethodDefinition(node)) { + // Rule 5: Local, Param, Event decorators must be used with Property + this.checkuseStateDecoratorsWithProperty(node); } - }); + if (!arkts.isStructDeclaration(node)) { + return; + } + this.validateClassPropertyDecorators(node); } - }; - private reportMultipleBuiltInDecoratorsError(annotation: arkts.AstNode, - annotationsName: string | undefined, builtInDecorators: string[]): void { - if (annotationsName && builtInDecorators.includes(annotationsName)) { - this.report({ - node: annotation, - message: this.messages.multipleBuiltInDecorators, - }); - } - } + private hasisComponentV2 = (node: arkts.StructDeclaration): boolean => !!getAnnotationUsage(node, + PresetDecorators.COMPONENT_V2); - private checkParamRequiresRequire(member: arkts.ClassProperty, - propertyDecorators: string[]): void { - if (propertyDecorators.includes(PresetDecorators.PARAM) && !member.value && - !propertyDecorators.includes(PresetDecorators.REQUIRE) && member.key) { - const memberKey = member.key; - this.report({ - node: memberKey, - message: this.messages.paramRequiresRequire, - fix: (memberKey) => { - const startPosition = memberKey.startPosition; - return { - range: [startPosition, startPosition], - code: `@${PresetDecorators.REQUIRE} `, - }; - }, - }); - } - }; + private checkMultipleBuiltInDecorators(member: arkts.ClassProperty, + propertyDecorators: string[]): void { + const appliedBuiltInDecorators = propertyDecorators.filter(d => builtInDecorators.includes(d)); + if (appliedBuiltInDecorators.length > 1) { + member.annotations.forEach(annotation => { + if (annotation.expr && arkts.isIdentifier(annotation.expr)) { + const annotationsName = annotation.expr.name; + this.reportMultipleBuiltInDecoratorsError(annotation, annotationsName, builtInDecorators); + } + }); + } + }; - private checkRequireOnlyWithParam(member: arkts.ClassProperty, - propertyDecorators: string[], isComponentV2: boolean): void { - const requireDecorator = member.annotations.find(annotation => - annotation.expr && arkts.isIdentifier(annotation.expr) && annotation.expr.name === PresetDecorators.REQUIRE - ); - if (isComponentV2 && - requireDecorator && - !propertyDecorators.includes(PresetDecorators.PARAM) && - !propertyDecorators.includes(PresetDecorators.BUILDER_PARAM)) { - this.report({ - node: requireDecorator, - message: this.messages.requireOnlyWithParam, - fix: (requireDecorator) => { - const startPosition = requireDecorator.startPosition; - const endPosition = requireDecorator.endPosition; - return { - range: [startPosition, endPosition], - code: '', - }; - }, - }); + private reportMultipleBuiltInDecoratorsError(annotation: arkts.AstNode, + annotationsName: string | undefined, builtInDecorators: string[]): void { + if (annotationsName && builtInDecorators.includes(annotationsName)) { + this.report({ + node: annotation, + message: this.messages.multipleBuiltInDecorators, + }); + } } - }; - private checkuseStateDecoratorsWithProperty(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; - this.reportInvalidDecoratorOnMethod(annotation, annotationName); - } - }); - } + private checkParamRequiresRequire(member: arkts.ClassProperty, + propertyDecorators: string[]): void { + if (propertyDecorators.includes(PresetDecorators.PARAM) && !member.value && + !propertyDecorators.includes(PresetDecorators.REQUIRE) && member.key) { + const memberKey = member.key; + this.report({ + node: memberKey, + message: this.messages.paramRequiresRequire, + fix: (memberKey) => { + const startPosition = memberKey.startPosition; + return { + range: [startPosition, startPosition], + code: `@${PresetDecorators.REQUIRE} `, + }; + }, + }); + } + }; - private reportInvalidDecoratorOnMethod(annotation: arkts.AnnotationUsage, - annotationName: string): void { - this.report({ - node: annotation, - message: this.messages.useStateDecoratorsWithProperty, - data: { annotationName }, - fix: (annotation) => { - const startPosition = annotation.startPosition; - const endPosition = annotation.endPosition; - return { - range: [startPosition, endPosition], - code: '', - }; - }, - }); - } + private checkRequireOnlyWithParam(member: arkts.ClassProperty, + propertyDecorators: string[], isComponentV2: boolean): void { + const requireDecorator = member.annotations.find(annotation => + annotation.expr && arkts.isIdentifier(annotation.expr) && annotation.expr.name === PresetDecorators.REQUIRE + ); + if (isComponentV2 && + requireDecorator && + !propertyDecorators.includes(PresetDecorators.PARAM) && + !propertyDecorators.includes(PresetDecorators.BUILDER_PARAM)) { + this.report({ + node: requireDecorator, + message: this.messages.requireOnlyWithParam, + fix: (requireDecorator) => { + let startPosition = requireDecorator.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + let endPosition = requireDecorator.endPosition; + return { + range: [startPosition, endPosition], + code: '', + }; + }, + }); + } + }; - private validateClassPropertyDecorators(node: arkts.StructDeclaration): void { - const isComponentV2 = this.hasisComponentV2(node); - node.definition.body.forEach(member => { - if (!arkts.isClassProperty(member)) { - return; - } - const propertyDecorators = getClassPropertyAnnotationNames(member); - // Rule 1: Multiple built-in decorators - this.checkMultipleBuiltInDecorators(member, propertyDecorators); + private checkuseStateDecoratorsWithProperty(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; + this.reportInvalidDecoratorOnMethod(annotation, annotationName); + } + }); + } - // Rule 2: @Param without default value must be combined with @Require - this.checkParamRequiresRequire(member, propertyDecorators); + private reportInvalidDecoratorOnMethod(annotation: arkts.AnnotationUsage, + annotationName: string): void { + this.report({ + node: annotation, + message: this.messages.useStateDecoratorsWithProperty, + data: { annotationName }, + fix: (annotation) => { + let startPosition = annotation.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + let endPosition = annotation.endPosition; + return { + range: [startPosition, endPosition], + code: '', + }; + }, + }); + } - // Rule 3: @Require must be used together with @Param - this.checkRequireOnlyWithParam(member, propertyDecorators, isComponentV2); - }); - } + private validateClassPropertyDecorators(node: arkts.StructDeclaration): void { + const isComponentV2 = this.hasisComponentV2(node); + node.definition.body.forEach(member => { + if (!arkts.isClassProperty(member)) { + return; + } + const propertyDecorators = getClassPropertyAnnotationNames(member); + // Rule 1: Multiple built-in decorators + this.checkMultipleBuiltInDecorators(member, propertyDecorators); - private 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; - } + // Rule 2: @Param without default value must be combined with @Require + this.checkParamRequiresRequire(member, propertyDecorators); - // Define a function to add property data to the property map - private addPropertyMap( - structName: string, - propertyName: string, - annotationName: string, - propertyMap: Map> - ): void { - if (!propertyMap.has(structName)) { - propertyMap.set(structName, new Map()); + // Rule 3: @Require must be used together with @Param + this.checkRequireOnlyWithParam(member, propertyDecorators, isComponentV2); + }); } - const structProperties = propertyMap.get(structName); - if (structProperties) { - structProperties.set(propertyName, annotationName); + + private 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; } - } - // Iterate through the incoming componentv2 node to see if there are any state variables for decorator decorations - private initComponentV2PropertyMap( - node: arkts.AstNode, - componentV2PropertyMap: Map> - ): void { - if (arkts.nodeType(node) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { - return; + // Define a function to add property data to the property map + private 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); + } } - node.getChildren().forEach((member) => { - if (!arkts.isStructDeclaration(member) || !member.definition.ident || - !this.checkDecorator(member.definition.annotations, PresetDecorators.COMPONENT_V2)) { - return; - } - let structName: string = member.definition.ident.name; - member.definition.body.forEach((item) => { - this.processClassPropertyAnnotations(item, structName, componentV2PropertyMap); - }); - }); - } - private processClassPropertyAnnotations( - item: arkts.AstNode, - structName: string, - componentV2PropertyMap: Map> - ): void { - if (!arkts.isClassProperty(item) || !item.key) { - return; + // Iterate through the incoming componentv2 node to see if there are any state variables for decorator decorations + private 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 || + !this.checkDecorator(member.definition.annotations, PresetDecorators.COMPONENT_V2)) { + return; + } + let structName: string = member.definition.ident.name; + member.definition.body.forEach((item) => { + this.processClassPropertyAnnotations(item, structName, componentV2PropertyMap); + }); + }); } - 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; - this.addPropertyMap(structName, propertyName, annotationName, componentV2PropertyMap); + + private 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; + this.addPropertyMap(structName, propertyName, annotationName, componentV2PropertyMap); + } + item.annotations.forEach((annotation) => { + if (!annotation.expr) { + return; + } + let annotationName: string = getIdentifierName(annotation.expr); + if (annotationName === PresetDecorators.LOCAL) { + this.addPropertyMap(structName, propertyName, annotationName, componentV2PropertyMap); + } + }); } - item.annotations.forEach((annotation) => { - if (!annotation.expr) { - return; - } - let annotationName: string = getIdentifierName(annotation.expr); - if (annotationName === PresetDecorators.LOCAL) { - this.addPropertyMap(structName, propertyName, annotationName, componentV2PropertyMap); - } - }); - } - private checkInitializeRule( - node: arkts.AstNode, + private checkInitializeRule( + node: arkts.AstNode, - componentV2PropertyMap: Map>, - ): void { - if (!arkts.isIdentifier(node) || !componentV2PropertyMap.has(getIdentifierName(node)) || !node.parent) { - 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; + componentV2PropertyMap: Map>, + ): void { + if (!arkts.isIdentifier(node) || !componentV2PropertyMap.has(getIdentifierName(node)) || !node.parent) { + return; } - propertyArray.push(annotation.expr.name); - }); - const classPropertyName = getClassPropertyName(property); - if (!classPropertyName) { - return; - } - parentPropertyMap.set(classPropertyName, 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; + let structName: string = getIdentifierName(node); + let parentNode: arkts.AstNode = node.parent; + if (!arkts.isCallExpression(parentNode)) { + return; } - const childkeyName = getIdentifierName(property.key); - if (!childPropertyName.has(childkeyName)) { - return; + let structNode = node.parent; + while (!arkts.isStructDeclaration(structNode)) { + if (!structNode.parent) { + return; + } + structNode = structNode.parent; } - this.reportLocalNeedInit(childPropertyName, childkeyName, property, structName); - }); - }); - } + 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); + }); + const classPropertyName = getClassPropertyName(property); + if (!classPropertyName) { + return; + } + parentPropertyMap.set(classPropertyName, 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; + } + this.reportLocalNeedInit(childPropertyName, childkeyName, property, structName); + }); + }); + } - private reportLocalNeedInit(childPropertyName: Map, childkeyName: string, - property: arkts.Property, structName: string): void { - if (childPropertyName.get(childkeyName) === PresetDecorators.LOCAL) { - this.report({ - node: property, - message: this.messages.localNeedNoInit, - data: { - decoratorName: '@' + childPropertyName.get(childkeyName)!, - key: childkeyName, - componentName: structName, - }, - fix: (property) => { - return { - range: [property.startPosition, property.endPosition], - code: '', - }; + private reportLocalNeedInit(childPropertyName: Map, childkeyName: string, + property: arkts.Property, structName: string): void { + if (childPropertyName.get(childkeyName) === PresetDecorators.LOCAL) { + this.report({ + node: property, + message: this.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) { - this.report({ - node: property, - message: this.messages.localNeedNoInit, - data: { - decoratorName: PresetDecorators.REGULAR, - key: childkeyName, - componentName: structName, - }, - fix: (property) => { - return { - range: [property.startPosition, property.endPosition], - code: '', - }; + if (childPropertyName.get(childkeyName) === PresetDecorators.REGULAR) { + this.report({ + node: property, + message: this.messages.localNeedNoInit, + data: { + decoratorName: PresetDecorators.REGULAR, + key: childkeyName, + componentName: structName, + }, + fix: (property) => { + return { + range: [property.startPosition, property.endPosition], + code: '', + }; + } + }); } - }); } - } }; export default ComponentV2StateUsageValidationRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/computed-decorator-check.ts b/arkui-plugins/ui-syntax-plugins/rules/computed-decorator-check.ts index f48e5fa94..72a802489 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/computed-decorator-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/computed-decorator-check.ts @@ -17,298 +17,299 @@ import * as arkts from '@koalaui/libarkts'; import { getIdentifierName, PresetDecorators, getAnnotationName, getAnnotationUsage, BUILD_NAME } from '../utils'; import { AbstractUISyntaxRule } from './ui-syntax-rule'; - class ComputedDecoratorCheckRule extends AbstractUISyntaxRule { - private computedGetters: Map = new Map(); - private computedSetters: Map = new Map(); - - public setup(): Record { - return { - onlyOnGetter: `@Computed can only decorate 'GetAccessor'.`, - onlyInObservedV2: `The '@Computed' can decorate only member method within a 'class' decorated with ObservedV2.`, - componentV2InStruct: `The '@Computed' annotation can only be used in a 'struct' decorated with ComponentV2.`, - noTwoWayBinding: `A property decorated by '@Computed' cannot be used with two-way bind syntax.`, - computedMethodDefineSet: `A property decorated by '@Computed' cannot define a set method.` - }; - } - - public beforeTransform(): void { - this.computedGetters = new Map(); - this.computedSetters = new Map(); - } + private computedGetters: Map = new Map(); + private computedSetters: Map = new Map(); - public parsed(node: arkts.AstNode): void { - if (arkts.isStructDeclaration(node)) { - this.validateComponentV2InStruct(node); - this.validateStructBody(node); + public setup(): Record { + return { + onlyOnGetter: `@Computed can only decorate 'GetAccessor'.`, + onlyInObservedV2: `The '@Computed' can decorate only member method within a 'class' decorated with ObservedV2.`, + componentV2InStruct: `The '@Computed' annotation can only be used in a 'struct' decorated with ComponentV2.`, + noTwoWayBinding: `A property decorated by '@Computed' cannot be used with two-way bind syntax.`, + computedMethodDefineSet: `A property decorated by '@Computed' cannot define a set method.` + }; } - if (arkts.isClassDeclaration(node)) { - this.validateClassBody(node); + public beforeTransform(): void { + this.computedGetters = new Map(); + this.computedSetters = new Map(); } - } - private validateStructBody(node: arkts.StructDeclaration): void { - let computedDecorator: arkts.AnnotationUsage | undefined; - node.definition.body.forEach((member) => { - if (arkts.isClassProperty(member)) { - this.validateComputedOnClassProperty(member); - return; - } - - if (arkts.isMethodDefinition(member)) { - const methodName = getIdentifierName(member.name); - computedDecorator = member.scriptFunction.annotations?.find(annotation => - annotation.expr && arkts.isIdentifier(annotation.expr) && - annotation.expr.name === PresetDecorators.COMPUTED - ); - - this.validateComputedMethodKind(member, computedDecorator, methodName); - if (member.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_SET) { - this.computedSetters.set(methodName, member); + public parsed(node: arkts.AstNode): void { + if (arkts.isStructDeclaration(node)) { + this.validateComponentV2InStruct(node); + this.validateStructBody(node); } - if (methodName === BUILD_NAME) { - this.validateBuildMethod(member); + if (arkts.isClassDeclaration(node)) { + this.validateClassBody(node); } - } - }); + } - this.validateGetterSetterConflict(); - } + private validateStructBody(node: arkts.StructDeclaration): void { + let computedDecorator: arkts.AnnotationUsage | undefined; + node.definition.body.forEach((member) => { + if (arkts.isClassProperty(member)) { + this.validateComputedOnClassProperty(member); + return; + } + + if (arkts.isMethodDefinition(member)) { + const methodName = getIdentifierName(member.name); + computedDecorator = member.scriptFunction.annotations?.find(annotation => + annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr.name === PresetDecorators.COMPUTED + ); + + this.validateComputedMethodKind(member, computedDecorator, methodName); + if (member.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_SET) { + this.computedSetters.set(methodName, member); + } + + if (methodName === BUILD_NAME) { + this.validateBuildMethod(member); + } + } + }); + + this.validateGetterSetterConflict(); + } - private validateComputedOnClassProperty(member: arkts.ClassProperty): void { - const computedDecorator = member.annotations?.find(annotation => - annotation.expr && arkts.isIdentifier(annotation.expr) && - annotation.expr.name === PresetDecorators.COMPUTED - ); - if (computedDecorator) { - this.report({ - node: computedDecorator, - message: this.messages.onlyOnGetter, - fix: (computedDecorator) => { - const startPosition = computedDecorator.startPosition; - const endPosition = computedDecorator.endPosition; - return { - range: [startPosition, endPosition], - code: '', - }; - }, - }); + private validateComputedOnClassProperty(member: arkts.ClassProperty): void { + const computedDecorator = member.annotations?.find(annotation => + annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr.name === PresetDecorators.COMPUTED + ); + if (computedDecorator) { + this.report({ + node: computedDecorator, + message: this.messages.onlyOnGetter, + fix: (computedDecorator) => { + let startPosition = computedDecorator.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + const endPosition = computedDecorator.endPosition; + return { + range: [startPosition, endPosition], + code: '', + }; + }, + }); + } } - } - private validateComputedMethodKind( - member: arkts.MethodDefinition, - computedDecorator: arkts.AnnotationUsage | undefined, - methodName: string - ): void { - if (computedDecorator) { - const isGetter = member.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET; - if (!isGetter) { - this.reportValidateComputedMethodKind(computedDecorator); - } else { - this.computedGetters.set(methodName, member); - } + private validateComputedMethodKind( + member: arkts.MethodDefinition, + computedDecorator: arkts.AnnotationUsage | undefined, + methodName: string + ): void { + if (computedDecorator) { + const isGetter = member.kind === arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET; + if (!isGetter) { + this.reportValidateComputedMethodKind(computedDecorator); + } else { + this.computedGetters.set(methodName, member); + } + } } - } - private reportValidateComputedMethodKind(computedDecorator: arkts.AnnotationUsage | undefined): void { - if (!computedDecorator) { - return; + private reportValidateComputedMethodKind(computedDecorator: arkts.AnnotationUsage | undefined): void { + if (!computedDecorator) { + return; + } + this.report({ + node: computedDecorator, + message: this.messages.onlyOnGetter, + fix: (computedDecorator) => { + let startPosition = computedDecorator.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + const endPosition = computedDecorator.endPosition; + return { + range: [startPosition, endPosition], + code: '', + }; + }, + }); } - this.report({ - node: computedDecorator, - message: this.messages.onlyOnGetter, - fix: (computedDecorator) => { - const startPosition = computedDecorator.startPosition; - const endPosition = computedDecorator.endPosition; - return { - range: [startPosition, endPosition], - code: '', - }; - }, - }); - } - private validateBuildMethod(member: arkts.MethodDefinition): void { - member.scriptFunction.body?.getChildren().forEach((childNode) => { - if (!arkts.isExpressionStatement(childNode)) { - return; - } + private validateBuildMethod(member: arkts.MethodDefinition): void { + member.scriptFunction.body?.getChildren().forEach((childNode) => { + if (!arkts.isExpressionStatement(childNode)) { + return; + } + const queue: Array = [childNode]; + while (queue.length > 0) { + const currentNode: arkts.AstNode = queue.shift() as arkts.AstNode; + if (arkts.isCallExpression(currentNode)) { + this.validateCallExpression(currentNode); + } + // Continue traversing the child nodes + const children = currentNode.getChildren(); + for (const child of children) { + queue.push(child); + } + } + }); + } - const queue: Array = [childNode]; - while (queue.length > 0) { - const currentNode: arkts.AstNode = queue.shift() as arkts.AstNode; - if (arkts.isCallExpression(currentNode)) { - this.validateCallExpression(currentNode); + private validateCallExpression(currentNode: arkts.CallExpression): void { + if (!arkts.isIdentifier(currentNode.expression) || getIdentifierName(currentNode.expression) !== '$$') { + return; } - // Continue traversing the child nodes - const children = currentNode.getChildren(); - for (const child of children) { - queue.push(child); - } - } - }); - } - private validateCallExpression(currentNode: arkts.CallExpression): void { - if (!arkts.isIdentifier(currentNode.expression) || getIdentifierName(currentNode.expression) !== '$$') { - return; + currentNode.arguments.forEach((argument) => { + if (arkts.isMemberExpression(argument)) { + const getterName = getIdentifierName(argument.property); + this.reportValidateCallExpression(currentNode, argument, getterName); + } + }); } - currentNode.arguments.forEach((argument) => { - if (arkts.isMemberExpression(argument)) { - const getterName = getIdentifierName(argument.property); - this.reportValidateCallExpression(currentNode, getterName); - } - }); - } + private reportValidateCallExpression( + currentNode: arkts.CallExpression, + argument: arkts.MemberExpression, + getterName: string + ): void { + if (this.computedGetters.has(getterName)) { + this.report({ + node: currentNode, + message: this.messages.noTwoWayBinding, + fix: (currentNode) => { + const startPosition = currentNode.startPosition; + const endPosition = currentNode.endPosition; + return { + range: [startPosition, endPosition], + code: argument.dumpSrc(), + }; + }, + }); + } + } - private reportValidateCallExpression( - currentNode: arkts.CallExpression, - getterName: string - ): void { - if (this.computedGetters.has(getterName)) { - this.report({ - node: currentNode, - message: this.messages.noTwoWayBinding, - fix: (currentNode) => { - const startPosition = currentNode.startPosition; - const endPosition = currentNode.endPosition; - return { - range: [startPosition, endPosition], - code: '', - }; - }, - }); + private validateGetterSetterConflict(): void { + for (const [name] of this.computedGetters) { + if (this.computedSetters.has(name)) { + this.reportValidateGetterSetterConflict(name); + } + } } - } - private validateGetterSetterConflict(): void { - for (const [name] of this.computedGetters) { - if (this.computedSetters.has(name)) { - this.reportValidateGetterSetterConflict(name); - } + private reportValidateGetterSetterConflict(name: string): void { + const setter = this.computedSetters.get(name)!; + this.report({ + node: setter, + message: this.messages.computedMethodDefineSet, + fix: (node) => { + const startPosition = node.startPosition; + const endPosition = node.endPosition; + return { + range: [startPosition, endPosition], + code: '', + }; + }, + }); } - } - private reportValidateGetterSetterConflict(name: string): void { - const setter = this.computedSetters.get(name)!; - this.report({ - node: setter, - message: this.messages.computedMethodDefineSet, - fix: (node) => { - const startPosition = node.startPosition; - const endPosition = node.endPosition; - return { - range: [startPosition, endPosition], - code: '', - }; - }, - }); - } + private validateClassBody(node: arkts.ClassDeclaration): void { + const observedV2Decorator = node.definition?.annotations.find(annotation => + getAnnotationName(annotation) === PresetDecorators.OBSERVED_V2 + ); - private validateClassBody(node: arkts.ClassDeclaration): void { - const observedV2Decorator = node.definition?.annotations.find(annotation => - getAnnotationName(annotation) === PresetDecorators.OBSERVED_V2 - ); + node.definition?.body.forEach((member) => { + if (arkts.isMethodDefinition(member)) { + + this.validateComputedInClass(node, member, observedV2Decorator); + const computedDecorator = member.scriptFunction.annotations?.find(annotation => + annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr.name === PresetDecorators.COMPUTED + ); + if (!arkts.isIdentifier(member.name)) { + return; + } + const methodName = getIdentifierName(member.name); + + this.validateComputedMethodKind(member, computedDecorator, methodName); + } + }); + } + + private validateComponentV2InStruct(node: arkts.StructDeclaration): void { + const componentV2Decorator = getAnnotationUsage(node, PresetDecorators.COMPONENT_V2); + const componentDecorator = getAnnotationUsage(node, PresetDecorators.COMPONENT_V1); - node.definition?.body.forEach((member) => { - if (arkts.isMethodDefinition(member)) { + node.definition?.body.forEach((member) => { + if (arkts.isMethodDefinition(member)) { + this.checkComponentV2InStruct(node, member, componentV2Decorator, componentDecorator); + } + }); + } - this.validateComputedInClass(node, member, observedV2Decorator); + private checkComponentV2InStruct( + node: arkts.StructDeclaration | arkts.ClassDeclaration, + member: arkts.MethodDefinition, + componentV2Decorator: arkts.AnnotationUsage | undefined, + componentDecorator: arkts.AnnotationUsage | undefined + ): void { const computedDecorator = member.scriptFunction.annotations?.find(annotation => - annotation.expr && arkts.isIdentifier(annotation.expr) && - annotation.expr.name === PresetDecorators.COMPUTED + annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr.name === PresetDecorators.COMPUTED ); - if (!arkts.isIdentifier(member.name)) { - return; + if (computedDecorator && !componentV2Decorator && !componentDecorator) { + this.report({ + node: computedDecorator, + message: this.messages.componentV2InStruct, + fix: () => { + const startPosition = node.startPosition; + const endPosition = node.startPosition; + return { + range: [startPosition, endPosition], + code: `@${PresetDecorators.COMPONENT_V2}\n`, + }; + }, + }); } - const methodName = getIdentifierName(member.name); - - this.validateComputedMethodKind(member, computedDecorator, methodName); - } - }); - } - private validateComponentV2InStruct(node: arkts.StructDeclaration): void { - const componentV2Decorator = getAnnotationUsage(node, PresetDecorators.COMPONENT_V2); - const componentDecorator = getAnnotationUsage(node, PresetDecorators.COMPONENT_V1); - - node.definition?.body.forEach((member) => { - if (arkts.isMethodDefinition(member)) { - this.checkComponentV2InStruct(node, member, componentV2Decorator, componentDecorator); - } - }); - } - - private checkComponentV2InStruct( - node: arkts.StructDeclaration | arkts.ClassDeclaration, - member: arkts.MethodDefinition, - componentV2Decorator: arkts.AnnotationUsage | undefined, - componentDecorator: arkts.AnnotationUsage | undefined - ): void { - const computedDecorator = member.scriptFunction.annotations?.find(annotation => - annotation.expr && arkts.isIdentifier(annotation.expr) && - annotation.expr.name === PresetDecorators.COMPUTED - ); - if (computedDecorator && !componentV2Decorator && !componentDecorator) { - this.report({ - node: computedDecorator, - message: this.messages.componentV2InStruct, - fix: () => { - const startPosition = node.startPosition; - const endPosition = node.startPosition; - return { - range: [startPosition, endPosition], - code: `@${PresetDecorators.COMPONENT_V2}\n`, - }; - }, - }); - } - - if (computedDecorator && !componentV2Decorator && componentDecorator) { - this.report({ - node: computedDecorator, - message: this.messages.componentV2InStruct, - fix: () => { - const startPosition = componentDecorator.startPosition; - const endPosition = componentDecorator.endPosition; - return { - range: [startPosition, endPosition], - code: `@${PresetDecorators.COMPONENT_V2}`, - }; - }, - }); + if (computedDecorator && !componentV2Decorator && componentDecorator) { + this.report({ + node: computedDecorator, + message: this.messages.componentV2InStruct, + fix: () => { + let startPosition = componentDecorator.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + const endPosition = componentDecorator.endPosition; + return { + range: [startPosition, endPosition], + code: `@${PresetDecorators.COMPONENT_V2}`, + }; + }, + }); + } } - } - private validateComputedInClass( - node: arkts.AstNode, - member: arkts.MethodDefinition, - observedV2Decorator: arkts.AnnotationUsage | undefined, - ): void { - const computedDecorator = member.scriptFunction.annotations?.find(annotation => - annotation.expr && arkts.isIdentifier(annotation.expr) && - annotation.expr.name === PresetDecorators.COMPUTED - ); - if (computedDecorator && !observedV2Decorator && - arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET === member.kind) { - this.report({ - node: computedDecorator, - message: this.messages.onlyInObservedV2, - fix: () => { - const startPosition = node.startPosition; - return { - range: [startPosition, startPosition], - code: `@${PresetDecorators.OBSERVED_V2}\n`, - }; - }, - }); + private validateComputedInClass( + node: arkts.AstNode, + member: arkts.MethodDefinition, + observedV2Decorator: arkts.AnnotationUsage | undefined, + ): void { + const computedDecorator = member.scriptFunction.annotations?.find(annotation => + annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr.name === PresetDecorators.COMPUTED + ); + if (computedDecorator && !observedV2Decorator && + arkts.Es2pandaMethodDefinitionKind.METHOD_DEFINITION_KIND_GET === member.kind) { + this.report({ + node: computedDecorator, + message: this.messages.onlyInObservedV2, + fix: () => { + const startPosition = node.startPosition; + return { + range: [startPosition, startPosition], + code: `@${PresetDecorators.OBSERVED_V2}\n`, + }; + }, + }); + } } - } } -export default ComputedDecoratorCheckRule; - +export default ComputedDecoratorCheckRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/construct-parameter-literal.ts b/arkui-plugins/ui-syntax-plugins/rules/construct-parameter-literal.ts index c57f218a1..784cecad5 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/construct-parameter-literal.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/construct-parameter-literal.ts @@ -20,7 +20,7 @@ import { getClassPropertyAnnotationNames, getClassPropertyName, getIdentifierNam class ConstructParameterLiteralRule extends AbstractUISyntaxRule { // Record all @Link and @ObjectLink attributes - private linkMap: Map = new Map(); + private linkMap: Map> = new Map(); public setup(): Record { return { @@ -37,21 +37,37 @@ class ConstructParameterLiteralRule extends AbstractUISyntaxRule { this.checkInitializeWithLiteral(node); } + private addLinkProperty( + structName: string, + propertyName: string, + annotationName: string + ): void { + if (!this.linkMap.has(structName)) { + this.linkMap.set(structName, new Map()); + } + const structProperties = this.linkMap.get(structName); + if (!structProperties) { + return; + } + structProperties.set(propertyName, annotationName); + } + private recordStructWithLinkDecorators(item: arkts.AstNode, structName: string): void { if (!arkts.isClassProperty(item)) { return; } const ClassPropertyAnnotations = getClassPropertyAnnotationNames(item); - if (!ClassPropertyAnnotations.includes(PresetDecorators.OBJECT_LINK) && - !ClassPropertyAnnotations.includes(PresetDecorators.LINK)) { + const classPropertyName = getClassPropertyName(item); + if (!classPropertyName) { return; } - const annotationName = getClassPropertyName(item); - if (!annotationName) { - return; + if (ClassPropertyAnnotations.includes(PresetDecorators.OBJECT_LINK)) { + this.addLinkProperty(structName, classPropertyName, PresetDecorators.OBJECT_LINK); + } + if (ClassPropertyAnnotations.includes(PresetDecorators.LINK)) { + this.addLinkProperty(structName, classPropertyName, PresetDecorators.LINK); } - this.linkMap.set(structName, annotationName); } private initMap(node: arkts.AstNode): void { @@ -85,13 +101,17 @@ class ConstructParameterLiteralRule extends AbstractUISyntaxRule { if (!this.linkMap.has(componentName)) { return; } + const structLinkMap = this.linkMap.get(componentName); + if (!structLinkMap) { + return; + } node.arguments.forEach((member) => { member.getChildren().forEach((property) => { if (!arkts.isProperty(property) || !property.key || !property.value) { return; } - const key: string = getIdentifierName(property.key); - if (key === '') { + const propertyName: string = getIdentifierName(property.key); + if (propertyName === '' || !structLinkMap.has(propertyName)) { return; } // If the statement type is single-level MemberExpression or Identifier, construct-parameter is validated. @@ -102,14 +122,13 @@ class ConstructParameterLiteralRule extends AbstractUISyntaxRule { return; } const initializerName = property.value.dumpSrc().replace(/\(this\)/g, 'this'); - const parameter: string = this.linkMap.get(componentName)!; this.report({ node: property, message: this.messages.initializerIsLiteral, data: { initializerName: initializerName, - parameter: `@${parameter}`, - parameterName: key, + parameter: `@${structLinkMap.get(propertyName)}`, + parameterName: propertyName, }, }); }); diff --git a/arkui-plugins/ui-syntax-plugins/rules/consumer-provider-decorator-check.ts b/arkui-plugins/ui-syntax-plugins/rules/consumer-provider-decorator-check.ts index 740655e1c..837c8d85f 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/consumer-provider-decorator-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/consumer-provider-decorator-check.ts @@ -18,304 +18,304 @@ import { getIdentifierName, MultiMap, PresetDecorators, getAnnotationName } from import { AbstractUISyntaxRule } from './ui-syntax-rule'; class ConsumerProviderDecoratorCheckRule extends AbstractUISyntaxRule { - private componentV2WithConsumer: MultiMap = new MultiMap(); - private componentV2WithProvider: MultiMap = new MultiMap(); + private componentV2WithConsumer: MultiMap = new MultiMap(); + private componentV2WithProvider: MultiMap = new MultiMap(); - public setup(): Record { - return { - providerAndConsumerOnlyOnProperty: `'@{{decorator}}' can only decorate member property.`, - multipleBuiltInDecorators: `The struct member variable can not be decorated by multiple built-in annotations.`, - providerAndConsumerOnlyInStruct: `The '@{{decorator}}' annotation can only be used with 'struct'.`, - forbiddenInitialization: `The '@{{decorator}}' property '{{value}}' in the custom component '{{structName}}' cannot be initialized here (forbidden to specify).`, - }; - } - - public beforeTransform(): void { - this.componentV2WithConsumer = new MultiMap(); - this.componentV2WithProvider = new MultiMap(); - } - - public parsed(node: arkts.AstNode): void { - this.collectStructsWithConsumerAndProvider(node); - this.validateDecoratorsOnMember(node); - this.validateOnlyInStruct(node); + public setup(): Record { + return { + providerAndConsumerOnlyOnProperty: `'@{{decorator}}' can only decorate member property.`, + multipleBuiltInDecorators: `The struct member variable can not be decorated by multiple built-in annotations.`, + providerAndConsumerOnlyInStruct: `The '@{{decorator}}' annotation can only be used with 'struct'.`, + forbiddenInitialization: `The '@{{decorator}}' property '{{value}}' in the custom component '{{structName}}' cannot be initialized here (forbidden to specify).`, + }; + } - if (arkts.isCallExpression(node)) { - this.validateConsumerInitialization(node); - this.validateProviderInitialization(node); + public beforeTransform(): void { + this.componentV2WithConsumer = new MultiMap(); + this.componentV2WithProvider = new MultiMap(); } - } - private collectStructsWithConsumerAndProvider(node: arkts.AstNode): void { - if (arkts.nodeType(node) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { - // Breadth traversal is done through while and queues - const queue: Array = [node]; - while (queue.length > 0) { - const currentNode: arkts.AstNode = queue.shift() as arkts.AstNode; - // Filter and record the nodes of the tree - this.rememberStructName(currentNode); - const children = currentNode.getChildren(); - for (const child of children) { - queue.push(child); + public parsed(node: arkts.AstNode): void { + this.collectStructsWithConsumerAndProvider(node); + this.validateDecoratorsOnMember(node); + this.validateOnlyInStruct(node); + + if (arkts.isCallExpression(node)) { + this.validateConsumerInitialization(node); + this.validateProviderInitialization(node); } - } } - } - private rememberStructName(node: arkts.AstNode): void { - if (arkts.isStructDeclaration(node)) { - node.definition.annotations.forEach((anno) => { - if (!anno.expr) { - return; + private collectStructsWithConsumerAndProvider(node: arkts.AstNode): void { + if (arkts.nodeType(node) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + // Breadth traversal is done through while and queues + const queue: Array = [node]; + while (queue.length > 0) { + const currentNode: arkts.AstNode = queue.shift() as arkts.AstNode; + // Filter and record the nodes of the tree + this.rememberStructName(currentNode); + const children = currentNode.getChildren(); + for (const child of children) { + queue.push(child); + } + } } - const annoName = getIdentifierName(anno.expr); - // Second, it must be decorated with a @component v2 decorator - if (annoName === PresetDecorators.COMPONENT_V2) { - const structName = node.definition.ident?.name ?? ''; - this.processStructMembers(node, structName); - } - }); } - } - private processStructMembers(node: arkts.StructDeclaration, structName: string): void { - node.definition.body.forEach((member) => { - // When a member variable is @consumer modified, it is stored to mark fields that cannot be initialized - if (arkts.isClassProperty(member)) { - const consumerDecorator = member.annotations.some(annotation => - annotation.expr && arkts.isIdentifier(annotation.expr) && - annotation.expr.name === PresetDecorators.CONSUMER - ); - const providerDecorator = member.annotations.some(annotation => - annotation.expr && arkts.isIdentifier(annotation.expr) && - annotation.expr.name === PresetDecorators.PROVIDER - ); - if (!member.key) { - return; - } - const memberName = getIdentifierName(member.key); - if (consumerDecorator && structName && memberName) { - this.componentV2WithConsumer.add(structName, memberName); + private rememberStructName(node: arkts.AstNode): void { + if (arkts.isStructDeclaration(node)) { + node.definition.annotations.forEach((anno) => { + if (!anno.expr) { + return; + } + const annoName = getIdentifierName(anno.expr); + // Second, it must be decorated with a @component v2 decorator + if (annoName === PresetDecorators.COMPONENT_V2) { + const structName = node.definition.ident?.name ?? ''; + this.processStructMembers(node, structName); + } + }); } + } - if (providerDecorator && structName && memberName) { - this.componentV2WithProvider.add(structName, memberName); - } - } - }); - } + private processStructMembers(node: arkts.StructDeclaration, structName: string): void { + node.definition.body.forEach((member) => { + // When a member variable is @consumer modified, it is stored to mark fields that cannot be initialized + if (arkts.isClassProperty(member)) { + const consumerDecorator = member.annotations.some(annotation => + annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr.name === PresetDecorators.CONSUMER + ); + const providerDecorator = member.annotations.some(annotation => + annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr.name === PresetDecorators.PROVIDER + ); + if (!member.key) { + return; + } + const memberName = getIdentifierName(member.key); + if (consumerDecorator && structName && memberName) { + this.componentV2WithConsumer.add(structName, memberName); + } - private validateDecoratorsOnMember(node: arkts.AstNode): void { - if (arkts.isScriptFunction(node) || arkts.isVariableDeclaration(node) || arkts.isTSInterfaceDeclaration(node) || - arkts.isTSTypeAliasDeclaration(node)) { - this.validateDecorator(node, this.messages.providerAndConsumerOnlyOnProperty, PresetDecorators.CONSUMER); - this.validateDecorator(node, this.messages.providerAndConsumerOnlyOnProperty, PresetDecorators.PROVIDER); + if (providerDecorator && structName && memberName) { + this.componentV2WithProvider.add(structName, memberName); + } + } + }); } - if (arkts.isStructDeclaration(node)) { - node.definition.body.forEach(member => { - if (arkts.isClassProperty(member)) { - this.validateMemberDecorators(member); + private validateDecoratorsOnMember(node: arkts.AstNode): void { + if (arkts.isScriptFunction(node) || arkts.isVariableDeclaration(node) || arkts.isTSInterfaceDeclaration(node) || + arkts.isTSTypeAliasDeclaration(node)) { + this.validateDecorator(node, this.messages.providerAndConsumerOnlyOnProperty, PresetDecorators.CONSUMER); + this.validateDecorator(node, this.messages.providerAndConsumerOnlyOnProperty, PresetDecorators.PROVIDER); } - }); - } - } - private validateMemberDecorators( - member: arkts.ClassProperty, - ): void { - // Check that the @Consumer is not mixed with other decorators - this.validateMultipleBuiltInDecorators(member, PresetDecorators.CONSUMER); + if (arkts.isStructDeclaration(node)) { + node.definition.body.forEach(member => { + if (arkts.isClassProperty(member)) { + this.validateMemberDecorators(member); + } + }); + } + } - // Check that the @Provider is mixed with other decorators - this.validateMultipleBuiltInDecorators(member, PresetDecorators.PROVIDER); - } + private validateMemberDecorators( + member: arkts.ClassProperty, + ): void { + // Check that the @Consumer is not mixed with other decorators + this.validateMultipleBuiltInDecorators(member, PresetDecorators.CONSUMER); - private validateMultipleBuiltInDecorators(member: arkts.ClassProperty, decoratorName: string): void { - const decorator = this.findDecorator(member, decoratorName); - const otherDecorators = this.findOtherDecorator(member, decoratorName); - if (!decorator || !otherDecorators) { - return; + // Check that the @Provider is mixed with other decorators + this.validateMultipleBuiltInDecorators(member, PresetDecorators.PROVIDER); } - this.report({ - node: decorator, - message: this.messages.multipleBuiltInDecorators, - data: { - decorator: getAnnotationName(decorator) - }, - fix: () => { - const startPosition = otherDecorators.startPosition; - const endPosition = otherDecorators.endPosition; - return { - range: [startPosition, endPosition], - code: '', - }; - } - }); - } - private findDecorator( - member: arkts.ClassProperty | arkts.VariableDeclaration | arkts.FunctionDeclaration | - arkts.ScriptFunction | arkts.TSInterfaceDeclaration | arkts.TSTypeAliasDeclaration, - decoratorName: string - ): arkts.AnnotationUsage | undefined { - return member.annotations.find(annotation => - annotation.expr && arkts.isIdentifier(annotation.expr) && - annotation.expr.name === decoratorName - ); - } + private validateMultipleBuiltInDecorators(member: arkts.ClassProperty, decoratorName: string): void { + const decorator = this.findDecorator(member, decoratorName); + const otherDecorators = this.findOtherDecorator(member, decoratorName); + if (!decorator || !otherDecorators) { + return; + } + this.report({ + node: decorator, + message: this.messages.multipleBuiltInDecorators, + data: { + decorator: getAnnotationName(decorator) + }, + fix: () => { + let startPosition = otherDecorators.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + const endPosition = otherDecorators.endPosition; + return { + range: [startPosition, endPosition], + code: '', + }; + } + }); + } - private findOtherDecorator(member: arkts.ClassProperty, decoratorName: string): arkts.AnnotationUsage | undefined { - return member.annotations.find(annotation => - annotation.expr && arkts.isIdentifier(annotation.expr) && - annotation.expr.name !== decoratorName - ); - } + private findDecorator( + member: arkts.ClassProperty | arkts.VariableDeclaration | arkts.FunctionDeclaration | + arkts.ScriptFunction | arkts.TSInterfaceDeclaration | arkts.TSTypeAliasDeclaration, + decoratorName: string + ): arkts.AnnotationUsage | undefined { + return member.annotations.find(annotation => + annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr.name === decoratorName + ); + } - private validateOnlyInStruct(node: arkts.AstNode): void { + private findOtherDecorator(member: arkts.ClassProperty, decoratorName: string): arkts.AnnotationUsage | undefined { + return member.annotations.find(annotation => + annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr.name !== decoratorName + ); + } - if (arkts.isClassDeclaration(node)) { - node.definition?.body.forEach(member => { - if (arkts.isClassProperty(member)) { - this.validateDecorator(member, this.messages.providerAndConsumerOnlyInStruct, PresetDecorators.CONSUMER); - this.validateDecorator(member, this.messages.providerAndConsumerOnlyInStruct, PresetDecorators.PROVIDER); + private validateOnlyInStruct(node: arkts.AstNode): void { + if (arkts.isClassDeclaration(node)) { + node.definition?.body.forEach(member => { + if (arkts.isClassProperty(member)) { + this.validateDecorator(member, this.messages.providerAndConsumerOnlyInStruct, PresetDecorators.CONSUMER); + this.validateDecorator(member, this.messages.providerAndConsumerOnlyInStruct, PresetDecorators.PROVIDER); + } + if (arkts.isMethodDefinition(member)) { + this.validateDecorator( + member.scriptFunction, this.messages.providerAndConsumerOnlyInStruct, PresetDecorators.CONSUMER); + this.validateDecorator( + member.scriptFunction, this.messages.providerAndConsumerOnlyInStruct, PresetDecorators.PROVIDER); + } + }); + return; } - if (arkts.isMethodDefinition(member)) { - this.validateDecorator( - member.scriptFunction, this.messages.providerAndConsumerOnlyInStruct, PresetDecorators.CONSUMER); - this.validateDecorator( - member.scriptFunction, this.messages.providerAndConsumerOnlyInStruct, PresetDecorators.PROVIDER); + + // function/ variable/ interface/ type alias declaration + if (arkts.isFunctionDeclaration(node) || + arkts.isVariableDeclaration(node) || + arkts.isTSInterfaceDeclaration(node) || + arkts.isTSTypeAliasDeclaration(node) + ) { + this.validateDecorator(node, this.messages.providerAndConsumerOnlyInStruct, PresetDecorators.CONSUMER); + this.validateDecorator(node, this.messages.providerAndConsumerOnlyInStruct, PresetDecorators.PROVIDER); + return; } - }); - return; } - // function/ variable/ interface/ type alias declaration - if (arkts.isFunctionDeclaration(node) || - arkts.isVariableDeclaration(node) || - arkts.isTSInterfaceDeclaration(node) || - arkts.isTSTypeAliasDeclaration(node) - ) { - this.validateDecorator(node, this.messages.providerAndConsumerOnlyInStruct, PresetDecorators.CONSUMER); - this.validateDecorator(node, this.messages.providerAndConsumerOnlyInStruct, PresetDecorators.PROVIDER); - return; - } - } + private validateDecorator( + node: arkts.ClassProperty | arkts.VariableDeclaration | arkts.FunctionDeclaration | + arkts.ScriptFunction | arkts.TSInterfaceDeclaration | arkts.TSTypeAliasDeclaration, + message: string, + decoratorName: string + ): void { + const decorator = this.findDecorator(node, decoratorName); + if (!decorator) { + return; + } - private validateDecorator( - node: arkts.ClassProperty | arkts.VariableDeclaration | arkts.FunctionDeclaration | - arkts.ScriptFunction | arkts.TSInterfaceDeclaration | arkts.TSTypeAliasDeclaration, - message: string, - decoratorName: string - ): void { - const decorator = this.findDecorator(node, decoratorName); - if (!decorator) { - return; + this.report({ + node: decorator, + message: message, + data: { + decorator: getAnnotationName(decorator), + }, + fix: (decorator) => { + let startPosition = decorator.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + const endPosition = decorator.endPosition; + return { + range: [startPosition, endPosition], + code: '', + }; + } + }); } - this.report({ - node: decorator, - message: message, - data: { - decorator: getAnnotationName(decorator), - }, - fix: (decorator) => { - const startPosition = decorator.startPosition; - const endPosition = decorator.endPosition; - return { - range: [startPosition, endPosition], - code: '', - }; - } - }); - } - - private validateConsumerInitialization(node: arkts.CallExpression): void { - if (!arkts.isIdentifier(node.expression)) { - return; - } - const callExpName: string = node.expression.name; - if (this.componentV2WithConsumer.has(callExpName)) { - const queue: Array = [node]; - while (queue.length > 0) { - const currentNode: arkts.AstNode = queue.shift() as arkts.AstNode; - if (arkts.isIdentifier(currentNode)) { - this.checkInvalidConsumerUsage(currentNode, callExpName); + private validateConsumerInitialization(node: arkts.CallExpression): void { + if (!arkts.isIdentifier(node.expression)) { + return; } - const children = currentNode.getChildren(); - for (const child of children) { - queue.push(child); + const callExpName: string = node.expression.name; + if (this.componentV2WithConsumer.has(callExpName)) { + const queue: Array = [node]; + while (queue.length > 0) { + const currentNode: arkts.AstNode = queue.shift() as arkts.AstNode; + if (arkts.isIdentifier(currentNode)) { + this.checkInvalidConsumerUsage(currentNode, callExpName); + } + const children = currentNode.getChildren(); + for (const child of children) { + queue.push(child); + } + } } - } } - } - private validateProviderInitialization(node: arkts.CallExpression): void { - if (!arkts.isIdentifier(node.expression)) { - return; - } - const callExpName: string = node.expression.name; - if (this.componentV2WithProvider.has(callExpName)) { - const queue: Array = [node]; - while (queue.length > 0) { - const currentNode: arkts.AstNode = queue.shift() as arkts.AstNode; - if (arkts.isIdentifier(currentNode)) { - this.checkInvalidProviderUsage(currentNode, callExpName); + private validateProviderInitialization(node: arkts.CallExpression): void { + if (!arkts.isIdentifier(node.expression)) { + return; } - const children = currentNode.getChildren(); - for (const child of children) { - queue.push(child); + const callExpName: string = node.expression.name; + if (this.componentV2WithProvider.has(callExpName)) { + const queue: Array = [node]; + while (queue.length > 0) { + const currentNode: arkts.AstNode = queue.shift() as arkts.AstNode; + if (arkts.isIdentifier(currentNode)) { + this.checkInvalidProviderUsage(currentNode, callExpName); + } + const children = currentNode.getChildren(); + for (const child of children) { + queue.push(child); + } + } } - } } - } - private checkInvalidConsumerUsage(currentNode: arkts.Identifier, callExpName: string): void { - const parent = currentNode.parent; - if (parent && this.componentV2WithConsumer.get(callExpName).includes(getIdentifierName(currentNode))) { - this.report({ - node: parent, - message: this.messages.forbiddenInitialization, - data: { - decorator: PresetDecorators.CONSUMER, - value: getIdentifierName(currentNode), - structName: callExpName - }, - fix: () => { - const startPosition = parent.startPosition; - const endPosition = parent.endPosition; - return { - range: [startPosition, endPosition], - code: '', - }; + private checkInvalidConsumerUsage(currentNode: arkts.Identifier, callExpName: string): void { + const parent = currentNode.parent; + if (parent && this.componentV2WithConsumer.get(callExpName).includes(getIdentifierName(currentNode))) { + this.report({ + node: parent, + message: this.messages.forbiddenInitialization, + data: { + decorator: PresetDecorators.CONSUMER, + value: getIdentifierName(currentNode), + structName: callExpName + }, + fix: () => { + const startPosition = parent.startPosition; + const endPosition = parent.endPosition; + return { + range: [startPosition, endPosition], + code: '', + }; + } + }); } - }); } - } - private checkInvalidProviderUsage(currentNode: arkts.Identifier, callExpName: string): void { - const parent = currentNode.parent; - if (parent && this.componentV2WithProvider.get(callExpName)?.includes(getIdentifierName(currentNode))) { - this.report({ - node: parent, - message: this.messages.forbiddenInitialization, - data: { - decorator: PresetDecorators.PROVIDER, - value: getIdentifierName(currentNode), - structName: callExpName - }, - fix: () => { - const startPosition = parent.startPosition; - const endPosition = parent.endPosition; - return { - range: [startPosition, endPosition], - code: '', - }; + private checkInvalidProviderUsage(currentNode: arkts.Identifier, callExpName: string): void { + const parent = currentNode.parent; + if (parent && this.componentV2WithProvider.get(callExpName)?.includes(getIdentifierName(currentNode))) { + this.report({ + node: parent, + message: this.messages.forbiddenInitialization, + data: { + decorator: PresetDecorators.PROVIDER, + value: getIdentifierName(currentNode), + structName: callExpName + }, + fix: () => { + const startPosition = parent.startPosition; + const endPosition = parent.endPosition; + return { + range: [startPosition, endPosition], + code: '', + }; + } + }); } - }); } - } } -export default ConsumerProviderDecoratorCheckRule; - +export default ConsumerProviderDecoratorCheckRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/monitor-decorator-check.ts b/arkui-plugins/ui-syntax-plugins/rules/monitor-decorator-check.ts index 47c3053d5..151ce78fb 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/monitor-decorator-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/monitor-decorator-check.ts @@ -14,212 +14,214 @@ */ import * as arkts from '@koalaui/libarkts'; -import { PresetDecorators } from '../utils'; +import { getAnnotationUsage, PresetDecorators, getClassAnnotationUsage } from '../utils'; import { AbstractUISyntaxRule } from './ui-syntax-rule'; -const MONITOR_COUNT_LIMIT = 1; - class MonitorDecoratorCheckRule extends AbstractUISyntaxRule { - public setup(): Record { - return { - monitorUsedAlone: - `The member property or method can not be decorated by multiple built-in annotations.`, - monitorUsedInObservedV2Class: - `The '@Monitor' can decorate only member method within a 'class' decorated with @ObservedV2.`, - monitorUsedInComponentV2Struct: - `The '@Monitor' annotation can only be used in a 'struct' decorated with '@ComponentV2'.`, - monitorDecorateMethod: - `@Monitor can only decorate method.`, - duplicatedMonitor: `Duplicate annotations for method are not allowed.`, - }; - } - - public parsed(node: arkts.AstNode): void { - if (!arkts.isClassDeclaration(node) && !arkts.isStructDeclaration(node)) { - return; + public setup(): Record { + return { + monitorUsedAlone: + `The member property or method can not be decorated by multiple built-in annotations.`, + monitorUsedInObservedV2Class: + `The '@Monitor' can decorate only member method within a 'class' decorated with @ObservedV2.`, + monitorUsedInComponentV2Struct: + `The '@Monitor' annotation can only be used in a 'struct' decorated with '@ComponentV2'.`, + monitorDecorateMethod: + `@Monitor can only decorate method.` + }; } - let monitorUsed = false; + public parsed(node: arkts.AstNode): void { + if (!arkts.isClassDeclaration(node) && !arkts.isStructDeclaration(node)) { + return; + } - const isObservedV2 = arkts.isClassDeclaration(node) && this.checkIfClassIsObservedV2(node); - const isComponentV2 = arkts.isStructDeclaration(node) && this.checkIfStructIsComponentV2(node); + const monitorDecorator = this.checkMonitorUsage(node); + if (monitorDecorator && arkts.isClassDeclaration(node)) { + this.checkMonitorInClass(node, monitorDecorator); + } + + if (monitorDecorator && arkts.isStructDeclaration(node)) { + this.checkMonitorInStruct(node, monitorDecorator); + } + this.checkDecorateMethod(node); + } - monitorUsed = this.checkMultipleDecorators(node); - // Check for errors related to @Monitor usage - if (monitorUsed && !isObservedV2 && arkts.isClassDeclaration(node)) { - this.checkInvalidUsage( - node, - this.messages.monitorUsedInObservedV2Class, - `@${PresetDecorators.OBSERVED_V2}` - ); + private checkMonitorInClass( + node: arkts.ClassDeclaration, + monitorDecorator: arkts.AnnotationUsage | undefined, + ): void { + if (!monitorDecorator) { + return; + } + const isObservedV2 = this.checkDecorator(node, PresetDecorators.OBSERVED_V2); + const observedV1Decorator = getClassAnnotationUsage(node, PresetDecorators.OBSERVED_V1); + + if (!isObservedV2 && !observedV1Decorator) { + this.report({ + node: monitorDecorator, + message: this.messages.monitorUsedInObservedV2Class, + fix: () => { + return { + range: [node.startPosition, node.startPosition], + code: `@${PresetDecorators.OBSERVED_V2}\n` + }; + } + }); + return; + } + if (!isObservedV2 && observedV1Decorator) { + this.report({ + node: monitorDecorator, + message: this.messages.monitorUsedInObservedV2Class, + fix: () => { + let startPosition = observedV1Decorator.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + return { + range: [startPosition, observedV1Decorator.endPosition], + code: `@${PresetDecorators.OBSERVED_V2}` + }; + } + }); + } } - if (monitorUsed && !isComponentV2 && arkts.isStructDeclaration(node)) { - this.checkInvalidUsage( - node, - this.messages.monitorUsedInComponentV2Struct, - `@${PresetDecorators.COMPONENT_V2}\n` - ); + + private checkMonitorInStruct( + node: arkts.StructDeclaration, + monitorDecorator: arkts.AnnotationUsage | undefined, + ): void { + if (!monitorDecorator) { + return; + } + const componentV1Decorator = getAnnotationUsage(node, PresetDecorators.COMPONENT_V1); + const isComponentV2 = this.checkDecorator(node, PresetDecorators.COMPONENT_V2); + if (!isComponentV2 && !componentV1Decorator) { + this.report({ + node: monitorDecorator, + message: this.messages.monitorUsedInComponentV2Struct, + fix: () => ({ + range: [node.startPosition, node.startPosition], + code: `@${PresetDecorators.COMPONENT_V2}\n` + }) + }); + return; + } + + if (!isComponentV2 && componentV1Decorator) { + this.report({ + node: monitorDecorator, + message: this.messages.monitorUsedInComponentV2Struct, + fix: () => { + let startPosition = componentV1Decorator.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + return { + range: [startPosition, componentV1Decorator.endPosition], + code: `@${PresetDecorators.COMPONENT_V2}` + }; + } + }); + } } - this.checkDecorateMethod(node); - } - private checkIfClassIsObservedV2(node: arkts.ClassDeclaration): boolean { - return node.definition?.annotations?.some( - observedV2 => observedV2.expr && arkts.isIdentifier(observedV2.expr) && - observedV2.expr?.name === PresetDecorators.OBSERVED_V2 - ) ?? false; - - } - - private checkIfStructIsComponentV2(node: arkts.StructDeclaration): boolean { - return node.definition.annotations?.some( - componentV2 => componentV2.expr && arkts.isIdentifier(componentV2.expr) && - componentV2.expr?.name === PresetDecorators.COMPONENT_V2 - ) ?? false; - } - - private checkMultipleDecorators( - node: arkts.ClassDeclaration | arkts.StructDeclaration - ): boolean { - // Traverse body of the class to check for @Monitor usage - let monitorUsed: boolean = false; - - node.definition?.body.forEach((body) => { - if (!arkts.isMethodDefinition(body)) { - return; - } - - const duplicatedMonitor = this.getMonitor(body); - const localMonitorUsed = this.getLocalMonitorUsed(body); - - if (duplicatedMonitor.length > MONITOR_COUNT_LIMIT && localMonitorUsed) { - this.reportDuplicatedMonitor(localMonitorUsed); - } - - if (localMonitorUsed) { - monitorUsed = true; - this.checkConflictingDecorators(body, localMonitorUsed); - return; // Stop further checks for this method - } + + private checkDecorator(node: arkts.ClassDeclaration | arkts.StructDeclaration, decoratorName: string): boolean { + return node.definition?.annotations?.some( + annotation => annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr?.name === decoratorName + ) ?? false; } - ); - - return monitorUsed; - } - - private getMonitor(body: arkts.MethodDefinition): arkts.AnnotationUsage[] { - const monitor = body.scriptFunction.annotations?.filter( - annotation => annotation.expr && arkts.isIdentifier(annotation.expr) && - annotation.expr.name === PresetDecorators.MONITOR - ); - return monitor; - } - - private getLocalMonitorUsed(body: arkts.MethodDefinition): arkts.AnnotationUsage | undefined { - const localMonitorUsed = body.scriptFunction.annotations?.find( - annotation => annotation.expr && arkts.isIdentifier(annotation.expr) && - annotation.expr.name === PresetDecorators.MONITOR - ); - return localMonitorUsed; - } - - private checkConflictingDecorators(body: arkts.MethodDefinition, localMonitorUsed: arkts.AnnotationUsage): boolean { - const conflictingDecorators = body.scriptFunction.annotations?.filter( - annotation => annotation.expr && arkts.isIdentifier(annotation.expr) && - annotation.expr.name !== PresetDecorators.MONITOR - ); - if (conflictingDecorators?.length > 0) { - this.reportConflictingDecorators(localMonitorUsed, conflictingDecorators); - return true; + + private checkMonitorUsage( + node: arkts.ClassDeclaration | arkts.StructDeclaration + ): arkts.AnnotationUsage | undefined { + let monitorUsage: arkts.AnnotationUsage | undefined; + + for (const body of node.definition?.body ?? []) { + if (!arkts.isMethodDefinition(body)) { + continue; + } + const currentMonitor = this.getLocalMonitorUsed(body); + + if (currentMonitor) { + monitorUsage = currentMonitor; + this.checkConflictingDecorators(body, currentMonitor); + break; + } + } + return monitorUsage; + } + + private getLocalMonitorUsed(body: arkts.MethodDefinition): arkts.AnnotationUsage | undefined { + const localMonitorUsed = body.scriptFunction.annotations?.find( + annotation => annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr.name === PresetDecorators.MONITOR + ); + return localMonitorUsed; + } + + private checkConflictingDecorators(body: arkts.MethodDefinition, localMonitorUsed: arkts.AnnotationUsage): boolean { + const conflictingDecorators = body.scriptFunction.annotations?.filter( + annotation => annotation.expr && arkts.isIdentifier(annotation.expr) && + annotation.expr.name !== PresetDecorators.MONITOR + ); + if (conflictingDecorators?.length > 0) { + this.reportConflictingDecorators(localMonitorUsed, conflictingDecorators); + return true; + } + return false; + } + + private reportConflictingDecorators(localMonitorUsed: arkts.AstNode, conflictingDecorators: arkts.AnnotationUsage[]): void { + this.report({ + node: localMonitorUsed, + message: this.messages.monitorUsedAlone, + fix: () => { + const startPositions = conflictingDecorators.map(annotation => + annotation.startPosition); + const endPositions = conflictingDecorators.map(annotation => annotation.endPosition); + let startPosition = startPositions[0]; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + const endPosition = endPositions[endPositions.length - 1]; + return { + range: [startPosition, endPosition], + code: '' + }; + } + }); + } + + private checkDecorateMethod(node: arkts.ClassDeclaration | arkts.StructDeclaration): void { + // Check if @Monitor is used on a property (which is not allowed) + node.definition?.body.forEach((body) => { + if (!arkts.isClassProperty(body)) { + return; + } + + const monitorDecorator = body.annotations?.find( + (annotation) => + annotation.expr && + arkts.isIdentifier(annotation.expr) && + annotation.expr.name === PresetDecorators.MONITOR + ); + + if (monitorDecorator === undefined) { + return; + } + this.report({ + node: monitorDecorator, + message: this.messages.monitorDecorateMethod, + fix: () => { + let startPosition = monitorDecorator.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + const endPosition = monitorDecorator.endPosition; + return { + range: [startPosition, endPosition], + code: '', + }; + }, + }); + }); } - return false; - } - - private reportConflictingDecorators(localMonitorUsed: arkts.AstNode, conflictingDecorators: arkts.AnnotationUsage[]): void { - this.report({ - node: localMonitorUsed, - message: this.messages.monitorUsedAlone, - fix: () => { - const startPositions = conflictingDecorators.map(annotation => - annotation.startPosition); - const endPositions = conflictingDecorators.map(annotation => annotation.endPosition); - const startPosition = startPositions[0]; - const endPosition = endPositions[endPositions.length - 1]; - return { - range: [startPosition, endPosition], - code: '' - }; - } - }); - } - - private reportDuplicatedMonitor(localMonitorUsed: arkts.AstNode): void { - this.report({ - node: localMonitorUsed, - message: this.messages.duplicatedMonitor, - fix: () => { - const startPosition = localMonitorUsed.startPosition; - const endPosition = localMonitorUsed.endPosition; - return { - range: [startPosition, endPosition], - code: '' - }; - } - }); - } - - private checkInvalidUsage(node: arkts.ClassDeclaration | arkts.StructDeclaration, message: string, fixCode: string): void { - let monitor: arkts.AnnotationUsage | undefined; - node.definition?.body.forEach((body) => { - if (!arkts.isMethodDefinition(body)) { - return; - } - monitor = this.getLocalMonitorUsed(body); - if (!monitor) { - return; - } - this.report({ - node: monitor, - message, - fix: () => ({ - range: [node.startPosition, node.startPosition], - code: fixCode - }) - }); - }); - } - - private checkDecorateMethod(node: arkts.ClassDeclaration | arkts.StructDeclaration): void { - // Check if @Monitor is used on a property (which is not allowed) - node.definition?.body.forEach((body) => { - if (!arkts.isClassProperty(body)) { - return; - } - - const monitorDecorator = body.annotations?.find( - (annotation) => - annotation.expr && - arkts.isIdentifier(annotation.expr) && - annotation.expr.name === PresetDecorators.MONITOR - ); - - if (monitorDecorator === undefined) { - return; - } - this.report({ - node: monitorDecorator, - message: this.messages.monitorDecorateMethod, - fix: () => { - const startPosition = monitorDecorator.startPosition; - const endPosition = monitorDecorator.endPosition; - return { - range: [startPosition, endPosition], - code: '', - }; - }, - }); - }); - } } export default MonitorDecoratorCheckRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-entry.ts b/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-entry.ts index fae7ecc97..0c2f21926 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-entry.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-entry.ts @@ -46,24 +46,34 @@ class NoDuplicateEntryRule extends AbstractUISyntaxRule { } if (this.entryDecoratorUsageIndex === MAX_ENTRY_DECORATOR_COUNT) { const entryDecoratorUsage = this.entryDecoratorUsages.at(0)!; + if (!entryDecoratorUsage) { + return; + } + let startPosition = entryDecoratorUsage.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); this.report({ node: entryDecoratorUsage, message: this.messages.duplicateEntry, fix: () => { return { - range: [entryDecoratorUsage.startPosition, entryDecoratorUsage.endPosition], + range: [startPosition, entryDecoratorUsage.endPosition], code: '', }; }, }); } entryDecoratorUsage = this.entryDecoratorUsages.at(this.entryDecoratorUsageIndex)!; + if (!entryDecoratorUsage) { + return; + } + let startPosition = entryDecoratorUsage.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); this.report({ node: entryDecoratorUsage, message: this.messages.duplicateEntry, fix: () => { return { - range: [entryDecoratorUsage.startPosition, entryDecoratorUsage.endPosition], + range: [startPosition, entryDecoratorUsage.endPosition], code: '', }; }, diff --git a/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-id.ts b/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-id.ts index 5b0470e8e..b33790e75 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-id.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-id.ts @@ -41,7 +41,6 @@ class NoDuplicateIdRule extends AbstractUISyntaxRule { node: arkts.BlockStatement, usedIds: Map ): void { - node.statements.forEach((statement) => { if ( arkts.isExpressionStatement(statement) && @@ -53,35 +52,30 @@ class NoDuplicateIdRule extends AbstractUISyntaxRule { } private findDuplicateComponentIds(node: arkts.CallExpression, usedIds: Map): void { - const idInfos = this.getAllIdParams(node, []); - for (const idInfo of idInfos) { - const value = idInfo.value; - const idNode = idInfo.node; - if (usedIds.has(value)) { - this.report({ - node: idNode, - message: this.messages.duplicateId, - data: { - id: idInfo.value, - path: this.getPath() ?? '', - line: idNode.startPosition.line().toString(), - index: idNode.startPosition.index().toString() - } - }); - } else { - // Otherwise, record it - usedIds.set(value, idInfo); - } + const idInfo = this.getIdInfo(node); + if (!idInfo) { + return; + } + if (usedIds.has(idInfo.value)) { + this.report({ + node: idInfo.node, + message: this.messages.duplicateId, + data: { + id: idInfo.value, + path: this.getPath(node) ?? '', + line: idInfo.node.startPosition.line().toString(), + index: idInfo.node.startPosition.index().toString() + } + }); + } else { + // Otherwise, record it + usedIds.set(idInfo.value, idInfo); } } - private getPath(): string | undefined { - const contextPtr = arkts.arktsGlobal.compilerContext?.peer; - if (!!contextPtr) { - let program = arkts.getOrUpdateGlobalContext(contextPtr).program; - return program.globalAbsName; - } - return undefined; + private getPath(node: arkts.AstNode): string | undefined { + const program = arkts.getProgramFromAstNode(node); + return program.absName; } private getIdInfo(node: arkts.CallExpression): IdInfo | undefined { @@ -102,28 +96,6 @@ class NoDuplicateIdRule extends AbstractUISyntaxRule { } return { value, node: property }; } - - private getAllIdParams(node: arkts.AstNode, results: IdInfo[]): IdInfo[] { - if (arkts.isCallExpression(node)) { - const idInfo = this.getIdInfo(node); - if (idInfo) { - results.push(idInfo); - } - - // Just continue to check if the callee that calls the expression is a chain call (e.g. with .id()) - const callee = node.expression; - if (arkts.isMemberExpression(callee)) { - const object = callee.object; - if (arkts.isCallExpression(object)) { - this.getAllIdParams(object, results); - } - } - } - - return results.sort((a, b) => { - return a.node.startPosition.index() - b.node.startPosition.index(); - }); - } } export default NoDuplicateIdRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-preview.ts b/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-preview.ts index 746f5dec3..75c0d847e 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-preview.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-preview.ts @@ -62,12 +62,14 @@ class NoDuplicatePreviewRule extends AbstractUISyntaxRule { } private reportError(errorNode: arkts.AnnotationUsage): void { + let startPosition = errorNode.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); this.report({ node: errorNode, message: this.messages.duplicateEntry, fix: () => { return { - range: [errorNode.startPosition, errorNode.endPosition], + range: [startPosition, errorNode.endPosition], code: '', }; } diff --git a/arkui-plugins/ui-syntax-plugins/rules/no-prop-link-objectlink-in-entry.ts b/arkui-plugins/ui-syntax-plugins/rules/no-prop-link-objectlink-in-entry.ts index 03eb874d5..7d0b98c83 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/no-prop-link-objectlink-in-entry.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/no-prop-link-objectlink-in-entry.ts @@ -71,8 +71,9 @@ class NoPropLinkObjectLinkInEntryRule extends AbstractUISyntaxRule { propertyName, }, fix: (annotation) => { - const startPosition = annotation.startPosition; - const endPosition = annotation.endPosition; + let startPosition = annotation.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + let endPosition = annotation.endPosition; return { range: [startPosition, endPosition], code: '', diff --git a/arkui-plugins/ui-syntax-plugins/rules/observedV2-trace-usage-validation.ts b/arkui-plugins/ui-syntax-plugins/rules/observedV2-trace-usage-validation.ts index 9a0ca19e1..c1b3a827c 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/observedV2-trace-usage-validation.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/observedV2-trace-usage-validation.ts @@ -48,8 +48,9 @@ class ObservedV2TraceUsageValidationRule extends AbstractUISyntaxRule { node: observedV2Decorator, message: this.messages.observedV2DecoratorError, fix: (observedV2Decorator) => { - const startPosition = observedV2Decorator.startPosition; - const endPosition = observedV2Decorator.endPosition; + let startPosition = observedV2Decorator.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + let endPosition = observedV2Decorator.endPosition; return { range: [startPosition, endPosition], code: '', @@ -64,8 +65,9 @@ class ObservedV2TraceUsageValidationRule extends AbstractUISyntaxRule { node: traceDecorator, message: this.messages.traceMemberVariableError, fix: (traceDecorator) => { - const startPosition = traceDecorator.startPosition; - const endPosition = traceDecorator.endPosition; + let startPosition = traceDecorator.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + let endPosition = traceDecorator.endPosition; return { range: [startPosition, endPosition], code: '', @@ -100,8 +102,9 @@ class ObservedV2TraceUsageValidationRule extends AbstractUISyntaxRule { node: traceDecorator, message: this.messages.traceDecoratorError, fix: (traceDecorator) => { - const startPosition = traceDecorator.startPosition; - const endPosition = traceDecorator.endPosition; + let startPosition = traceDecorator.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + let endPosition = traceDecorator.endPosition; return { range: [startPosition, endPosition], code: '', @@ -135,7 +138,7 @@ class ObservedV2TraceUsageValidationRule extends AbstractUISyntaxRule { const endPosition = observedDecorator.endPosition; return { range: [startPosition, endPosition], - code: `@${PresetDecorators.OBSERVED_V2}`, + code: `${PresetDecorators.OBSERVED_V2}`, }; }, }); diff --git a/arkui-plugins/ui-syntax-plugins/rules/old-new-decorator-mix-use-check.ts b/arkui-plugins/ui-syntax-plugins/rules/old-new-decorator-mix-use-check.ts index 5726d15cd..704eb038b 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/old-new-decorator-mix-use-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/old-new-decorator-mix-use-check.ts @@ -101,7 +101,7 @@ class OldNewDecoratorMixUseCheckRule extends AbstractUISyntaxRule { fix: () => { return { range: [hasComponentV2Decorator.startPosition, hasComponentV2Decorator.endPosition], - code: `@${structDecoratorName}`, + code: structDecoratorName, }; }, }); diff --git a/arkui-plugins/ui-syntax-plugins/rules/once-decorator-check.ts b/arkui-plugins/ui-syntax-plugins/rules/once-decorator-check.ts index cd59caad8..a8465549a 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/once-decorator-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/once-decorator-check.ts @@ -42,7 +42,6 @@ class OnceDecoratorCheckRule extends AbstractUISyntaxRule { } private validateOnlyInStruct(node: arkts.AstNode): void { - if (arkts.isClassDeclaration(node)) { node.definition?.body.forEach(member => { if (arkts.isClassProperty(member)) { @@ -80,7 +79,8 @@ class OnceDecoratorCheckRule extends AbstractUISyntaxRule { node: decorator, message: message, fix: (decorator) => { - const startPosition = decorator.startPosition; + let startPosition = decorator.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); const endPosition = decorator.endPosition; return { range: [startPosition, endPosition], @@ -200,7 +200,8 @@ class OnceDecoratorCheckRule extends AbstractUISyntaxRule { message: this.messages.invalidUsage, fix: () => { if (componentDecoratorUsage) { - const startPosition = componentDecoratorUsage.startPosition; + let startPosition = componentDecoratorUsage.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); const endPosition = componentDecoratorUsage.endPosition; return { range: [startPosition, endPosition], diff --git a/arkui-plugins/ui-syntax-plugins/rules/one-decorator-on-function-method.ts b/arkui-plugins/ui-syntax-plugins/rules/one-decorator-on-function-method.ts index f31cfec4a..69c7eb7ac 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/one-decorator-on-function-method.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/one-decorator-on-function-method.ts @@ -107,7 +107,8 @@ class OneDecoratorOnFunctionMethodRule extends AbstractUISyntaxRule { node: annotation, message: this.messages.invalidDecorator, fix: () => { - const startPosition = otherDecorator.startPosition; + let startPosition = otherDecorator.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); const endPosition = otherDecorator.endPosition; return { range: [startPosition, endPosition], diff --git a/arkui-plugins/ui-syntax-plugins/rules/property-type.ts b/arkui-plugins/ui-syntax-plugins/rules/property-type.ts index 1c70f2d39..04b2b3da1 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/property-type.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/property-type.ts @@ -52,7 +52,7 @@ const SimpleTypesUnSupported = [ const ARRAY_TYPES = ['Array', 'Map', 'Set', 'Date']; -const PropErrorType = ['any', 'unknown']; +const PropErrorType = ['Any', 'unknown']; class PropertyTypeRule extends AbstractUISyntaxRule { private noObservedV2ClassNames: string[] = []; @@ -323,6 +323,7 @@ class PropertyTypeRule extends AbstractUISyntaxRule { return; } node.getChildren().forEach((member) => { + // Only the situation within the component is judged if (!arkts.isStructDeclaration(member) || !member.definition.ident) { return; } diff --git a/arkui-plugins/ui-syntax-plugins/rules/track-decorator-check.ts b/arkui-plugins/ui-syntax-plugins/rules/track-decorator-check.ts index 9178bfafc..ec80d3afd 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/track-decorator-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/track-decorator-check.ts @@ -15,7 +15,7 @@ import * as arkts from '@koalaui/libarkts'; import { AbstractUISyntaxRule } from './ui-syntax-rule'; -import { PresetDecorators, findDecorator, getClassDeclarationAnnotation } from '../utils/index'; +import { PresetDecorators, findDecorator, getClassDeclarationAnnotation, getAnnotationUsage } from '../utils/index'; class TrackDecoratorCheckRule extends AbstractUISyntaxRule { public setup(): Record { @@ -52,6 +52,10 @@ class TrackDecoratorCheckRule extends AbstractUISyntaxRule { } private checkInvalidTrackAnnotations(node: arkts.StructDeclaration): void { + const trackAnnotation = getAnnotationUsage(node, PresetDecorators.TRACK); + if (trackAnnotation) { + this.reportInvalidTarget(trackAnnotation); + } // Traverse all members of the struct body node.definition.body.forEach((member) => { // Check whether it is a member variable @@ -74,8 +78,9 @@ class TrackDecoratorCheckRule extends AbstractUISyntaxRule { } private checkTrackUsedWithObservedV2(node: arkts.ClassDeclaration): void { - // Check if the class is decorated with @Observed + // Check if the struct is decorated with @Observed const observedV2Decorator = getClassDeclarationAnnotation(node, PresetDecorators.OBSERVED_V2); + // Traverse all members of the body class node.definition?.body.forEach((member) => { // Check whether it is a class attribute @@ -105,8 +110,9 @@ class TrackDecoratorCheckRule extends AbstractUISyntaxRule { node: trackDecorator, message: this.messages.trackMustUsedWithObserved, fix: (trackDecorator) => { - const startPosition = trackDecorator.startPosition; - const endPosition = trackDecorator.endPosition; + let startPosition = trackDecorator.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + let endPosition = trackDecorator.endPosition; return { range: [startPosition, endPosition], code: '', @@ -120,8 +126,9 @@ class TrackDecoratorCheckRule extends AbstractUISyntaxRule { node: node, message: this.messages.trackOnClassMemberOnly, fix: (node) => { - const startPosition = node.startPosition; - const endPosition = node.endPosition; + let startPosition = node.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); + let endPosition = node.endPosition; return { range: [startPosition, endPosition], code: '', -- Gitee