diff --git a/arkui-plugins/ui-syntax-plugins/rules/component-componentV2-init-check.ts b/arkui-plugins/ui-syntax-plugins/rules/component-componentV2-init-check.ts index 42c497eb713c8cc2b1a0cb47ef43e23a3423cbc9..04df9cbfbfcbd430ea7d1363b325ec11c15a0311 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/component-componentV2-init-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/component-componentV2-init-check.ts @@ -18,76 +18,77 @@ import { getAnnotationUsage, getIdentifierName, hasAnnotation, PresetDecorators import { AbstractUISyntaxRule } from './ui-syntax-rule'; class ComponentComponentV2InitCheckRule extends AbstractUISyntaxRule { - private componentV1WithLinkList: string[] = []; + private componentV1WithLinkList: string[] = []; - public setup(): Record { - return { - componentInitLinkCheck: `A V2 component cannot be used with any member property decorated by '@Link' in a V1 component.`, - }; - } + public setup(): Record { + return { + componentInitLinkCheck: `A V2 component cannot be used with any member property decorated by '@Link' in a V1 component.`, + }; + } - public beforeTransform(): void { - this.componentV1WithLinkList = []; - } + public beforeTransform(): void { + this.componentV1WithLinkList = []; + } - public parsed(node: arkts.StructDeclaration): void { - this.initComponentV1WithLinkList(node); - this.checkComponentInitLink(node); - } + public parsed(node: arkts.StructDeclaration): void { + this.initComponentV1WithLinkList(node); + this.checkComponentInitLink(node); + } - private initComponentV1WithLinkList(node: arkts.AstNode): void { - if (arkts.nodeType(node) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { - return; + private initComponentV1WithLinkList(node: arkts.AstNode): void { + if (arkts.nodeType(node) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + return; + } + node.getChildren().forEach((member) => { + if (!arkts.isStructDeclaration(member) || !member.definition.ident || + !hasAnnotation(member?.definition.annotations, PresetDecorators.COMPONENT_V1)) { + return; + } + let structName: string = member.definition.ident?.name ?? ''; + member.definition?.body?.forEach((item) => { + if (!arkts.isClassProperty(item) || !item.key) { + return; + } + if (item.annotations.some(annotation => annotation.expr && + getIdentifierName(annotation.expr) === PresetDecorators.LINK)) { + this.componentV1WithLinkList.push(structName); + } + }); + }); } - node.getChildren().forEach((member) => { - if (!arkts.isStructDeclaration(member) || !member.definition.ident || - !hasAnnotation(member?.definition.annotations, PresetDecorators.COMPONENT_V1)) { - return; - } - let structName: string = member.definition.ident?.name ?? ''; - member.definition?.body?.forEach((item) => { - if (!arkts.isClassProperty(item) || !item.key) { - return; + + private checkComponentInitLink(node: arkts.AstNode): void { + if (!arkts.isIdentifier(node) || !this.componentV1WithLinkList.includes(getIdentifierName(node))) { + return; } - if (item.annotations.some(annotation => annotation.expr && - getIdentifierName(annotation.expr) === PresetDecorators.LINK)) { - this.componentV1WithLinkList.push(structName); + if (!node.parent) { + return; } - }); - }); - } - - private checkComponentInitLink(node: arkts.AstNode): void { - if (!arkts.isIdentifier(node) || !this.componentV1WithLinkList.includes(getIdentifierName(node))) { - return; - } - if (!node.parent) { - return; - } - let structNode = node.parent; - while (!arkts.isStructDeclaration(structNode)) { - if (!structNode.parent) { - return; - } - structNode = structNode.parent; - } - if (getAnnotationUsage(structNode, PresetDecorators.COMPONENT_V2) !== undefined) { - if (!node.parent) { - return; - } - const parentNode = node.parent; - this.report({ - node: parentNode, - message: this.messages.componentInitLinkCheck, - fix: () => { - return { - range: [parentNode.startPosition, parentNode.endPosition], - code: '', - }; + let structNode = node.parent; + while (!arkts.isStructDeclaration(structNode)) { + if (!structNode.parent) { + return; + } + structNode = structNode.parent; + } + if (getAnnotationUsage(structNode, PresetDecorators.COMPONENT_V2) !== undefined) { + if (!node.parent) { + return; + } + const parentNode = node.parent; + this.report({ + node: parentNode, + message: this.messages.componentInitLinkCheck, + fix: () => { + return { + title: 'Remove the component', + range: [parentNode.startPosition, parentNode.endPosition], + code: '', + }; + } + }); } - }); } - } } export default ComponentComponentV2InitCheckRule; \ 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 f77421dd03d424af64d6cda13f80161f7d12119a..47d5088ec97e6e2df8055d203173b8a9bbe8b613 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 @@ -94,6 +94,7 @@ class ComponentV2StateUsageValidationRule extends AbstractUISyntaxRule { fix: (memberKey) => { const startPosition = memberKey.startPosition; return { + title: 'Add @Require annotation', range: [startPosition, startPosition], code: `@${PresetDecorators.REQUIRE} `, }; @@ -119,6 +120,7 @@ class ComponentV2StateUsageValidationRule extends AbstractUISyntaxRule { startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); let endPosition = requireDecorator.endPosition; return { + title: 'Remove the @Require annotation', range: [startPosition, endPosition], code: '', }; @@ -154,6 +156,7 @@ class ComponentV2StateUsageValidationRule extends AbstractUISyntaxRule { startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); let endPosition = annotation.endPosition; return { + title: 'Remove the annotation', range: [startPosition, endPosition], code: '', }; @@ -325,6 +328,7 @@ class ComponentV2StateUsageValidationRule extends AbstractUISyntaxRule { }, fix: (property) => { return { + title: 'Remove the property', range: [property.startPosition, property.endPosition], code: '', }; @@ -342,6 +346,7 @@ class ComponentV2StateUsageValidationRule extends AbstractUISyntaxRule { }, fix: (property) => { return { + title: 'Remove the property', range: [property.startPosition, property.endPosition], code: '', }; 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 9caa5e6e7c70f47056656733f69a8ef4426e2c48..fd271307d169280f104a1155e215ebe1c8854a76 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/computed-decorator-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/computed-decorator-check.ts @@ -84,6 +84,7 @@ class ComputedDecoratorCheckRule extends AbstractUISyntaxRule { startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); const endPosition = computedDecorator.endPosition; return { + title: 'Remove the annotation', range: [startPosition, endPosition], code: '', }; @@ -119,6 +120,7 @@ class ComputedDecoratorCheckRule extends AbstractUISyntaxRule { startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); const endPosition = computedDecorator.endPosition; return { + title: 'Remove the annotation', range: [startPosition, endPosition], code: '', }; @@ -188,6 +190,7 @@ class ComputedDecoratorCheckRule extends AbstractUISyntaxRule { const startPosition = node.startPosition; const endPosition = node.endPosition; return { + title: 'Remove the set method', range: [startPosition, endPosition], code: '', }; @@ -240,6 +243,7 @@ class ComputedDecoratorCheckRule extends AbstractUISyntaxRule { const startPosition = node.startPosition; const endPosition = node.startPosition; return { + title: 'Add @ComponentV2 annotation', range: [startPosition, endPosition], code: `@${PresetDecorators.COMPONENT_V2}\n`, }; @@ -255,6 +259,7 @@ class ComputedDecoratorCheckRule extends AbstractUISyntaxRule { const startPosition = componentDecorator.startPosition; const endPosition = componentDecorator.endPosition; return { + title: 'Change @Component to @ComponentV2', range: [startPosition, endPosition], code: `${PresetDecorators.COMPONENT_V2}`, }; @@ -278,6 +283,7 @@ class ComputedDecoratorCheckRule extends AbstractUISyntaxRule { fix: () => { const startPosition = node.startPosition; return { + title: 'Add @ObservedV2 annotation', range: [startPosition, startPosition], code: `@${PresetDecorators.OBSERVED_V2}\n`, }; @@ -293,6 +299,7 @@ class ComputedDecoratorCheckRule extends AbstractUISyntaxRule { const startPosition = observedDecorator.startPosition; const endPosition = observedDecorator.endPosition; return { + title: 'Change @Observed to @ObservedV2', range: [startPosition, endPosition], code: `${PresetDecorators.OBSERVED_V2}`, }; 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 837c8d85f2da7234926b5f998f12a1638e674838..538f7e9fcef5a83726f9f0c5ffb0ab0aa520490b 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 @@ -148,6 +148,7 @@ class ConsumerProviderDecoratorCheckRule extends AbstractUISyntaxRule { startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); const endPosition = otherDecorators.endPosition; return { + title: 'Remove other annotations', range: [startPosition, endPosition], code: '', }; @@ -224,6 +225,7 @@ class ConsumerProviderDecoratorCheckRule extends AbstractUISyntaxRule { startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); const endPosition = decorator.endPosition; return { + title: 'Remove the annotation', range: [startPosition, endPosition], code: '', }; @@ -286,6 +288,7 @@ class ConsumerProviderDecoratorCheckRule extends AbstractUISyntaxRule { const startPosition = parent.startPosition; const endPosition = parent.endPosition; return { + title: 'Remove the property', range: [startPosition, endPosition], code: '', }; @@ -309,6 +312,7 @@ class ConsumerProviderDecoratorCheckRule extends AbstractUISyntaxRule { const startPosition = parent.startPosition; const endPosition = parent.endPosition; return { + title: 'Remove the property', range: [startPosition, endPosition], code: '', }; 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 8cbd2a27ea3b9a077ad7b08d2e5ad598c0c70f8b..e845cac4430e3780f6f039edf407cdbe276b9f32 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/monitor-decorator-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/monitor-decorator-check.ts @@ -64,6 +64,7 @@ class MonitorDecoratorCheckRule extends AbstractUISyntaxRule { message: this.messages.monitorUsedInObservedV2Class, fix: () => { return { + title: 'Add @ObservedV2 annotation', range: [node.startPosition, node.startPosition], code: `@${PresetDecorators.OBSERVED_V2}\n` }; @@ -77,6 +78,7 @@ class MonitorDecoratorCheckRule extends AbstractUISyntaxRule { message: this.messages.monitorUsedInObservedV2Class, fix: () => { return { + title: 'Change @Observed to @ObservedV2', range: [observedV1Decorator.startPosition, observedV1Decorator.endPosition], code: `${PresetDecorators.OBSERVED_V2}` }; @@ -99,6 +101,7 @@ class MonitorDecoratorCheckRule extends AbstractUISyntaxRule { node: monitorDecorator, message: this.messages.monitorUsedInComponentV2Struct, fix: () => ({ + title: 'Add @ComponentV2 annotation', range: [node.startPosition, node.startPosition], code: `@${PresetDecorators.COMPONENT_V2}\n` }) @@ -112,6 +115,7 @@ class MonitorDecoratorCheckRule extends AbstractUISyntaxRule { message: this.messages.monitorUsedInComponentV2Struct, fix: () => { return { + title: 'Change @Component to @ComponentV2', range: [componentV1Decorator.startPosition, componentV1Decorator.endPosition], code: `${PresetDecorators.COMPONENT_V2}` }; @@ -179,6 +183,7 @@ class MonitorDecoratorCheckRule extends AbstractUISyntaxRule { startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); const endPosition = endPositions[endPositions.length - 1]; return { + title: 'Remove the annotation', range: [startPosition, endPosition], code: '' }; @@ -211,6 +216,7 @@ class MonitorDecoratorCheckRule extends AbstractUISyntaxRule { startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); const endPosition = monitorDecorator.endPosition; return { + title: 'Remove the @Monitor annotation', range: [startPosition, endPosition], code: '', }; diff --git a/arkui-plugins/ui-syntax-plugins/rules/nested-reuse-component-check.ts b/arkui-plugins/ui-syntax-plugins/rules/nested-reuse-component-check.ts index 62f109253ea5b5f661f2f4e4fb3f985784a20298..c1206b1ab177f28fd07423ceefa91c85446508fb 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/nested-reuse-component-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/nested-reuse-component-check.ts @@ -18,180 +18,184 @@ import { COMPONENT_REPEAT, getIdentifierName, PresetDecorators, TEMPLATE } from import { AbstractUISyntaxRule } from './ui-syntax-rule'; class NestedReuseComponentCheckRule extends AbstractUISyntaxRule { - private reusableV2StructName: string[] = []; - private reusableStructName: string[] = []; + private reusableV2StructName: string[] = []; + private reusableStructName: string[] = []; - public setup(): Record { - return { - noReusableV2InComponentV1: `A custom component decorated with @Component cannot contain child components decorated with @ReusableV2.`, - noReusableV2InReusableV1: `A custom component decorated with @Reusable cannot contain child components decorated with @ReusableV2.`, - noReusableV1InReusableV2: `A custom component decorated with @ReusableV2 cannot contain child components decorated with @Reusable.`, - noReusableV2InRepeatTemplate: `The template attribute of the Repeat component cannot contain any custom component decorated with @ReusableV2.`, - }; - } - - public beforeTransform(): void { - this.reusableV2StructName = []; - this.reusableStructName = []; - } - - public parsed(node: arkts.StructDeclaration): void { - this.initStructName(node); - this.checkNestedReuseComponent(node); - this.checkNoReusableV1InReusableV2(node); - } - - private initStructName(node: arkts.AstNode): void { - if (arkts.nodeType(node) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { - return; - } - //Go through all the children of Program - for (const childNode of node.getChildren()) { - // Check whether the type is struct - if (!arkts.isStructDeclaration(childNode)) { - continue; - } - // Get a list of annotations - const annotationsList = childNode.definition.annotations; - // Check that the current component has @Reusable and @ReusableV2 decorators - if (annotationsList?.some((annotation: any) => annotation.expr.name === PresetDecorators.REUSABLE_V2)) { - const struceName = childNode.definition?.ident?.name || ''; - this.reusableV2StructName.push(struceName); - } else if (annotationsList?.some((annotation: any) => annotation.expr.name === PresetDecorators.REUSABLE_V1)) { - const struceName = childNode.definition?.ident?.name || ''; - this.reusableStructName.push(struceName); - } - } - } - - private reportNoReusableV2InRepeatTemplate(errorNode: arkts.AstNode): void { - this.report({ - node: errorNode, - message: this.messages.noReusableV2InRepeatTemplate, - fix: (errorNode) => { + public setup(): Record { return { - range: [errorNode.startPosition, errorNode.endPosition], - code: '', + noReusableV2InComponentV1: `A custom component decorated with @Component cannot contain child components decorated with @ReusableV2.`, + noReusableV2InReusableV1: `A custom component decorated with @Reusable cannot contain child components decorated with @ReusableV2.`, + noReusableV1InReusableV2: `A custom component decorated with @ReusableV2 cannot contain child components decorated with @Reusable.`, + noReusableV2InRepeatTemplate: `The template attribute of the Repeat component cannot contain any custom component decorated with @ReusableV2.`, }; - } - }); - } - - private checkHasRepeatOrTemplate(node: arkts.CallExpression): { hasRepeat: boolean, hasTemplate: boolean } { - let hasRepeat: boolean = false; - let hasTemplate: boolean = false; - while (arkts.isCallExpression(node) && - node.expression && arkts.isMemberExpression(node.expression) && - node.expression.object && arkts.isCallExpression(node.expression.object)) { - if (arkts.isIdentifier(node.expression.property) && getIdentifierName(node.expression.property) === TEMPLATE) { - hasTemplate = true; - } - node = node.expression.object; } - if (arkts.isCallExpression(node) && arkts.isIdentifier(node.expression)) { - hasRepeat = getIdentifierName(node.expression) === COMPONENT_REPEAT; + + public beforeTransform(): void { + this.reusableV2StructName = []; + this.reusableStructName = []; } - return { hasRepeat, hasTemplate }; - } - private checkNoReusableV2InRepeatTemplate(node: arkts.AstNode, errorNode: arkts.AstNode): boolean { - if (!arkts.isCallExpression(node)) { - return false; + public parsed(node: arkts.StructDeclaration): void { + this.initStructName(node); + this.checkNestedReuseComponent(node); + this.checkNoReusableV1InReusableV2(node); } - const flag = this.checkHasRepeatOrTemplate(node); - if (!flag.hasRepeat || !flag.hasTemplate) { - return false; + + private initStructName(node: arkts.AstNode): void { + if (arkts.nodeType(node) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + return; + } + //Go through all the children of Program + for (const childNode of node.getChildren()) { + // Check whether the type is struct + if (!arkts.isStructDeclaration(childNode)) { + continue; + } + // Get a list of annotations + const annotationsList = childNode.definition.annotations; + // Check that the current component has @Reusable and @ReusableV2 decorators + if (annotationsList?.some((annotation: any) => annotation.expr.name === PresetDecorators.REUSABLE_V2)) { + const struceName = childNode.definition?.ident?.name || ''; + this.reusableV2StructName.push(struceName); + } else if (annotationsList?.some((annotation: any) => annotation.expr.name === PresetDecorators.REUSABLE_V1)) { + const struceName = childNode.definition?.ident?.name || ''; + this.reusableStructName.push(struceName); + } + } } - this.reportNoReusableV2InRepeatTemplate(errorNode); - return true; - } - private reportNoReusableV1InReusableV2(node: arkts.AstNode): void { - this.report({ - node: node, - message: this.messages.noReusableV1InReusableV2, - fix: (node) => { - return { - range: [node.startPosition, node.endPosition], - code: '', - }; - } - }); - } + private reportNoReusableV2InRepeatTemplate(errorNode: arkts.AstNode): void { + this.report({ + node: errorNode, + message: this.messages.noReusableV2InRepeatTemplate, + fix: (errorNode) => { + return { + title: 'Remove the component', + range: [errorNode.startPosition, errorNode.endPosition], + code: '', + }; + } + }); + } - private checkNoReusableV1InReusableV2(node: arkts.AstNode): void { - if (!arkts.isCallExpression(node) || !arkts.isIdentifier(node.expression)) { - return; + private checkHasRepeatOrTemplate(node: arkts.CallExpression): { hasRepeat: boolean, hasTemplate: boolean } { + let hasRepeat: boolean = false; + let hasTemplate: boolean = false; + while (arkts.isCallExpression(node) && + node.expression && arkts.isMemberExpression(node.expression) && + node.expression.object && arkts.isCallExpression(node.expression.object)) { + if (arkts.isIdentifier(node.expression.property) && getIdentifierName(node.expression.property) === TEMPLATE) { + hasTemplate = true; + } + node = node.expression.object; + } + if (arkts.isCallExpression(node) && arkts.isIdentifier(node.expression)) { + hasRepeat = getIdentifierName(node.expression) === COMPONENT_REPEAT; + } + return { hasRepeat, hasTemplate }; } - if (this.reusableStructName.includes(node.expression.name)) { - // Traverse upwards to find the custom component. - let struceNode: arkts.AstNode = node; - while (!arkts.isStructDeclaration(struceNode)) { - if (!struceNode.parent) { - return; + + private checkNoReusableV2InRepeatTemplate(node: arkts.AstNode, errorNode: arkts.AstNode): boolean { + if (!arkts.isCallExpression(node)) { + return false; } - struceNode = struceNode.parent; - } - const annotationsList = struceNode.definition.annotations; - // Check that the current component is decorated by the @ComponentV2 decorator - if (annotationsList.some((annotation: any) => annotation.expr.name === PresetDecorators.REUSABLE_V2)) { - this.reportNoReusableV1InReusableV2(node); - } + const flag = this.checkHasRepeatOrTemplate(node); + if (!flag.hasRepeat || !flag.hasTemplate) { + return false; + } + this.reportNoReusableV2InRepeatTemplate(errorNode); + return true; } - } - private reportNoReusableV2InReusableV1(node: arkts.AstNode): void { - this.report({ - node: node, - message: this.messages.noReusableV2InReusableV1, - fix: (node) => { - return { - range: [node.startPosition, node.endPosition], - code: '', - }; - } - }); - } + private reportNoReusableV1InReusableV2(node: arkts.AstNode): void { + this.report({ + node: node, + message: this.messages.noReusableV1InReusableV2, + fix: (node) => { + return { + title: 'Remove the component', + range: [node.startPosition, node.endPosition], + code: '', + }; + } + }); + } - private reportNoReusableV2InComponentV1(node: arkts.AstNode): void { - this.report({ - node: node, - message: this.messages.noReusableV2InComponentV1, - fix: (node) => { - return { - range: [node.startPosition, node.endPosition], - code: '', - }; - } - }); - } + private checkNoReusableV1InReusableV2(node: arkts.AstNode): void { + if (!arkts.isCallExpression(node) || !arkts.isIdentifier(node.expression)) { + return; + } + if (this.reusableStructName.includes(node.expression.name)) { + // Traverse upwards to find the custom component. + let struceNode: arkts.AstNode = node; + while (!arkts.isStructDeclaration(struceNode)) { + if (!struceNode.parent) { + return; + } + struceNode = struceNode.parent; + } + const annotationsList = struceNode.definition.annotations; + // Check that the current component is decorated by the @ComponentV2 decorator + if (annotationsList.some((annotation: any) => annotation.expr.name === PresetDecorators.REUSABLE_V2)) { + this.reportNoReusableV1InReusableV2(node); + } + } + } - private checkNestedReuseComponent(node: arkts.AstNode): void { - if (!arkts.isCallExpression(node) || !arkts.isIdentifier(node.expression)) { - return; + private reportNoReusableV2InReusableV1(node: arkts.AstNode): void { + this.report({ + node: node, + message: this.messages.noReusableV2InReusableV1, + fix: (node) => { + return { + title: 'Remove the component', + range: [node.startPosition, node.endPosition], + code: '', + }; + } + }); } - if (this.reusableV2StructName.includes(node.expression.name)) { - // Traverse upwards to find the custom component. - let struceNode: arkts.AstNode = node; - let hasReportedError = false; - while (!arkts.isStructDeclaration(struceNode)) { - if (!struceNode.parent) { - return; + + private reportNoReusableV2InComponentV1(node: arkts.AstNode): void { + this.report({ + node: node, + message: this.messages.noReusableV2InComponentV1, + fix: (node) => { + return { + title: 'Remove the component', + range: [node.startPosition, node.endPosition], + code: '', + }; + } + }); + } + + private checkNestedReuseComponent(node: arkts.AstNode): void { + if (!arkts.isCallExpression(node) || !arkts.isIdentifier(node.expression)) { + return; } - struceNode = struceNode.parent; - if (!hasReportedError) { - hasReportedError = this.checkNoReusableV2InRepeatTemplate(struceNode, node); + if (this.reusableV2StructName.includes(node.expression.name)) { + // Traverse upwards to find the custom component. + let struceNode: arkts.AstNode = node; + let hasReportedError = false; + while (!arkts.isStructDeclaration(struceNode)) { + if (!struceNode.parent) { + return; + } + struceNode = struceNode.parent; + if (!hasReportedError) { + hasReportedError = this.checkNoReusableV2InRepeatTemplate(struceNode, node); + } + } + // Gets a list of decorators for the current Struct + const annotationsList = struceNode.definition.annotations; + if (annotationsList?.some((annotation: any) => annotation.expr.name === PresetDecorators.REUSABLE_V1)) { + this.reportNoReusableV2InReusableV1(node); + } else if (annotationsList?.some((annotation: any) => annotation.expr.name === PresetDecorators.COMPONENT_V1)) { + this.reportNoReusableV2InComponentV1(node); + } } - } - // Gets a list of decorators for the current Struct - const annotationsList = struceNode.definition.annotations; - if (annotationsList?.some((annotation: any) => annotation.expr.name === PresetDecorators.REUSABLE_V1)) { - this.reportNoReusableV2InReusableV1(node); - } else if (annotationsList?.some((annotation: any) => annotation.expr.name === PresetDecorators.COMPONENT_V1)) { - this.reportNoReusableV2InComponentV1(node); - } } - } } export default NestedReuseComponentCheckRule; \ No newline at end of file diff --git a/arkui-plugins/ui-syntax-plugins/rules/no-child-in-button.ts b/arkui-plugins/ui-syntax-plugins/rules/no-child-in-button.ts index 8673700ac2b586656c340f7038db807562f428c6..74028790cd61832575de5c4885eea3c67109bc6b 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/no-child-in-button.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/no-child-in-button.ts @@ -19,79 +19,80 @@ import { getIdentifierName } from '../utils'; class NoChildInButtonRule extends AbstractUISyntaxRule { - public setup(): Record { - return { - noChildInButton: `The Button component with a label parameter can not have any child.`, - }; - } - - public parsed(node: arkts.AstNode): void { - // Check if the current node is an identifier - if (!arkts.isIdentifier(node)) { - return; - } - const componentName = getIdentifierName(node); - // If the current node is 'Button' - if (componentName !== 'Button') { - return; - } - if (!this.isInsideStructAndBuild(node)) { - return; + public setup(): Record { + return { + noChildInButton: `The Button component with a label parameter can not have any child.`, + }; } - if (!node.parent) { - return; - } - // Obtain the information of the parent node of the current node - let parentNode = node.parent; - if (!arkts.isCallExpression(parentNode)) { - return; - }; - // Gets and traverses all the children of the parent node - this.reportNoChildInButtonError(parentNode); - } - private isInsideStructAndBuild(node: arkts.AstNode): boolean { - if (!node.parent) { - return false; - } - let parentNode = node.parent; - let isInStruct = false; - let isInBuild = false; - while (arkts.nodeType(parentNode) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { - if (arkts.isStructDeclaration(parentNode)) { - isInStruct = true; - } - if (arkts.isScriptFunction(parentNode) && parentNode.id?.name === 'build') { - isInBuild = true; - } - if (!parentNode.parent) { - return false; - } - parentNode = parentNode.parent; + public parsed(node: arkts.AstNode): void { + // Check if the current node is an identifier + if (!arkts.isIdentifier(node)) { + return; + } + const componentName = getIdentifierName(node); + // If the current node is 'Button' + if (componentName !== 'Button') { + return; + } + if (!this.isInsideStructAndBuild(node)) { + return; + } + if (!node.parent) { + return; + } + // Obtain the information of the parent node of the current node + let parentNode = node.parent; + if (!arkts.isCallExpression(parentNode)) { + return; + }; + // Gets and traverses all the children of the parent node + this.reportNoChildInButtonError(parentNode); } - return isInStruct && isInBuild; - } - private reportNoChildInButtonError(parentNode: arkts.AstNode): void { - const siblings = parentNode.getChildren(); - if (!Array.isArray(siblings) || siblings.length < 3) { - return; + private isInsideStructAndBuild(node: arkts.AstNode): boolean { + if (!node.parent) { + return false; + } + let parentNode = node.parent; + let isInStruct = false; + let isInBuild = false; + while (arkts.nodeType(parentNode) !== arkts.Es2pandaAstNodeType.AST_NODE_TYPE_ETS_MODULE) { + if (arkts.isStructDeclaration(parentNode)) { + isInStruct = true; + } + if (arkts.isScriptFunction(parentNode) && parentNode.id?.name === 'build') { + isInBuild = true; + } + if (!parentNode.parent) { + return false; + } + parentNode = parentNode.parent; + } + return isInStruct && isInBuild; } - if (arkts.isStringLiteral(siblings[1]) && arkts.isBlockStatement(siblings[2])) { - this.report({ - node: parentNode, - message: this.messages.noChildInButton, - fix: () => { - const startPosition = siblings[2].startPosition; - const endPosition = siblings[2].endPosition; - return { - range: [startPosition, endPosition], - code: '', - }; - }, - }); + + private reportNoChildInButtonError(parentNode: arkts.AstNode): void { + const siblings = parentNode.getChildren(); + if (!Array.isArray(siblings) || siblings.length < 3) { + return; + } + if (arkts.isStringLiteral(siblings[1]) && arkts.isBlockStatement(siblings[2])) { + this.report({ + node: parentNode, + message: this.messages.noChildInButton, + fix: () => { + const startPosition = siblings[2].startPosition; + const endPosition = siblings[2].endPosition; + return { + title: 'Remove child components', + range: [startPosition, endPosition], + code: '', + }; + }, + }); + } } - } }; 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 0c2f219261e576dbea50ed4e35bbbaec47dcdbac..51043718df9ce2cc9fd2a877cdcf4484faec4057 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-entry.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-entry.ts @@ -56,6 +56,7 @@ class NoDuplicateEntryRule extends AbstractUISyntaxRule { message: this.messages.duplicateEntry, fix: () => { return { + title: 'Remove the duplicate \'Entry\' annotation', range: [startPosition, entryDecoratorUsage.endPosition], code: '', }; @@ -73,6 +74,7 @@ class NoDuplicateEntryRule extends AbstractUISyntaxRule { message: this.messages.duplicateEntry, fix: () => { return { + title: 'Remove the duplicate \'Entry\' annotation', range: [startPosition, entryDecoratorUsage.endPosition], code: '', }; 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 0fa2d00c6d585a1e7dfac97e8d2ca10e917fc171..35d1b31e534cf0edd929a64fa20d69e4216513a6 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-preview.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/no-duplicate-preview.ts @@ -69,6 +69,7 @@ class NoDuplicatePreviewRule extends AbstractUISyntaxRule { message: this.messages.duplicateEntry, fix: () => { return { + title: 'Remove the duplicate \'Preview\' annotation', 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 7d0b98c834648e5238e2352f5081c0b60ba8c0fd..7c90f0c9da8d1141a918a76f32d1c67f5f103927 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 @@ -75,6 +75,7 @@ class NoPropLinkObjectLinkInEntryRule extends AbstractUISyntaxRule { startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); let endPosition = annotation.endPosition; return { + title: 'Remove the annotation', 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 c1b3a827c4322b6210450c4319cd3996d5493478..fce5a72c87ace28a055f5e63ab268a9d4603f9eb 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 @@ -52,6 +52,7 @@ class ObservedV2TraceUsageValidationRule extends AbstractUISyntaxRule { startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); let endPosition = observedV2Decorator.endPosition; return { + title: 'Remove the @ObservedV2 annotation', range: [startPosition, endPosition], code: '', }; @@ -69,6 +70,7 @@ class ObservedV2TraceUsageValidationRule extends AbstractUISyntaxRule { startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); let endPosition = traceDecorator.endPosition; return { + title: 'Remove the @Trace annotation', range: [startPosition, endPosition], code: '', }; @@ -106,6 +108,7 @@ class ObservedV2TraceUsageValidationRule extends AbstractUISyntaxRule { startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); let endPosition = traceDecorator.endPosition; return { + title: 'Remove the @Trace annotation', range: [startPosition, endPosition], code: '', }; @@ -121,6 +124,7 @@ class ObservedV2TraceUsageValidationRule extends AbstractUISyntaxRule { fix: () => { const startPosition = currentNode.startPosition; return { + title: 'Add @ObservedV2 annotation', range: [startPosition, startPosition], code: `@${PresetDecorators.OBSERVED_V2}\n`, }; @@ -137,6 +141,7 @@ class ObservedV2TraceUsageValidationRule extends AbstractUISyntaxRule { const startPosition = observedDecorator.startPosition; const endPosition = observedDecorator.endPosition; return { + title: 'Change @Observed to @ObservedV2', range: [startPosition, endPosition], 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 704eb038b5636ace1c45682d633f3401ffe30f8e..ce916bb8318db96da0568e5f5d7c076c5c73389a 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 @@ -91,6 +91,8 @@ class OldNewDecoratorMixUseCheckRule extends AbstractUISyntaxRule { structDecoratorName: string ): void { let propertyDecoratorName = getAnnotationName(errorDecorator); + const curStructDecoratorName = structDecoratorName === + PresetDecorators.COMPONENT_V2 ? PresetDecorators.COMPONENT_V1 : PresetDecorators.COMPONENT_V2; this.report({ node: errorDecorator, message: this.messages.oldAndNewDecoratorsMixUse, @@ -100,6 +102,7 @@ class OldNewDecoratorMixUseCheckRule extends AbstractUISyntaxRule { }, fix: () => { return { + title: `Change @${curStructDecoratorName} to @${structDecoratorName}`, range: [hasComponentV2Decorator.startPosition, hasComponentV2Decorator.endPosition], code: structDecoratorName, }; @@ -121,6 +124,7 @@ class OldNewDecoratorMixUseCheckRule extends AbstractUISyntaxRule { }, fix: () => { return { + title: 'Add @ComponentV2 annotation', range: [structNode.startPosition, structNode.startPosition], code: `@${PresetDecorators.COMPONENT_V2}\n`, }; 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 26bec5af19407b293cb3a1a76a96a0563dbd599d..0c0d16f8209d8216c83551262df98a3261a333ad 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/once-decorator-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/once-decorator-check.ts @@ -78,6 +78,7 @@ class OnceDecoratorCheckRule extends AbstractUISyntaxRule { startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); const endPosition = decorator.endPosition; return { + title: 'Remove the annotation', range: [startPosition, endPosition], code: '', }; @@ -131,6 +132,7 @@ class OnceDecoratorCheckRule extends AbstractUISyntaxRule { const startPosition = onceDecorator.endPosition; const endPosition = onceDecorator.endPosition; return { + title: 'Add @Param annotation', range: [startPosition, endPosition], code: `@${PresetDecorators.PARAM}` }; 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 69c7eb7ac70431e0f0a48f4b4dbc41b7a81361df..38b35cae0d139936f75a095d48a07d3a6e69cc5a 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 @@ -111,6 +111,7 @@ class OneDecoratorOnFunctionMethodRule extends AbstractUISyntaxRule { startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); const endPosition = otherDecorator.endPosition; return { + title: 'Remove the annotation', range: [startPosition, endPosition], code: `` }; diff --git a/arkui-plugins/ui-syntax-plugins/rules/reuse-attribute-check.ts b/arkui-plugins/ui-syntax-plugins/rules/reuse-attribute-check.ts index 5c60e83b6f6da95069420fdb7557e2027c835468..509a2a2fd79163578377eb4f433b10a1d59d3f06 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/reuse-attribute-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/reuse-attribute-check.ts @@ -90,6 +90,7 @@ class ReuseAttributeCheckRule extends AbstractUISyntaxRule { const startPosition = structNode.startPosition; const endPosition = structNode.endPosition; return { + title: 'Change reuse to reuseId', range: [startPosition, endPosition], code: ReuseConstants.REUSE_ID, }; @@ -108,6 +109,7 @@ class ReuseAttributeCheckRule extends AbstractUISyntaxRule { const startPosition = structNode.startPosition; const endPosition = structNode.endPosition; return { + title: 'Change reuseId to reuse', range: [startPosition, endPosition], code: ReuseConstants.REUSE, }; 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 ec80d3afd03ed13e07234b9fe0b9db6da4b4a148..a585241ece44e81b987776a2e9a59610114226a7 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/track-decorator-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/track-decorator-check.ts @@ -114,6 +114,7 @@ class TrackDecoratorCheckRule extends AbstractUISyntaxRule { startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); let endPosition = trackDecorator.endPosition; return { + title: 'Remove the annotation', range: [startPosition, endPosition], code: '', }; @@ -130,6 +131,7 @@ class TrackDecoratorCheckRule extends AbstractUISyntaxRule { startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); let endPosition = node.endPosition; return { + title: 'Remove the annotation', range: [startPosition, endPosition], code: '', }; diff --git a/arkui-plugins/ui-syntax-plugins/rules/ui-consistent-check.ts b/arkui-plugins/ui-syntax-plugins/rules/ui-consistent-check.ts index 45d3705217922a485c22d72521651691c1b1c05e..695b3579f2d12f0340882ca4a70cd17e71ca2989 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/ui-consistent-check.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/ui-consistent-check.ts @@ -130,7 +130,7 @@ class UiConsistentCheckRule extends AbstractUISyntaxRule { } private isColorProperty(propertyName: string): boolean { - // If the attribute name contains 'ResourceColor', 'Color', 'Resource', 'string', it is mabe a color property + // If the attribute name contains 'ResourceColor', 'Color', 'Resource', 'string', it is maybe a color property return propertyName.includes(UiConsistentCheckRule.resourceColorType) || UiConsistentCheckRule.colorUnionTypes.some(str => propertyName.includes(str)); } @@ -165,13 +165,16 @@ class UiConsistentCheckRule extends AbstractUISyntaxRule { if (!resources || resources.length < 1) { return; } + const propertyValue = (argNode as arkts.StringLiteral).str; + const targetValue = `$r('sys.float.${resources[0].resourceName}')`; this.report({ node: argNode, message: this.messages.vpSizeWarning, fix: () => { return { + title: `change ${propertyValue} to ${targetValue}`, range: [argNode.startPosition, argNode.endPosition], - code: `$r('sys.float.${resources[0].resourceName}')`, + code: `${targetValue}`, }; } }); @@ -208,13 +211,16 @@ class UiConsistentCheckRule extends AbstractUISyntaxRule { if (!resources || resources.length < 1) { return; } + const propertyValue = (argNode as arkts.StringLiteral).str; + const targetValue = `$r('sys.float.${resources[0].resourceName}')`; this.report({ node: argNode, message: this.messages.vpAndPxSizeWarning, fix: () => { return { + title: `change ${propertyValue} to ${targetValue}`, range: [argNode.startPosition, argNode.endPosition], - code: `$r('sys.float.${resources[0].resourceName}')`, + code: `${targetValue}`, }; } }); @@ -249,13 +255,16 @@ class UiConsistentCheckRule extends AbstractUISyntaxRule { if (!resources || resources.length < 1) { return; } + const propertyValue = (argNode as arkts.StringLiteral).str; + const targetValue = `$r('sys.color.${resources[0].resourceName}')`; this.report({ node: argNode, message: this.messages.colorConsistentWarning, fix: () => { return { + title: `change ${propertyValue} to ${targetValue}`, range: [argNode.startPosition, argNode.endPosition], - code: `$r('sys.color.${resources[0].resourceName}')`, + code: `${targetValue}`, }; } }); diff --git a/arkui-plugins/ui-syntax-plugins/rules/ui-syntax-rule.ts b/arkui-plugins/ui-syntax-plugins/rules/ui-syntax-rule.ts index 77e44ebf5145c2aed9fba8739e41be43dbfa08e1..ca75e40b5b0b9e4840811f8526d1fc989b98bc5d 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/ui-syntax-rule.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/ui-syntax-rule.ts @@ -20,6 +20,7 @@ import { UISyntaxRuleComponents } from 'ui-syntax-plugins/utils'; export type FixSuggestion = { range: [start: arkts.SourcePosition, end: arkts.SourcePosition]; code: string; + title?: string; }; export type ReportOptions = { diff --git a/arkui-plugins/ui-syntax-plugins/rules/validate-build-in-struct.ts b/arkui-plugins/ui-syntax-plugins/rules/validate-build-in-struct.ts index 0388d347889eff2bc920a217188f436885a721e7..d187edd5257846eacf9b930e6d8a729fe78ec784 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/validate-build-in-struct.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/validate-build-in-struct.ts @@ -91,6 +91,7 @@ class ValidateBuildInStructRule extends AbstractUISyntaxRule { const startPosition = member.startPosition; const endPosition = member.endPosition; return { + title: 'Remove the duplicate build function.', range: [startPosition, endPosition], code: `` }; @@ -127,6 +128,7 @@ class ValidateBuildInStructRule extends AbstractUISyntaxRule { const startPosition = param.startPosition; const endPosition = param.endPosition; return { + title: 'Remove the parmeters of the build function', range: [startPosition, endPosition], code: '' }; @@ -152,6 +154,7 @@ class ValidateBuildInStructRule extends AbstractUISyntaxRule { startPosition = arkts.SourcePosition.create(startPosition.index() - 1, startPosition.line()); const endPosition = startPosition; return { + title: 'Add a build function to the custom component', range: [startPosition, endPosition], code: 'build() {\n}\n' }; diff --git a/arkui-plugins/ui-syntax-plugins/rules/watch-decorator-function.ts b/arkui-plugins/ui-syntax-plugins/rules/watch-decorator-function.ts index bd3ea010b327bf21467b8cea11d6175dcf2138d2..fe2135a6e9a5c4ec74509f4270a6cb382897bb0b 100644 --- a/arkui-plugins/ui-syntax-plugins/rules/watch-decorator-function.ts +++ b/arkui-plugins/ui-syntax-plugins/rules/watch-decorator-function.ts @@ -158,6 +158,7 @@ class WatchDecoratorFunctionRule extends AbstractUISyntaxRule { const startPosition = member.endPosition; const endPosition = member.endPosition; return { + title: 'Add a watch function to the custom component', range: [startPosition, endPosition], code: `\n${parameterName}(){\n}`, }; @@ -181,6 +182,7 @@ class WatchDecoratorFunctionRule extends AbstractUISyntaxRule { const startPosition = parameters.startPosition; const endPosition = parameters.endPosition; return { + title: 'Remove the parameters', range: [startPosition, endPosition], code: ``, };