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