• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2022-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import * as ts from 'typescript';
17
18import { FaultID } from '../utils/lib/FaultId';
19import { visitVisitResult } from './utils/ASTHelpers';
20
21export class Autofixer {
22  private readonly typeChecker: ts.TypeChecker;
23  private readonly context: ts.TransformationContext;
24
25  constructor(typeChecker: ts.TypeChecker, context: ts.TransformationContext) {
26    this.typeChecker = typeChecker;
27    this.context = context;
28  }
29
30  private readonly autofixes = new Map<ts.SyntaxKind, ts.Visitor[]>([
31    [ts.SyntaxKind.VariableDeclarationList, [this[FaultID.VarDeclaration].bind(this)]],
32    [ts.SyntaxKind.PropertyDeclaration, [this[FaultID.PrivateIdentifier].bind(this)]],
33    [ts.SyntaxKind.SourceFile, [this[FaultID.ImportAfterStatement].bind(this)]]
34  ]);
35
36  fixNode(node: ts.Node): ts.VisitResult<ts.Node> {
37    const autofixes = this.autofixes.get(node.kind);
38
39    if (autofixes === undefined) {
40      return node;
41    }
42
43    let result: ts.VisitResult<ts.Node> = node;
44
45    for (const autofix of autofixes) {
46      result = visitVisitResult(result, autofix);
47    }
48
49    return result;
50  }
51
52  /**
53   * Rule: `arkts-no-var`
54   */
55  private [FaultID.VarDeclaration](node: ts.Node): ts.VisitResult<ts.Node> {
56    if (ts.isVariableDeclarationList(node)) {
57      const isLetDeclaration = node.flags & ts.NodeFlags.Let;
58      const isConstDeclaration = node.flags & ts.NodeFlags.Const;
59
60      if (!isLetDeclaration && !isConstDeclaration) {
61        const newFlags = node.flags | ts.NodeFlags.Let;
62        return this.context.factory.createVariableDeclarationList(node.declarations, newFlags);
63      }
64    }
65
66    return node;
67  }
68
69  /**
70   * Rule: `arkts-no-private-identifiers`
71   */
72  private [FaultID.PrivateIdentifier](node: ts.Node): ts.VisitResult<ts.Node> {
73    void this;
74
75    /*
76     * Since we can access only public members of the imported dynamic class,
77     * there is no need to fix private fields, we just do not emit them
78     */
79    if (ts.isPropertyDeclaration(node)) {
80      if (
81        ts.isPrivateIdentifier(node.name) ||
82        node.modifiers?.find((v) => {
83          return v.kind === ts.SyntaxKind.PrivateKeyword;
84        })
85      ) {
86        return undefined;
87      }
88    }
89
90    return node;
91  }
92
93  /**
94   * Rule: `arkts-no-misplaced-imports`
95   */
96  private [FaultID.ImportAfterStatement](node: ts.Node): ts.VisitResult<ts.Node> {
97    void this;
98
99    /**
100     * This algorithm is very very bad for both memory and performance.
101     * Redo later, when implementing other autofixes
102     */
103
104    if (ts.isSourceFile(node)) {
105      const importDeclarations: ts.Statement[] = [];
106      const statements: ts.Statement[] = [];
107
108      for (const stmt of node.statements) {
109        if (ts.isImportDeclaration(stmt)) {
110          importDeclarations.push(stmt);
111        } else {
112          statements.push(stmt);
113        }
114      }
115
116      return this.context.factory.updateSourceFile(node, [...importDeclarations, ...statements]);
117    }
118
119    return node;
120  }
121}
122