• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2# coding=utf-8
3##############################################
4# Copyright (c) 2021-2022 Huawei Device Co., Ltd.
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#     http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16##############################################
17
18import json
19import os
20import subprocess
21
22from clang.cindex import CursorKind
23
24from typedef.check.check import DocInfo, FileDocInfo, TAGS, CheckErrorMessage, CheckOutPut, CommentStartEndValue
25
26current_file = os.path.dirname(__file__)
27# permission数据来源于https://gitee.com/openharmony/utils_system_resources/raw/master/systemres/main/config.json
28permission_tag_rules = ['ohos.permission.HEALTH_DATA', 'ohos.permission.HEART_RATE', 'ohos.permission.ACCELERATION']
29with open(os.path.abspath(os.path.join(current_file, "rules/perssion_rule.json"))) as json_file:
30    permission_file_content = json.load(json_file)
31    permission_tag_rules.extend([item['name'] for item in permission_file_content['module']['definePermissions']])
32syscap_tag_rules: list = []
33with open(os.path.abspath(os.path.join(current_file, "rules/syscap_rule.json"))) as json_file:
34    syscap_tag_rules = json.load(json_file)
35
36
37def get_tage_start_and_end(tag_info, comment_start_line, comment_str, api_info):
38    value = {
39        'code_context_start_line': comment_start_line,
40        'main_buggy_code': comment_str,
41        # todo 多段注释无法确认具体行数
42        'main_buggy_line': comment_start_line + comment_str.count('\n'),
43    }
44    if tag_info is None:
45        return value
46    value['code_context_start_line'] = comment_start_line + tag_info['source'][0]['number']
47    value['main_buggy_code'] = ''
48    for element in tag_info['source']:
49        tag_source = "{}{}".format(element['source'], '\n')
50        value['main_buggy_code'] = "{}{}".format(value['main_buggy_code'], tag_source)
51    value['main_buggy_line'] = comment_start_line + tag_info['source'][len(tag_info['source'])-1]['number']
52    return value
53
54
55def process_tag_addtogroup(tag_info, file_doc_info: FileDocInfo, api_info, comment_str, comment_start_line) -> list:
56    api_result_info_list = []
57    group_name = tag_info['name']
58    if group_name == "":
59        api_result_info = set_value_to_result(api_info, CheckErrorMessage.API_DOC_GLOBAL_11.name,
60                                              get_tage_start_and_end(tag_info, comment_start_line,
61                                                                     comment_str, api_info))
62        api_result_info_list.append(api_result_info)
63    return api_result_info_list
64
65
66def process_tag_brief(tag_info, file_doc_info: FileDocInfo, api_info, comment_str, comment_start_line) -> list:
67    doc_info = file_doc_info.curr_doc_info
68    api_result_info_list = []
69    brief = "{}{}".format(tag_info['name'], tag_info['description'])
70    doc_info.brief = brief
71    return api_result_info_list
72
73
74def process_tag_deprecated(tag_info, file_doc_info: FileDocInfo, api_info, comment_str, comment_start_line) -> list:
75    doc_info = file_doc_info.curr_doc_info
76    api_result_info_list = []
77    name = tag_info['name']
78    version: str = tag_info['description']
79    if name != "since" or not version.isdigit():
80        api_result_info = set_value_to_result(api_info, CheckErrorMessage.API_DOC_UNIVERSAL_01.name,
81                                              get_tage_start_and_end(tag_info, comment_start_line,
82                                                                     comment_str, api_info))
83        api_result_info_list.append(api_result_info)
84    doc_info.deprecated = version
85    return api_result_info_list
86
87
88def process_tag_file(tag_info, file_doc_info: FileDocInfo, api_info, comment_str, comment_start_line) -> list:
89    doc_info = file_doc_info.curr_doc_info
90    api_result_info_list = []
91    file_name = tag_info['name']
92    if file_name == "":
93        api_result_info = set_value_to_result(api_info, CheckErrorMessage.API_DOC_GLOBAL_12.name,
94                                              get_tage_start_and_end(tag_info, comment_start_line,
95                                                                     comment_str, api_info))
96        api_result_info_list.append(api_result_info)
97    doc_info.file = file_name
98    return api_result_info_list
99
100
101def process_tag_library(tag_info, file_doc_info: FileDocInfo, api_info, comment_str, comment_start_line) -> list:
102    api_result_info_list = []
103    library: str = '{}{}'.format(tag_info['name'], tag_info['description'])
104    if not library.endswith(('.so', '.a')) and library != "NA":
105        api_result_info = set_value_to_result(api_info, CheckErrorMessage.API_DOC_GLOBAL_13.name,
106                                              get_tage_start_and_end(tag_info, comment_start_line,
107                                                                     comment_str, api_info))
108        api_result_info_list.append(api_result_info)
109    if not library == library.lower():
110        api_result_info = set_value_to_result(api_info, CheckErrorMessage.API_DOC_GLOBAL_16.name,
111                                              get_tage_start_and_end(tag_info, comment_start_line,
112                                                                     comment_str, api_info))
113        api_result_info_list.append(api_result_info)
114    return api_result_info_list
115
116
117def process_tag_param(tag_info, file_doc_info: FileDocInfo, api_info, comment_str, comment_start_line) -> list:
118    file_doc_info.curr_doc_info.param_index += 1
119    api_result_info_list = []
120    if api_info['kind'] != CursorKind.FUNCTION_DECL.name:
121        return api_result_info_list
122
123    if 'parm' not in api_info.keys():
124        api_result_info = set_value_to_result(api_info, CheckErrorMessage.API_DOC_FUNCTION_01.name,
125                                              get_tage_start_and_end(tag_info, comment_start_line,
126                                                                     comment_str, api_info))
127        api_result_info_list.append(api_result_info)
128        return api_result_info_list
129    index = file_doc_info.curr_doc_info.param_index
130    params = api_info['parm']
131    if len(params) < index + 1:
132        return api_result_info_list
133    param = api_info['parm'][index]
134    if tag_info['name'] != param['name']:
135        main_buggy_code = get_main_buggy_code(api_info)
136        api_result_info = CheckOutPut(os.path.abspath(os.path.join(api_info['gn_path'],
137                                                                   api_info['location']['location_path'])),
138                                      api_info['location']['location_line'], CheckErrorMessage.API_DOC_FUNCTION_02.name,
139                                      CheckErrorMessage.__getitem__(CheckErrorMessage.API_DOC_FUNCTION_02.name).value
140                                      .replace('$$', tag_info['name']).replace('&&', param['name']),
141                                      main_buggy_code, api_info['location']['location_line'])
142        api_result_info_list.append(api_result_info)
143
144    return api_result_info_list
145
146
147def process_tag_return(tag_info, file_doc_info: FileDocInfo, api_info, comment_str, comment_start_line) -> list:
148    api_result_info_list = []
149    return api_result_info_list
150
151
152def process_tag_since(tag_info, file_doc_info: FileDocInfo, api_info, comment_str, comment_start_line) -> list:
153    doc_info = file_doc_info.curr_doc_info
154    api_result_info_list = []
155    value: str = '{}{}'.format(tag_info['name'], tag_info['description'])
156    if value == "":
157        api_result_info = set_value_to_result(api_info, CheckErrorMessage.API_DOC_UNIVERSAL_03.name,
158                                              get_tage_start_and_end(tag_info, comment_start_line,
159                                                                     comment_str, api_info))
160        api_result_info_list.append(api_result_info)
161    elif not value.isdigit():
162        api_result_info = set_value_to_result(api_info, CheckErrorMessage.API_DOC_UNIVERSAL_04.name,
163                                              get_tage_start_and_end(tag_info, comment_start_line,
164                                                                     comment_str, api_info))
165        api_result_info_list.append(api_result_info)
166    doc_info.since = tag_info['name']
167    return api_result_info_list
168
169
170def process_tag_syscap(tag_info, file_doc_info: FileDocInfo, api_info, comment_str, comment_start_line) -> list:
171    doc_info = file_doc_info.curr_doc_info
172    api_result_info_list = []
173    syscap = '{}{}'.format(tag_info['name'], tag_info['description'])
174    if syscap == "":
175        api_result_info = set_value_to_result(api_info, CheckErrorMessage.API_DOC_UNIVERSAL_05.name,
176                                              get_tage_start_and_end(tag_info, comment_start_line,
177                                                                     comment_str, api_info))
178        api_result_info_list.append(api_result_info)
179    doc_info.syscap = syscap
180    return api_result_info_list
181
182
183def process_tag_left_brace(tag_info, file_doc_info: FileDocInfo, api_info, comment_str, comment_start_line) -> list:
184    api_result_info_list = []
185    if not file_doc_info.is_in_group_tag:
186        api_result_info = set_value_to_result(api_info, CheckErrorMessage.API_DOC_GLOBAL_14.name,
187                                              get_tage_start_and_end(tag_info, comment_start_line,
188                                                                     comment_str, api_info))
189        api_result_info_list.append(api_result_info)
190    return api_result_info_list
191
192
193def process_tag_right_brace(tag_info, file_doc_info: FileDocInfo, api_info, comment_str, comment_start_line) -> list:
194    api_result_info_list = []
195    if not file_doc_info.has_group_end:
196        file_doc_info.has_group_end = True
197        return api_result_info_list
198    api_result_info = set_value_to_result(api_info, CheckErrorMessage.API_DOC_GLOBAL_21.name,
199                                          get_tage_start_and_end(tag_info,
200                                                                 comment_start_line - tag_info['source'][0]['number'],
201                                                                 comment_str, api_info))
202    api_result_info_list.append(api_result_info)
203    return api_result_info_list
204
205
206process_tag_function = {
207    TAGS['ADD_TO_GROUP'].value: process_tag_addtogroup,
208    TAGS['BRIEF'].value: process_tag_brief,
209    TAGS['DEPRECATED'].value: process_tag_deprecated,
210    TAGS['FILE'].value: process_tag_file,
211    TAGS['LIBRARY'].value: process_tag_library,
212    TAGS['PARAM'].value: process_tag_param,
213    TAGS['RETURN'].value: process_tag_return,
214    TAGS['SINCE'].value: process_tag_since,
215    TAGS['SYSCAP'].value: process_tag_syscap,
216    TAGS['LEFT_BRACE'].value: process_tag_left_brace,
217    TAGS['RIGHT_BRACE'].value: process_tag_right_brace,
218}
219
220
221def process_each_tags(tag_info, file_doc_info: FileDocInfo, api_info, comment_str, comment_start_line) -> list:
222    '''
223    处理解析出来的每个tag标签
224    '''
225    api_result_info_list = []
226    doc_info = file_doc_info.curr_doc_info
227    tag: str = tag_info['tag']
228    if tag.lower() != tag:
229        tag = tag.lower()
230        api_result_info = set_value_to_result(api_info, CheckErrorMessage.API_DOC_GLOBAL_15.name,
231                                              get_tage_start_and_end(tag_info, comment_start_line,
232                                                                     comment_str, api_info))
233        api_result_info_list.append(api_result_info)
234    if tag not in process_tag_function.keys():
235        return []
236    tag_process = process_tag_function[tag]
237    api_result_info_list.extend(tag_process(tag_info, file_doc_info, api_info, comment_str, comment_start_line))
238    return api_result_info_list
239
240
241def process_file_doc_group(file_doc_info: FileDocInfo, item, api_info, comment_start_line) -> list:
242    '''
243    处理每个文件中头文件中的addtogroup
244    '''
245    api_result_info_list = []
246    if item['tag'] == 'addtogroup':
247        if file_doc_info.group_name is None:
248            file_doc_info.group_name = item['name']
249            file_doc_info.is_in_group_tag = True
250            # todo 在调用地方修改start_line
251        else:
252            api_result_info = set_value_to_result(api_info, CheckErrorMessage.API_DOC_GLOBAL_04.name,
253                                                  get_tage_start_and_end(None, comment_start_line,
254                                                                         file_doc_info.group_comment_str, api_info))
255            api_result_info_list.append(api_result_info)
256    if item['tag'] == '{':
257        if not file_doc_info.has_group_start:
258            file_doc_info.has_group_start = True
259        else:
260            # todo 在调用地方修改start_line
261            api_result_info = set_value_to_result(api_info, CheckErrorMessage.API_DOC_GLOBAL_06.name,
262                                                  get_tage_start_and_end(None, comment_start_line,
263                                                                         file_doc_info.group_comment_str, api_info))
264            api_result_info_list.append(api_result_info)
265    elif item['tag'] == 'brief':
266        file_doc_info.group_brief = '{}{}'.format(item['name'], item['description'])
267    elif item['tag'] == 'library':
268        file_doc_info.group_library = '{}{}'.format(item['name'], item['description'])
269    elif item['tag'] == 'syscap':
270        file_doc_info.group_syscap = '{}{}'.format(item['name'], item['description'])
271    return api_result_info_list
272
273
274def process_file_doc_file(file_doc_info: FileDocInfo, item, api_info, comment_start_line) -> list:
275    '''
276    处理每个文件中头文件中的file
277    '''
278    api_result_info_list = []
279    if item['tag'] == 'file':
280        if file_doc_info.file_name is None:
281            file_doc_info.file_name = item['name']
282            file_doc_info.is_in_file_tag = True
283        else:
284            # todo 在调用地方修改start_line
285            api_result_info = set_value_to_result(api_info, CheckErrorMessage.API_DOC_GLOBAL_01.name,
286                                                  get_tage_start_and_end(None, comment_start_line,
287                                                                         file_doc_info.file_comment_str, api_info))
288            api_result_info_list.append(api_result_info)
289    elif item['tag'] == 'brief':
290        file_doc_info.file_brief = '{}{}'.format(item['name'], item['description'])
291    elif item['tag'] == 'library':
292        file_doc_info.file_library = '{}{}'.format(item['name'], item['description'])
293    elif item['tag'] == 'syscap':
294        file_doc_info.file_syscap = '{}{}'.format(item['name'], item['description'])
295    elif item['tag'] == 'since':
296        file_doc_info.file_since = True
297    return api_result_info_list
298
299
300def get_source_doc(comment_object):
301    comment_str = ''
302    for item in comment_object['source']:
303        doc_source = '{}{}'.format(item['source'], '\n')
304        comment_str = '{}{}'.format(comment_str, doc_source)
305    return comment_str
306
307
308def handle_file_group_comments(comment_object, file_doc_info: FileDocInfo, api_info, comment_start_line) -> list:
309    comment_object_start = comment_object['source'][0]['number']
310    comment_str = get_source_doc(comment_object)
311    api_result_info_list = []
312    tags = comment_object['tags']
313    for item in tags:
314        # 处理文件组数据,一个文件只有一个,是否需要判断多个存在?
315        if item['tag'] == 'addtogroup' or file_doc_info.is_in_group_tag:
316            file_doc_info.group_comment_str = comment_str
317            api_result_info_list.extend(process_file_doc_group(file_doc_info, item, api_info,
318                                                               comment_start_line + comment_object_start))
319        # 处理文件中file说明,一个文件只有一个,是否需要判断多个存在?
320        if item['tag'] == 'file' or file_doc_info.is_in_file_tag:
321            file_doc_info.file_comment_str = comment_str
322            api_result_info_list.extend(process_file_doc_file(file_doc_info, item, api_info,
323                                                              comment_start_line + comment_object_start))
324        api_result_info_list.extend(process_each_tags(item, file_doc_info, api_info, comment_str,
325                                                      comment_start_line))
326    return api_result_info_list
327
328
329def handle_each_comment_of_file(file_doc_info: FileDocInfo, api_info,
330                                comment_start_line, comment_object_start) -> list:
331    api_result_info_list = []
332    if file_doc_info.is_in_file_tag:
333        comment_str = file_doc_info.file_comment_str
334        if file_doc_info.file_brief is None:
335            api_result_info = set_value_to_result(api_info, CheckErrorMessage.API_DOC_GLOBAL_17.name,
336                                                  get_tage_start_and_end(None,
337                                                                         comment_start_line + comment_object_start,
338                                                                         comment_str, api_info))
339            api_result_info_list.append(api_result_info)
340        if file_doc_info.file_library is None:
341            api_result_info = set_value_to_result(api_info, CheckErrorMessage.API_DOC_GLOBAL_18.name,
342                                                  get_tage_start_and_end(None,
343                                                                         comment_start_line + comment_object_start,
344                                                                         comment_str, api_info))
345            api_result_info_list.append(api_result_info)
346        if file_doc_info.file_syscap is None:
347            api_result_info = set_value_to_result(api_info, CheckErrorMessage.API_DOC_GLOBAL_19.name,
348                                                  get_tage_start_and_end(None,
349                                                                         comment_start_line + comment_object_start,
350                                                                         comment_str, api_info))
351            api_result_info_list.append(api_result_info)
352        if not file_doc_info.file_since:
353            api_result_info = set_value_to_result(api_info, CheckErrorMessage.API_DOC_GLOBAL_20.name,
354                                                  get_tage_start_and_end(None,
355                                                                         comment_start_line + comment_object_start,
356                                                                         comment_str, api_info))
357            api_result_info_list.append(api_result_info)
358    return api_result_info_list
359
360
361# 第二步
362def process_each_comment(comment_object, file_doc_info: FileDocInfo, api_info, comment_start_line) -> list:
363    '''
364    检查单个comment对应的报错信息,不处理文件级别的校验
365    '''
366    comment_object_start = comment_object['source'][0]['number']
367    api_result_info_list = []
368    file_doc_info.curr_doc_info = DocInfo()  # 每段doc返回的数据格式
369    result_info_list = handle_file_group_comments(comment_object, file_doc_info, api_info, comment_start_line)
370    api_result_info_list.extend(result_info_list)
371    # 判断param标签的数量和方法参数的数量是否对应
372    param_tag_count = file_doc_info.curr_doc_info.param_index + 1
373    if api_info['kind'] == CursorKind.FUNCTION_DECL.name and \
374            'parm' in api_info.keys() and len(api_info['parm']) != param_tag_count:
375        api_result_info = set_value_to_result(api_info, CheckErrorMessage.API_DOC_FUNCTION_01.name,
376                                              get_tage_start_and_end(None,
377                                                                     comment_start_line + comment_object_start,
378                                                                     api_info['comment'], api_info))
379        api_result_info_list.append(api_result_info)
380    # 判断group标签的开头
381    if file_doc_info.is_in_group_tag:
382        comment_str = file_doc_info.group_comment_str
383        if not file_doc_info.has_group_start:
384            api_result_info = set_value_to_result(api_info, CheckErrorMessage.API_DOC_GLOBAL_05.name,
385                                                  get_tage_start_and_end(None,
386                                                                         comment_start_line + comment_object_start,
387                                                                         comment_str, api_info))
388            api_result_info_list.append(api_result_info)
389        if file_doc_info.group_brief is None:
390            api_result_info = set_value_to_result(api_info, CheckErrorMessage.API_DOC_GLOBAL_02.name,
391                                                  get_tage_start_and_end(None,
392                                                                         comment_start_line + comment_object_start,
393                                                                         comment_str, api_info))
394            api_result_info_list.append(api_result_info)
395    # 处理file标签的值
396    handle_result_info_list = handle_each_comment_of_file(file_doc_info, api_info,
397                                                          comment_start_line, comment_object_start)
398    api_result_info_list.extend(handle_result_info_list)
399    return api_result_info_list
400
401
402def process_tag_missing(comment_object, file_doc_info: FileDocInfo, api_info, comment_start_line):
403    comment_object_start = comment_object['source'][0]['number']
404    api_result_info_list = []
405    if api_info['kind'] not in process_tag_missing_function.keys():
406        return []
407    has_since_tag = False
408    tags = comment_object['tags']
409    for item in tags:
410        if item['tag'] == 'since':
411            has_since_tag = True
412    if not has_since_tag:
413        api_result_info = set_value_to_result(api_info, CheckErrorMessage.API_DOC_GLOBAL_20.name,
414                                              get_tage_start_and_end(None,
415                                                                     comment_start_line + comment_object_start,
416                                                                     api_info['comment'], api_info))
417        api_result_info_list.append(api_result_info)
418    return api_result_info_list
419
420
421process_tag_missing_function = {
422    CursorKind.FUNCTION_DECL.name: [],
423    CursorKind.UNION_DECL.name: [],
424    CursorKind.STRUCT_DECL.name: [],
425    CursorKind.ENUM_DECL.name: [],
426    CursorKind.TYPEDEF_DECL.name: [],
427    CursorKind.VAR_DECL.name: [],
428    CursorKind.MACRO_DEFINITION.name: [],
429}
430
431
432# 第一步
433def process_comment(comment: str, file_doc_info: FileDocInfo, api_info) -> list:
434    '''
435    处理comment数据,通过node调用comment-parser解析doc注释
436    '''
437    comment_start_line = -1
438    api_result_info_list = []
439    if comment == "none_comment":
440        return api_result_info_list
441    result = subprocess.check_output(
442        ['node', os.path.abspath(os.path.join(current_file, "comment_parser.js")), comment])  # 解析comment
443    result_json = json.loads(result.decode('utf-8'))
444    if api_info['kind'] == CursorKind.TRANSLATION_UNIT.name:
445        comment_start_line = 1
446    else:
447        comment_start_line = api_info['location']['location_line'] - comment.count('\n') - 1
448    for index, item in enumerate(result_json):
449        if api_info['kind'] == CursorKind.TRANSLATION_UNIT.name and len(item['tags']) > 0 and\
450                item['tags'][0]['tag'] == '}' and 'line_list' in api_info and index <= len(api_info['line_list']) - 1:
451            comment_start_line = api_info['line_list'][index]
452        api_result_info_list.extend(process_each_comment(item, file_doc_info, api_info, comment_start_line))
453        if index == len(result_json) - 1:
454            api_result_info_list.extend(process_tag_missing(item, file_doc_info, api_info, comment_start_line))
455        file_doc_info.is_in_group_tag = False  # 初始化group标签判断
456        file_doc_info.is_in_file_tag = False  # 初始化file标签判断
457    return api_result_info_list
458
459
460def process_file_doc_info(file_doc_info: FileDocInfo, file_info, comment_start_line) -> list:
461    '''
462    根据文件信息校验文件级错误信息
463    '''
464    api_result_info_list = []
465    # 处理group说明
466    if file_doc_info.group_name is None:
467        api_result_info = set_value_to_result(file_info, CheckErrorMessage.API_DOC_GLOBAL_09.name,
468                                              get_tage_start_and_end(None, comment_start_line,
469                                                                     file_doc_info.group_comment_str, file_info))
470        api_result_info_list.append(api_result_info)
471    else:
472        # 判断group标签的结尾
473        if not file_doc_info.has_group_end:
474            api_result_info = set_value_to_result(file_info, CheckErrorMessage.API_DOC_GLOBAL_10.name,
475                                                  get_tage_start_and_end(None, comment_start_line,
476                                                                         file_doc_info.group_comment_str, file_info))
477            api_result_info.mainBuggyLine = CommentStartEndValue.DEFAULT_END.value
478            api_result_info_list.append(api_result_info)
479    # 处理file说明
480    if file_doc_info.file_name is None:
481        # todo 在调用地方修改start_line
482        api_result_info = set_value_to_result(file_info, CheckErrorMessage.API_DOC_GLOBAL_03.name,
483                                              get_tage_start_and_end(None, comment_start_line,
484                                                                     file_doc_info.file_comment_str, file_info))
485        api_result_info_list.append(api_result_info)
486
487    return api_result_info_list
488
489
490def set_value_to_result(api_info, command, comment_value_info):
491    main_buggy_code = comment_value_info['main_buggy_code'] if (comment_value_info['main_buggy_code']
492                                                                != '') else get_main_buggy_code(api_info)
493    code_context_start_line = comment_value_info['code_context_start_line']
494    main_buggy_line = comment_value_info['main_buggy_line']
495    description = CheckErrorMessage.__getitem__(command).value
496    return CheckOutPut(os.path.abspath(os.path.join(api_info['gn_path'], api_info['location']['location_path'])),
497                       code_context_start_line,
498                       command,
499                       description,
500                       main_buggy_code,
501                       main_buggy_line)
502
503
504def get_main_buggy_code(api_info):
505    main_buggy_code = '' if (len(api_info['node_content']) == 0) else api_info['node_content']['content']
506    return main_buggy_code
507