From 235776aebb57b99abf8dbea6b270098d9408256b Mon Sep 17 00:00:00 2001 From: wangweiyuan Date: Tue, 12 Aug 2025 17:11:44 +0800 Subject: [PATCH] ui-syntax-plugins cherry-pick to 0728 Signed-off-by: wangweiyuan --- .../rules/build-root-node.ts | 5 +- .../componentV2-state-usage-validation.ts | 2 + .../rules/computed-decorator-check.ts | 17 +-- .../consumer-provider-decorator-check.ts | 6 +- .../ui-syntax-plugins/rules/index.ts | 4 +- .../rules/monitor-decorator-check.ts | 6 +- .../rules/no-duplicate-preview.ts | 103 ++++++++-------- .../rules/no-prop-link-objectlink-in-entry.ts | 1 + .../observedV2-trace-usage-validation.ts | 3 + .../rules/once-decorator-check.ts | 49 ++------ .../rules/one-decorator-on-function-method.ts | 3 +- .../ui-syntax-plugins/rules/property-type.ts | 28 ----- .../rules/static-param-require.ts | 110 ++++++++++++++++++ .../rules/struct-property-decorator.ts | 27 +++-- .../rules/struct-variable-initialization.ts | 12 -- .../rules/track-decorator-check.ts | 2 + 16 files changed, 216 insertions(+), 162 deletions(-) create mode 100644 arkui-plugins/ui-syntax-plugins/rules/static-param-require.ts diff --git a/arkui-plugins/ui-syntax-plugins/rules/build-root-node.ts b/arkui-plugins/ui-syntax-plugins/rules/build-root-node.ts index a914891f9..10cb57d43 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/build-root-node.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/build-root-node.ts @@ -103,7 +103,7 @@ class BuildRootNodeRule extends AbstractUISyntaxRule { } private isContainerComponent(componentName: string): boolean { - const loadedContainerComponents = this.context.componentsInfo.containerComponents; + const loadedContainerComponents = this.context.componentsInfo?.containerComponents; if (!componentName || !loadedContainerComponents) { return false; } @@ -135,5 +135,4 @@ class BuildRootNodeRule extends AbstractUISyntaxRule { } } - -export default BuildRootNodeRule; +export default BuildRootNodeRule; \ No newline at end of file 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 2f62ea5af..f77421dd0 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 @@ -116,6 +116,7 @@ class ComponentV2StateUsageValidationRule extends AbstractUISyntaxRule { 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], @@ -150,6 +151,7 @@ class ComponentV2StateUsageValidationRule extends AbstractUISyntaxRule { 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], 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 2a1d477ab..9caa5e6e7 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/computed-decorator-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/computed-decorator-check.ts @@ -80,7 +80,8 @@ class ComputedDecoratorCheckRule extends AbstractUISyntaxRule { node: computedDecorator, message: this.messages.onlyOnGetter, fix: (computedDecorator) => { - const startPosition = computedDecorator.startPosition; + let startPosition = computedDecorator.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); const endPosition = computedDecorator.endPosition; return { range: [startPosition, endPosition], @@ -114,7 +115,8 @@ class ComputedDecoratorCheckRule extends AbstractUISyntaxRule { node: computedDecorator, message: this.messages.onlyOnGetter, fix: (computedDecorator) => { - const startPosition = computedDecorator.startPosition; + let startPosition = computedDecorator.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); const endPosition = computedDecorator.endPosition; return { range: [startPosition, endPosition], @@ -152,28 +154,19 @@ class ComputedDecoratorCheckRule extends AbstractUISyntaxRule { currentNode.arguments.forEach((argument) => { if (arkts.isMemberExpression(argument)) { const getterName = getIdentifierName(argument.property); - this.reportValidateCallExpression(currentNode, argument, getterName); + 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(), - }; - }, }); } } 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 9f56e3915..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 @@ -144,7 +144,8 @@ class ConsumerProviderDecoratorCheckRule extends AbstractUISyntaxRule { decorator: getAnnotationName(decorator) }, fix: () => { - const startPosition = otherDecorators.startPosition; + let startPosition = otherDecorators.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); const endPosition = otherDecorators.endPosition; return { range: [startPosition, endPosition], @@ -219,7 +220,8 @@ class ConsumerProviderDecoratorCheckRule extends AbstractUISyntaxRule { decorator: getAnnotationName(decorator), }, 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], diff --git a/arkui-plugins/ui-syntax-plugins/rules/index.ts b/arkui-plugins/ui-syntax-plugins/rules/index.ts index 0757b96b1..6fdab3812 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/index.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/index.ts @@ -41,6 +41,7 @@ import NoDuplicatePreviewRule from './no-duplicate-preview'; import NoPropLinkObjectLinkInEntryRule from './no-prop-link-objectlink-in-entry'; import NoSameAsBuiltInAttributeRule from './no-same-as-built-in-attribute'; import ReuseAttributeCheckRule from './reuse-attribute-check'; +import StaticParamRequireRule from './static-param-require'; import StructMissingDecoratorRule from './struct-missing-decorator'; import StructPropertyDecoratorRule from './struct-property-decorator'; import TrackDecoratorCheckRule from './track-decorator-check'; @@ -94,6 +95,7 @@ const rules: Array = [ [NoPropLinkObjectLinkInEntryRule, 'warn'], [NoSameAsBuiltInAttributeRule, 'error'], [ReuseAttributeCheckRule, 'error'], + [StaticParamRequireRule, 'warn'], [StructMissingDecoratorRule, 'error'], [StructPropertyDecoratorRule, 'error'], [TrackDecoratorCheckRule, 'error'], @@ -117,7 +119,7 @@ const rules: Array = [ [WatchDecoratorFunctionRule, 'error'], [WatchDecoratorRegularRule, 'error'], [OldNewDecoratorMixUseCheckRule, 'error'], - [RequireDecoratorRegularRule, 'error'], + [RequireDecoratorRegularRule, 'warn'], [ReusableComponentInV2CheckRule, 'warn'], [SpecificComponentChildrenRule, 'error'], ]; 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 4074a1724..8cbd2a27e 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/monitor-decorator-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/monitor-decorator-check.ts @@ -175,7 +175,8 @@ class MonitorDecoratorCheckRule extends AbstractUISyntaxRule { const startPositions = conflictingDecorators.map(annotation => annotation.startPosition); const endPositions = conflictingDecorators.map(annotation => annotation.endPosition); - const startPosition = startPositions[0]; + let startPosition = startPositions[0]; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); const endPosition = endPositions[endPositions.length - 1]; return { range: [startPosition, endPosition], @@ -206,7 +207,8 @@ class MonitorDecoratorCheckRule extends AbstractUISyntaxRule { node: monitorDecorator, message: this.messages.monitorDecorateMethod, fix: () => { - const startPosition = monitorDecorator.startPosition; + let startPosition = monitorDecorator.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); const endPosition = monitorDecorator.endPosition; return { range: [startPosition, endPosition], 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 4cd553402..0fa2d00c6 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-preview.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-preview.ts @@ -18,62 +18,63 @@ import { getAnnotationUsage, MAX_PREVIEW_DECORATOR_COUNT, PresetDecorators } fro import { AbstractUISyntaxRule } from './ui-syntax-rule'; class NoDuplicatePreviewRule extends AbstractUISyntaxRule { - private previewDecoratorUsages: arkts.AnnotationUsage[] = []; - private previewDecoratorUsageIndex = 10; + private previewDecoratorUsages: arkts.AnnotationUsage[] = []; + private previewDecoratorUsageIndex = 10; - public setup(): Record { - return { - duplicateEntry: `A page can contain at most 10 '@Preview' annotations.`, - }; - } - - public beforeTransform(): void { - this.previewDecoratorUsages = []; - this.previewDecoratorUsageIndex = 10; - } - - public parsed(node: arkts.StructDeclaration): void { - if (!arkts.isStructDeclaration(node)) { - return; - } - const previewDecoratorUsage = getAnnotationUsage( - node, - PresetDecorators.PREVIEW, - ); - if (previewDecoratorUsage) { - this.previewDecoratorUsages.push(previewDecoratorUsage); + public setup(): Record { + return { + duplicateEntry: `A page can contain at most 10 '@Preview' annotations.`, + }; } - // If the number of preview decorators is less than 10, no error is reported - if (this.previewDecoratorUsages.length <= MAX_PREVIEW_DECORATOR_COUNT) { - return; + + public beforeTransform(): void { + this.previewDecoratorUsages = []; + this.previewDecoratorUsageIndex = 10; } - if (this.previewDecoratorUsageIndex === MAX_PREVIEW_DECORATOR_COUNT) { - this.previewDecoratorUsages.forEach((previewDecoratorUsage) => { - this.reportError(previewDecoratorUsage); - }); - } else { - let previewDecoratorUsage = this.previewDecoratorUsages.at(this.previewDecoratorUsageIndex); - if (!previewDecoratorUsage) { - return; - } - this.reportError(previewDecoratorUsage); + + public parsed(node: arkts.StructDeclaration): void { + if (!arkts.isStructDeclaration(node)) { + return; + } + const previewDecoratorUsage = getAnnotationUsage( + node, + PresetDecorators.PREVIEW, + ); + if (previewDecoratorUsage) { + this.previewDecoratorUsages.push(previewDecoratorUsage); + } + // If the number of preview decorators is less than 10, no error is reported + if (this.previewDecoratorUsages.length <= MAX_PREVIEW_DECORATOR_COUNT) { + return; + } + if (this.previewDecoratorUsageIndex === MAX_PREVIEW_DECORATOR_COUNT) { + this.previewDecoratorUsages.forEach((previewDecoratorUsage) => { + this.reportError(previewDecoratorUsage); + }); + } else { + let previewDecoratorUsage = this.previewDecoratorUsages.at(this.previewDecoratorUsageIndex); + if (!previewDecoratorUsage) { + return; + } + this.reportError(previewDecoratorUsage); + } + this.previewDecoratorUsageIndex++; } - this.previewDecoratorUsageIndex++; - } - private reportError(errorNode: arkts.AnnotationUsage): void { - let startPosition = errorNode.startPosition; - this.report({ - node: errorNode, - message: this.messages.duplicateEntry, - fix: () => { - return { - range: [startPosition, errorNode.endPosition], - code: '', - }; - } - }); - } + 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: [startPosition, errorNode.endPosition], + code: '', + }; + } + }); + } } export default NoDuplicatePreviewRule; \ No newline at end of file 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 5cfe3351c..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 @@ -72,6 +72,7 @@ class NoPropLinkObjectLinkInEntryRule extends AbstractUISyntaxRule { }, fix: (annotation) => { let startPosition = annotation.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); let endPosition = annotation.endPosition; return { range: [startPosition, endPosition], 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 c8a7ae084..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 @@ -49,6 +49,7 @@ class ObservedV2TraceUsageValidationRule extends AbstractUISyntaxRule { message: this.messages.observedV2DecoratorError, fix: (observedV2Decorator) => { let startPosition = observedV2Decorator.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); let endPosition = observedV2Decorator.endPosition; return { range: [startPosition, endPosition], @@ -65,6 +66,7 @@ class ObservedV2TraceUsageValidationRule extends AbstractUISyntaxRule { message: this.messages.traceMemberVariableError, fix: (traceDecorator) => { let startPosition = traceDecorator.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); let endPosition = traceDecorator.endPosition; return { range: [startPosition, endPosition], @@ -101,6 +103,7 @@ class ObservedV2TraceUsageValidationRule extends AbstractUISyntaxRule { message: this.messages.traceDecoratorError, fix: (traceDecorator) => { let startPosition = traceDecorator.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); let endPosition = traceDecorator.endPosition; return { range: [startPosition, endPosition], 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 85e8bd8cf..26bec5af1 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/once-decorator-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/once-decorator-check.ts @@ -27,13 +27,12 @@ class OnceDecoratorCheckRule extends AbstractUISyntaxRule { } public parsed(node: arkts.AstNode): void { - let onceDecorator: arkts.AnnotationUsage | undefined; this.validateOnlyInStruct(node); this.validateOnlyOnProperty(node); if (arkts.isStructDeclaration(node)) { - this.validateDecorator(node, onceDecorator); + this.validateDecorator(node); } } @@ -75,7 +74,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], @@ -95,48 +95,39 @@ class OnceDecoratorCheckRule extends AbstractUISyntaxRule { private validateDecorator( node: arkts.StructDeclaration, - onceDecorator: arkts.AnnotationUsage | undefined, ): void { node.definition?.body.forEach(body => { // Check if @Once is used on a property and if @Param is used with if (arkts.isClassProperty(body)) { - this.validatePropertyAnnotations(body, onceDecorator); + this.validatePropertyAnnotations(body); } }); } private validatePropertyAnnotations( - body: arkts.ClassProperty, - onceDecorator: arkts.AnnotationUsage | undefined + body: arkts.ClassProperty ): void { const propertyAnnotations = getClassPropertyAnnotationNames(body); - onceDecorator = findDecorator(body, PresetDecorators.ONCE); + const onceDecorator = findDecorator(body, PresetDecorators.ONCE); if (onceDecorator) { const isParamUsed = propertyAnnotations.includes(PresetDecorators.PARAM); - // If @Once is found, check if @Param is also used + // If @Once is found, check if @Param is not used if (!isParamUsed) { this.reportMissingParamWithOnce(onceDecorator); - } else { - // If both @Once and @Param are used, check for other - // incompatible decorators - const otherDecorators = body.annotations?.find(annotation => - annotation.expr && arkts.isIdentifier(annotation.expr) && - annotation.expr.name !== PresetDecorators.ONCE && - annotation.expr.name !== PresetDecorators.PARAM - ); - this.reportInvalidDecoratorsWithOnceAndParam(otherDecorators); } } } - private reportMissingParamWithOnce(onceDecorator: arkts.AnnotationUsage | undefined): void { + private reportMissingParamWithOnce( + onceDecorator: arkts.AnnotationUsage | undefined + ): void { if (!onceDecorator) { return; } this.report({ node: onceDecorator, message: this.messages.invalidDecorator, - fix: (onceDecorator) => { + fix: () => { const startPosition = onceDecorator.endPosition; const endPosition = onceDecorator.endPosition; return { @@ -146,24 +137,6 @@ class OnceDecoratorCheckRule extends AbstractUISyntaxRule { } }); } - - private reportInvalidDecoratorsWithOnceAndParam(otherDecorators: arkts.AnnotationUsage | undefined): void { - if (!otherDecorators) { - return; - } - this.report({ - node: otherDecorators, - message: this.messages.invalidDecorator, - fix: (otherDecorators) => { - const startPosition = otherDecorators.startPosition; - const endPosition = otherDecorators.endPosition; - return { - range: [startPosition, endPosition], - code: '' - }; - } - }); - } } export default OnceDecoratorCheckRule; \ No newline at end of file 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 04b2b3da1..3a43242f0 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/property-type.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/property-type.ts @@ -63,7 +63,6 @@ class PropertyTypeRule extends AbstractUISyntaxRule { public setup(): Record { return { - propertyHasType: `The property '{{propertyName}}' must specify a type.`, propertyObjectLink: `'@ObjectLink' cannot be used with this type. Apply it only to classes decorated by '@Observed' or initialized using the return value of 'makeV1Observed'.`, propertyProp: `The '@{{decoratorName}}' decorated attribute '{{propertyName}}' must be of the string, number, boolean, enum or object type.`, propertyBuilderParam: `'@BuilderParam' property can only be initialized by '@Builder' function or '@Builder' method in struct.`, @@ -175,34 +174,7 @@ class PropertyTypeRule extends AbstractUISyntaxRule { const propertyName = node.key.name; // Gets the type of property const propertyType = node.typeAnnotation; - const mustHasTypeDecorator = node.annotations.some(annotation => - annotation.expr && arkts.isIdentifier(annotation.expr) && - (v1DecoratorMustHasType.includes(annotation.expr.name) || - v2DecoratorMustHasType.includes(annotation.expr.name)) - ); const nodeKey = node.key; - // Check if there is a type declaration - if (!propertyType && nodeKey && mustHasTypeDecorator) { - this.report({ - node: nodeKey, - message: this.messages.propertyHasType, - data: { - propertyName, - }, - }); - } - if (propertyType && - arkts.nodeType(propertyType) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ERROR_TYPE_NODE && - nodeKey && - mustHasTypeDecorator) { - this.report({ - node: nodeKey, - message: this.messages.propertyHasType, - data: { - propertyName, - }, - }); - } const objectLinkDecorator = findDecorator(node, PresetDecorators.OBJECT_LINK); // Determine whether the property has @Objectlink if (objectLinkDecorator && propertyType) { diff --git a/arkui-plugins/ui-syntax-plugins/rules/static-param-require.ts b/arkui-plugins/ui-syntax-plugins/rules/static-param-require.ts new file mode 100644 index 000000000..8f2fdb8e2 --- /dev/null +++ b/arkui-plugins/ui-syntax-plugins/rules/static-param-require.ts @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as arkts from '@koalaui/libarkts'; +import { getClassPropertyName, hasAnnotation, PresetDecorators } from '../utils'; +import { AbstractUISyntaxRule } from './ui-syntax-rule'; + +class StaticParamRequireRule extends AbstractUISyntaxRule { + private staticPropertyMap: Map = new Map(); + + public setup(): Record { + return { + cannotInitializePrivateVariables: `Static property '{{propertyName}}' can not be initialized through the component constructor.`, + }; + } + + public beforeTransform(): void { + this.staticPropertyMap = new Map(); + } + + public parsed(node: arkts.StructDeclaration): void { + // Check if the current node is the root node + if (arkts.nodeType(node) === arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + node.getChildren().forEach((member) => { + if (!arkts.isStructDeclaration(member) || !member.definition.ident || !member.definition.ident.name) { + return; + } + const hasComponentV1 = hasAnnotation(member.definition.annotations, PresetDecorators.COMPONENT_V1); + const hasComponentV2 = hasAnnotation(member.definition.annotations, PresetDecorators.COMPONENT_V2); + const structName: string = member.definition.ident.name; + member.definition.body.forEach((item) => { + this.addStaticProperty(item, structName, hasComponentV1, hasComponentV2); + }); + }); + } + if (!arkts.isCallExpression(node) || !arkts.isIdentifier(node.expression)) { + return; + } + const componentName = node.expression.name; + // If the initialization is for a component with private properties + if (!this.staticPropertyMap.has(componentName)) { + return; + } + node.arguments.forEach((member) => { + member.getChildren().forEach((property) => { + if (!arkts.isProperty(property) || !property.key || !arkts.isIdentifier(property.key)) { + return; + } + const propertyName: string = property.key.name; + if (this.staticPropertyMap.get(componentName)!.includes(propertyName)) { + this.report({ + node: property, + message: this.messages.cannotInitializePrivateVariables, + data: { + propertyName: propertyName, + }, + }); + } + }); + }); + } + + private addStaticProperty( + item: arkts.AstNode, + structName: string, + hasComponentV1: boolean, + hasComponentV2: boolean + ): void { + if (!arkts.isClassProperty(item) || !item.isStatic) { + return; + } + const propertyName = getClassPropertyName(item); + if (!propertyName) { + return; + } + // Static properties with decorators in componentV2 need to be checked + if (hasComponentV2 && item.annotations.length > 0) { + this.addElement(structName, propertyName); + } + // Static properties in componentV1 need to be verified + if (hasComponentV1 && !hasComponentV2) { + this.addElement(structName, propertyName); + } + } + + private addElement(structName: string, propertyName: string): void { + // Check if structName already exists in privateMap + if (this.staticPropertyMap.has(structName)) { + // If it exists, retrieve the current string[] and append the new content + this.staticPropertyMap.get(structName)!.push(propertyName); + } else { + // If it doesn't exist, create a new string[] and add the content + this.staticPropertyMap.set(structName, [propertyName]); + } + } +} + +export default StaticParamRequireRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/struct-property-decorator.ts b/arkui-plugins/ui-syntax-plugins/rules/struct-property-decorator.ts index caac4a335..fd6f98af8 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/struct-property-decorator.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/struct-property-decorator.ts @@ -14,7 +14,7 @@ */ import * as arkts from '@koalaui/libarkts'; -import { getClassPropertyAnnotationNames, PresetDecorators } from '../utils'; +import { getClassPropertyAnnotationNames, hasAnnotation, PresetDecorators } from '../utils'; import { AbstractUISyntaxRule } from './ui-syntax-rule'; const decorators: string[] = [ @@ -41,7 +41,8 @@ class StructPropertyDecoratorRule extends AbstractUISyntaxRule { if (!arkts.isStructDeclaration(node)) { return; } - this.checkInvalidStaticPropertyDecorations(node); + const hasComponentV1 = hasAnnotation(node.definition.annotations, PresetDecorators.COMPONENT_V1); + this.checkInvalidStaticPropertyDecorations(node, hasComponentV1); } private hasPropertyDecorator( @@ -53,18 +54,20 @@ class StructPropertyDecoratorRule extends AbstractUISyntaxRule { ); } - private checkInvalidStaticPropertyDecorations(node: arkts.StructDeclaration,): void { + private checkInvalidStaticPropertyDecorations(node: arkts.StructDeclaration, hasComponentV1: boolean): void { node.definition.body.forEach((member) => { - // Errors are reported when the node type is ClassProperty, - if (arkts.isClassProperty(member)) { - const propertyNameNode = member.key; - if ((member.isStatic && this.hasPropertyDecorator(member)) && propertyNameNode) { - this.report({ - node: propertyNameNode, - message: this.messages.invalidStaticUsage - }); - } + // Errors are reported when the node type is static ClassProperty, + if (!arkts.isClassProperty(member) || !member.key) { + return; } + if (!hasComponentV1 || !member.isStatic || !this.hasPropertyDecorator(member)) { + return; + } + const propertyNameNode = member.key; + this.report({ + node: propertyNameNode, + message: this.messages.invalidStaticUsage + }); }); } } diff --git a/arkui-plugins/ui-syntax-plugins/rules/struct-variable-initialization.ts b/arkui-plugins/ui-syntax-plugins/rules/struct-variable-initialization.ts index 06cc166b1..38cbf5e3f 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/struct-variable-initialization.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/struct-variable-initialization.ts @@ -42,7 +42,6 @@ class StructVariableInitializationRule extends AbstractUISyntaxRule { return { mustBeInitializedLocally: `The '@{{decoratorName}}' property must be specified a default value.`, prohibitLocalInitialization: `The '@{{decoratorName}}' property cannot be specified a default value.`, - propRefRequireNoDefault: `The '@PropRef' property with '@Require' cannot be specified a default value.`, }; } @@ -59,10 +58,6 @@ class StructVariableInitializationRule extends AbstractUISyntaxRule { const valueExists = !!node.value; // Check for the presence of require decorator const hasRequire = findDecorator(node, PresetDecorators.REQUIRE); - const hasPropRef = findDecorator(node, PresetDecorators.PROP_REF); - if (hasPropRef && hasRequire && valueExists) { - this.reportPropRefRequireNoDefault(hasPropRef); - } node.annotations.some(annotation => { if (annotation.expr && arkts.isIdentifier(annotation.expr) && mustInitializeDecorators.includes(annotation.expr.name)) { @@ -108,13 +103,6 @@ class StructVariableInitializationRule extends AbstractUISyntaxRule { }); } } - - private reportPropRefRequireNoDefault(annotation: arkts.AnnotationUsage): void { - this.report({ - node: annotation, - message: this.messages.propRefRequireNoDefault, - }); - } } export default StructVariableInitializationRule; \ No newline at end of file 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 e7ab4ec8d..ec80d3afd 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/track-decorator-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/track-decorator-check.ts @@ -111,6 +111,7 @@ class TrackDecoratorCheckRule extends AbstractUISyntaxRule { message: this.messages.trackMustUsedWithObserved, fix: (trackDecorator) => { let startPosition = trackDecorator.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); let endPosition = trackDecorator.endPosition; return { range: [startPosition, endPosition], @@ -126,6 +127,7 @@ class TrackDecoratorCheckRule extends AbstractUISyntaxRule { message: this.messages.trackOnClassMemberOnly, fix: (node) => { let startPosition = node.startPosition; + startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); let endPosition = node.endPosition; return { range: [startPosition, endPosition], -- Gitee