• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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