1/* 2 * Copyright (c) 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 16import ts from 'typescript'; 17import { ApiInfo, ApiType, ClassInfo, MethodInfo } from '../../../typedef/parser/ApiInfoDefination'; 18import { 19 tagsArrayOfOrder, 20 optionalTags, 21 apiLegalityCheckTypeMap, 22 permissionOptionalTags, 23} from '../../../utils/checkUtils'; 24import { Comment } from '../../../typedef/parser/Comment'; 25import { ErrorTagFormat, ErrorMessage } from '../../../typedef/checker/result_type'; 26import { CommonFunctions, conditionalOptionalTags } from '../../../utils/checkUtils'; 27 28export class LegalityCheck { 29 /** 30 * Tag's legality check 31 * @param { ApiInfo } singleApi -Individual node information. 32 * @param { Comment.JsDocInfo } apiJsdoc -Individual node JsDoc. 33 * @returns { ErrorTagFormat[] } 34 */ 35 static apiLegalityCheck(singleApi: ApiInfo, apiJsdoc: Comment.JsDocInfo): ErrorTagFormat[] { 36 const apiLegalityCheckResult: ErrorTagFormat[] = []; 37 const nodeInfo: ts.Node = singleApi.getNode() as ts.Node; 38 const apiLegalityTagsArray: string[] = apiLegalityCheckTypeMap.get(nodeInfo.kind) as string[]; 39 40 // 判断api的jsdoc中是否存在非法标签,是否缺失必选标签 41 if (Array.isArray(apiLegalityTagsArray)) { 42 const apiLegalityTagsSet: Set<string> = new Set(apiLegalityTagsArray); 43 const illegalTagsArray = LegalityCheck.getIllegalTagsArray(apiLegalityTagsArray); 44 const apiTags: Comment.CommentTag[] | undefined = apiJsdoc.tags; 45 if (apiTags === undefined) { 46 return apiLegalityCheckResult; 47 } 48 49 let paramTagNumber: number = 0; 50 let paramApiNumber: number = 51 singleApi.getApiType() === ApiType.METHOD ? (singleApi as MethodInfo).getParams().length : 0; 52 apiTags.forEach((apiTag) => { 53 paramTagNumber = apiTag.tag === 'param' ? paramTagNumber + 1 : paramTagNumber; 54 const isUseinsteadLegalSituation: boolean = apiTag.tag === 'useinstead' && apiJsdoc.deprecatedVersion !== '-1'; 55 if (illegalTagsArray.includes(apiTag.tag)) { 56 if (apiTag.tag !== 'useinstead' || !isUseinsteadLegalSituation) { 57 const apiRedundantResultFormat: ErrorTagFormat = { 58 state: false, 59 errorInfo: CommonFunctions.createErrorInfo(ErrorMessage.ERROR_USE, [apiTag.tag]), 60 }; 61 apiLegalityCheckResult.push(apiRedundantResultFormat); 62 } 63 } 64 apiLegalityTagsSet.delete('param'); 65 if (apiLegalityTagsSet.has(apiTag.tag)) { 66 apiLegalityTagsSet.delete(apiTag.tag); 67 } 68 if (singleApi.getApiType() === ApiType.INTERFACE && (apiTag.tag === 'typedef' || apiTag.tag === 'interface')) { 69 apiLegalityTagsSet.delete('typedef'); 70 apiLegalityTagsSet.delete('interface'); 71 } 72 if (singleApi.getApiType() === ApiType.METHOD && (singleApi as MethodInfo).getReturnValue().length === 0) { 73 apiLegalityTagsSet.delete('returns'); 74 } 75 if (apiLegalityTagsSet.has('extends') && (singleApi as ClassInfo).getParentClasses().length === 0) { 76 apiLegalityTagsSet.delete('extends'); 77 } 78 }); 79 // param合法性单独进行校验 80 LegalityCheck.paramLegalityCheck(paramTagNumber, paramApiNumber, apiLegalityCheckResult); 81 // 缺失标签set合集 82 apiLegalityTagsSet.forEach((apiLegalityTag) => { 83 const isSyacapOptionalSituation: boolean = 84 apiLegalityTag === 'syscap' && 85 (nodeInfo.kind === ts.SyntaxKind.ClassDeclaration || nodeInfo.kind === ts.SyntaxKind.ModuleDeclaration); 86 const isPermissionOptionalSituation: boolean = 87 apiLegalityTag === 'permission' && permissionOptionalTags.includes(nodeInfo.kind); 88 const isExtendsRequireSituation: boolean = 89 apiLegalityTag === 'extends' && (singleApi as ClassInfo).getParentClasses().length > 0; 90 91 if ( 92 !conditionalOptionalTags.includes(apiLegalityTag) && 93 (!isSyacapOptionalSituation || !isPermissionOptionalSituation || isExtendsRequireSituation) 94 ) { 95 const apiLostResultFormat: ErrorTagFormat = { 96 state: false, 97 errorInfo: CommonFunctions.createErrorInfo(ErrorMessage.ERROR_LOST_LABEL, [apiLegalityTag]), 98 }; 99 apiLegalityCheckResult.push(apiLostResultFormat); 100 } 101 }); 102 } 103 return apiLegalityCheckResult; 104 } 105 106 /** 107 * param tag legality check 108 * @param { number } paramTagNumber 109 * @param { number } paramApiNumber 110 * @param { ErrorTagFormat[] } apiLegalityCheckResult 111 */ 112 static paramLegalityCheck( 113 paramTagNumber: number, 114 paramApiNumber: number, 115 apiLegalityCheckResult: ErrorTagFormat[] 116 ): void { 117 if (paramTagNumber > paramApiNumber) { 118 const apiRedundantResultFormat: ErrorTagFormat = { 119 state: false, 120 errorInfo: CommonFunctions.createErrorInfo(ErrorMessage.ERROR_MORELABEL, [ 121 JSON.stringify(paramTagNumber - paramApiNumber), 122 'param', 123 ]), 124 }; 125 apiLegalityCheckResult.push(apiRedundantResultFormat); 126 } else if (paramTagNumber < paramApiNumber) { 127 const apiLostResultFormat: ErrorTagFormat = { 128 state: false, 129 errorInfo: CommonFunctions.createErrorInfo(ErrorMessage.ERROR_LOST_LABEL, ['param']), 130 }; 131 apiLegalityCheckResult.push(apiLostResultFormat); 132 } 133 } 134 135 /** 136 * Gets all illegal tags for the api. 137 * @param { string[] } RequiredTagsArray 138 * @returns { string[] } 139 */ 140 static getIllegalTagsArray(requiredTagsArray: string[]): string[] { 141 const illegalTagsArray: string[] = []; 142 tagsArrayOfOrder.forEach((tag) => { 143 if (!optionalTags.includes(tag) && !requiredTagsArray.includes(tag)) { 144 illegalTagsArray.push(tag); 145 } 146 }); 147 return illegalTagsArray; 148 } 149} 150