1/* 2 * Copyright (c) 2023-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'; 17import type { ProblemInfo } from '../ProblemInfo'; 18import { ProblemSeverity } from '../ProblemSeverity'; 19import { getStrictDiagnostics } from './TypeScriptDiagnosticsExtractor'; 20import { FaultID } from '../Problems'; 21import { faultsAttrs } from '../FaultAttrs'; 22 23export interface TSCCompiledProgram { 24 getProgram: () => ts.Program; 25 getStrictDiagnostics: (fileName: string) => ts.Diagnostic[]; 26} 27 28export class TSCCompiledProgramSimple implements TSCCompiledProgram { 29 private readonly program: ts.Program; 30 31 constructor(program: ts.Program) { 32 this.program = program; 33 } 34 35 getProgram(): ts.Program { 36 return this.program; 37 } 38 39 getStrictDiagnostics(fileName: string): ts.Diagnostic[] { 40 void this; 41 void fileName; 42 return []; 43 } 44} 45 46export type ProgressCallback = (message: string) => void; 47 48export class TSCCompiledProgramWithDiagnostics implements TSCCompiledProgram { 49 private readonly program: ts.Program; 50 private readonly cachedDiagnostics: Map<string, ts.Diagnostic[]> = new Map(); 51 52 constructor( 53 strict: ts.Program, 54 nonStrict: ts.Program, 55 inputFiles: string[], 56 cancellationToken?: ts.CancellationToken, 57 progressCb?: ProgressCallback 58 ) { 59 this.program = strict; 60 61 inputFiles.forEach((fileName) => { 62 progressCb?.(fileName); 63 64 const sourceFile = this.program.getSourceFile(fileName); 65 if (sourceFile !== undefined) { 66 this.cachedDiagnostics.set( 67 sourceFile.fileName, 68 getStrictDiagnostics(strict, nonStrict, sourceFile.fileName, cancellationToken) 69 ); 70 } 71 }); 72 } 73 74 getProgram(): ts.Program { 75 return this.program; 76 } 77 78 getStrictDiagnostics(fileName: string): ts.Diagnostic[] { 79 return this.cachedDiagnostics.get(fileName) ?? []; 80 } 81} 82 83export function transformDiagnostic(diagnostic: ts.Diagnostic): ProblemInfo { 84 const startPos = diagnostic.start!; 85 const start = getLineAndColumn(diagnostic.file!, startPos); 86 const endPos = startPos + diagnostic.length!; 87 const end = getLineAndColumn(diagnostic.file!, endPos); 88 const messageText = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'); 89 const faultId = FaultID.StrictDiagnostic; 90 91 return { 92 line: start.line, 93 column: start.column, 94 endLine: end.line, 95 endColumn: end.column, 96 start: startPos, 97 end: endPos, 98 type: 'StrictModeError', 99 // expect strict options to always present 100 severity: ProblemSeverity.ERROR, 101 problem: FaultID[faultId], 102 suggest: messageText, 103 rule: messageText, 104 ruleTag: faultsAttrs[faultId] ? faultsAttrs[faultId].cookBookRef : 0 105 }; 106} 107 108/** 109 * Returns line and column of the position, counts from 1 110 */ 111function getLineAndColumn(file: ts.SourceFile, position: number): { line: number; column: number } { 112 const { line, character } = file.getLineAndCharacterOfPosition(position); 113 // TSC counts lines and columns from zero 114 return { 115 line: line + 1, 116 column: character + 1 117 }; 118} 119