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 16/* eslint prefer-named-capture-group: 0 */ 17 18import type * as ts from 'typescript'; 19import type { DiagnosticChecker } from './DiagnosticChecker'; 20 21/* 22 * Current approach relates on error code and error message matching and it is quite fragile, 23 * so this place should be checked thoroughly in the case of typescript upgrade 24 */ 25export const TYPE_0_IS_NOT_ASSIGNABLE_TO_TYPE_1_ERROR_CODE = 2322; 26export const TYPE_UNKNOWN_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE = 27 /^Type '(.*)\bunknown\b(.*)' is not assignable to type '.*'\.$/; 28export const TYPE_NULL_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE = /^Type '(.*)\bnull\b(.*)' is not assignable to type '.*'\.$/; 29export const TYPE_UNDEFINED_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE = 30 /^Type '(.*)\bundefined\b(.*)' is not assignable to type '.*'\.$/; 31 32export const ARGUMENT_OF_TYPE_0_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_ERROR_CODE = 2345; 33export const OBJECT_IS_POSSIBLY_UNDEFINED_ERROR_CODE = 2532; 34export const NO_OVERLOAD_MATCHES_THIS_CALL_ERROR_CODE = 2769; 35export const ARGUMENT_OF_TYPE_NULL_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_RE = 36 /^Argument of type '(.*)\bnull\b(.*)' is not assignable to parameter of type '.*'\.$/; 37export const ARGUMENT_OF_TYPE_UNDEFINED_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_RE = 38 /^Argument of type '(.*)\bundefined\b(.*)' is not assignable to parameter of type '.*'\.$/; 39 40export class LibraryTypeCallDiagnosticChecker implements DiagnosticChecker { 41 inLibCall: boolean = false; 42 diagnosticMessages: Array<ts.DiagnosticMessageChain> | undefined; 43 filteredDiagnosticMessages: Set<ts.DiagnosticMessageChain>; 44 45 constructor(filteredDiagnosticMessages: Set<ts.DiagnosticMessageChain>) { 46 this.filteredDiagnosticMessages = filteredDiagnosticMessages; 47 } 48 49 configure(inLibCall: boolean, diagnosticMessages: Array<ts.DiagnosticMessageChain>): void { 50 this.inLibCall = inLibCall; 51 this.diagnosticMessages = diagnosticMessages; 52 } 53 54 checkMessageText(msg: string): boolean { 55 if (this.inLibCall) { 56 const match = 57 msg.match(ARGUMENT_OF_TYPE_NULL_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_RE) || 58 msg.match(ARGUMENT_OF_TYPE_UNDEFINED_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_RE) || 59 msg.match(TYPE_UNDEFINED_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE) || 60 msg.match(TYPE_NULL_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE); 61 return !match; 62 } 63 return true; 64 } 65 66 checkMessageChain(chain: ts.DiagnosticMessageChain): boolean { 67 if (chain.code === TYPE_0_IS_NOT_ASSIGNABLE_TO_TYPE_1_ERROR_CODE) { 68 if (chain.messageText.match(TYPE_UNKNOWN_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE)) { 69 return false; 70 } 71 if (this.inLibCall && chain.messageText.match(TYPE_UNDEFINED_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE)) { 72 return false; 73 } 74 if (this.inLibCall && chain.messageText.match(TYPE_NULL_IS_NOT_ASSIGNABLE_TO_TYPE_1_RE)) { 75 return false; 76 } 77 } 78 if (chain.code === ARGUMENT_OF_TYPE_0_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_ERROR_CODE) { 79 if ( 80 this.inLibCall && 81 chain.messageText.match(ARGUMENT_OF_TYPE_UNDEFINED_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_RE) 82 ) { 83 return false; 84 } 85 if ( 86 this.inLibCall && 87 chain.messageText.match(ARGUMENT_OF_TYPE_NULL_IS_NOT_ASSIGNABLE_TO_PARAMETER_OF_TYPE_1_RE) 88 ) { 89 return false; 90 } 91 } 92 return chain.next === undefined ? true : this.checkMessageChain(chain.next[0]); 93 } 94 95 checkFilteredDiagnosticMessages(msgText: ts.DiagnosticMessageChain | string): boolean { 96 if (this.filteredDiagnosticMessages.size === 0) { 97 return true; 98 } 99 100 if (typeof msgText !== 'string' && this.filteredDiagnosticMessages.has(msgText)) { 101 return false; 102 } 103 104 for (const msgChain of this.filteredDiagnosticMessages) { 105 if (typeof msgText === 'string') { 106 if (msgText === msgChain.messageText) { 107 return false; 108 } 109 continue; 110 } 111 112 let curMsg: ts.DiagnosticMessageChain | undefined = msgText; 113 let curFilteredMsg: ts.DiagnosticMessageChain | undefined = msgChain; 114 while (curMsg) { 115 if (!curFilteredMsg) { 116 return true; 117 } 118 119 if (curMsg.code !== curFilteredMsg.code) { 120 return true; 121 } 122 123 if (curMsg.messageText !== curFilteredMsg.messageText) { 124 return true; 125 } 126 127 curMsg = curMsg.next ? curMsg.next[0] : undefined; 128 curFilteredMsg = curFilteredMsg.next ? curFilteredMsg.next[0] : undefined; 129 } 130 131 return false; 132 } 133 return true; 134 } 135 136 checkDiagnosticMessage(msgText: string | ts.DiagnosticMessageChain): boolean { 137 if (!this.diagnosticMessages) { 138 return false; 139 } 140 141 if (this.inLibCall && !this.checkFilteredDiagnosticMessages(msgText)) { 142 return false; 143 } 144 145 if (typeof msgText === 'string') { 146 return this.checkMessageText(msgText); 147 } 148 149 if (!this.checkMessageChain(msgText)) { 150 this.diagnosticMessages.push(msgText); 151 return false; 152 } 153 return true; 154 } 155} 156