• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2025 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 type * as ts from 'typescript';
17import type { LinterOptions } from './LinterOptions';
18import type { ProblemInfo } from './ProblemInfo';
19import type { Autofix } from './autofixes/Autofixer';
20import { FileStatistics } from './statistics/FileStatistics';
21import { TsUtils } from './utils/TsUtils';
22import { cookBookRefToFixTitle } from './autofixes/AutofixTitles';
23import { faultDesc } from './FaultDesc';
24import { TypeScriptLinterConfig } from './TypeScriptLinterConfig';
25import { faultsAttrs } from './FaultAttrs';
26import { cookBookTag } from './CookBookMsg';
27import { FaultID } from './Problems';
28import { ProblemSeverity } from './ProblemSeverity';
29import { arkts2Rules, onlyArkts2SyntaxRules } from './utils/consts/ArkTS2Rules';
30
31export abstract class BaseTypeScriptLinter {
32  problemsInfos: ProblemInfo[] = [];
33  fileStats: FileStatistics;
34  tsUtils: TsUtils;
35
36  constructor(
37    protected readonly tsTypeChecker: ts.TypeChecker,
38    readonly options: LinterOptions,
39    protected sourceFile: ts.SourceFile
40  ) {
41    this.tsUtils = new TsUtils(this.tsTypeChecker, options);
42    this.fileStats = new FileStatistics(sourceFile, this.problemsInfos);
43  }
44
45  protected getLineAndCharacterOfNode(node: ts.Node | ts.CommentRange): ts.LineAndCharacter {
46    const startPos = TsUtils.getStartPos(node);
47    const { line, character } = this.sourceFile.getLineAndCharacterOfPosition(startPos);
48    // TSC counts lines and columns from zero
49    return { line: line + 1, character: character + 1 };
50  }
51
52  abstract lint(): void;
53
54  protected updateFileStats(faultId: number, line: number): void {
55    this.fileStats.nodeCounters[faultId]++;
56    this.fileStats.lineCounters[faultId].add(line);
57  }
58
59  protected static addLineColumnInfoInAutofix(
60    autofix: Autofix[],
61    startPos: ts.LineAndCharacter,
62    endPos: ts.LineAndCharacter
63  ): Autofix[] {
64    return autofix?.map((autofixElem) => {
65      autofixElem.line = startPos.line + 1;
66      autofixElem.column = startPos.character + 1;
67      autofixElem.endLine = endPos.line + 1;
68      autofixElem.endColumn = endPos.character + 1;
69      return autofixElem;
70    });
71  }
72
73  protected incrementCounters(
74    node: ts.Node | ts.CommentRange,
75    faultId: number,
76    autofix?: Autofix[],
77    errorMsg?: string
78  ): void {
79    const badNodeInfo = this.getbadNodeInfo(node, faultId, autofix, errorMsg);
80
81    if (this.shouldSkipRule(badNodeInfo)) {
82      return;
83    }
84
85    this.problemsInfos.push(badNodeInfo);
86    this.updateFileStats(faultId, badNodeInfo.line);
87    // problems with autofixes might be collected separately
88    if (this.options.reportAutofixCb && badNodeInfo.autofix) {
89      this.options.reportAutofixCb(badNodeInfo);
90    }
91  }
92
93  private getbadNodeInfo(
94    node: ts.Node | ts.CommentRange,
95    faultId: number,
96    autofix?: Autofix[],
97    errorMsg?: string
98  ): ProblemInfo {
99    const [startOffset, endOffset] = TsUtils.getHighlightRange(node, faultId);
100    const startPos = this.sourceFile.getLineAndCharacterOfPosition(startOffset);
101    const endPos = this.sourceFile.getLineAndCharacterOfPosition(endOffset);
102    const faultDescr = faultDesc[faultId];
103    const faultType = TypeScriptLinterConfig.tsSyntaxKindNames[node.kind];
104    const cookBookMsgNum = faultsAttrs[faultId] ? faultsAttrs[faultId].cookBookRef : 0;
105    const cookBookTg = errorMsg ? errorMsg : cookBookTag[cookBookMsgNum];
106    const severity = faultsAttrs[faultId]?.severity ?? ProblemSeverity.ERROR;
107    const isMsgNumValid = cookBookMsgNum > 0;
108    autofix = BaseTypeScriptLinter.processAutofix(autofix, startPos, endPos);
109    const badNodeInfo: ProblemInfo = {
110      line: startPos.line + 1,
111      column: startPos.character + 1,
112      endLine: endPos.line + 1,
113      endColumn: endPos.character + 1,
114      start: startOffset,
115      end: endOffset,
116      type: faultType,
117      severity: severity,
118      faultId: faultId,
119      problem: FaultID[faultId],
120      suggest: '',
121      // eslint-disable-next-line no-nested-ternary
122      rule: isMsgNumValid && cookBookTg !== '' ? cookBookTg : faultDescr ? faultDescr : faultType,
123      ruleTag: cookBookMsgNum,
124      autofixable: !!autofix,
125      autofix: autofix,
126      autofixTitle: isMsgNumValid && autofix !== undefined ? cookBookRefToFixTitle.get(cookBookMsgNum) : undefined
127    };
128    return badNodeInfo;
129  }
130
131  private static processAutofix(
132    autofix: Autofix[] | undefined,
133    startPos: ts.LineAndCharacter,
134    endPos: ts.LineAndCharacter
135  ): Autofix[] | undefined {
136    return autofix ? BaseTypeScriptLinter.addLineColumnInfoInAutofix(autofix, startPos, endPos) : autofix;
137  }
138
139  private shouldSkipRule(badNodeInfo: ProblemInfo): boolean {
140    const ruleConfigTags = this.options.ruleConfigTags;
141    if (ruleConfigTags && !ruleConfigTags.has(badNodeInfo.ruleTag)) {
142      return true;
143    }
144    if (this.options?.ideInteractive) {
145      if (this.options.onlySyntax) {
146        if (onlyArkts2SyntaxRules.has(badNodeInfo.ruleTag)) {
147          return false;
148        }
149      } else if (this.options.arkts2 && arkts2Rules.includes(badNodeInfo.ruleTag)) {
150        return false;
151      }
152      return true;
153    }
154    return false;
155  }
156}
157