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