blob: 562a365c4f9d3e96fec8753714948412ec5d078a [file] [log] [blame]
Yuto Takano39639672021-08-05 19:47:48 +01001#!/usr/bin/env python3
2#
3# Copyright The Mbed TLS Contributors
4# SPDX-License-Identifier: Apache-2.0
5#
6# Licensed under the Apache License, Version 2.0 (the "License"); you may
7# not use this file except in compliance with the License.
8# You may obtain a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing, software
13# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
14# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15# See the License for the specific language governing permissions and
16# limitations under the License.
17
Darryl Greend5802922018-05-08 15:30:59 +010018"""
Yuto Takano39639672021-08-05 19:47:48 +010019This script confirms that the naming of all symbols and identifiers in Mbed TLS
Yuto Takano159255a2021-08-06 17:00:28 +010020are consistent with the house style and are also self-consistent. It only runs
21on Linux and macOS since it depends on nm.
22
Yuto Takano55c6c872021-08-09 15:35:19 +010023It contains two major Python classes, CodeParser and NameChecker. They both have
24a comprehensive "run-all" function (comprehensive_parse() and perform_checks())
25but the individual functions can also be used for specific needs.
26
27CodeParser makes heavy use of regular expressions to parse the code, and is
28dependent on the current code formatting. Many Python C parser libraries require
29preprocessed C code, which means no macro parsing. Compiler tools are also not
30very helpful when we want the exact location in the original source (which
31becomes impossible when e.g. comments are stripped).
32
33NameChecker performs the following checks:
Yuto Takano81528c02021-08-06 16:22:06 +010034
35- All exported and available symbols in the library object files, are explicitly
Yuto Takano159255a2021-08-06 17:00:28 +010036 declared in the header files. This uses the nm command.
Yuto Takano81528c02021-08-06 16:22:06 +010037- All macros, constants, and identifiers (function names, struct names, etc)
Yuto Takano55c6c872021-08-09 15:35:19 +010038 follow the required regex pattern.
Yuto Takano81528c02021-08-06 16:22:06 +010039- Typo checking: All words that begin with MBED exist as macros or constants.
Yuto Takanofc54dfb2021-08-07 17:18:28 +010040
Yuto Takano55c6c872021-08-09 15:35:19 +010041The script returns 0 on success, 1 on test failure, and 2 if there is a script
Yuto Takano8246eb82021-08-16 10:37:24 +010042error. It must be run from Mbed TLS root.
Darryl Greend5802922018-05-08 15:30:59 +010043"""
Yuto Takano39639672021-08-05 19:47:48 +010044
Yuto Takanofc1e9ff2021-08-23 13:54:56 +010045import abc
Yuto Takano39639672021-08-05 19:47:48 +010046import argparse
Gilles Peskine89458d12021-09-27 19:20:17 +020047import fnmatch
Yuto Takano977e07f2021-08-09 11:56:15 +010048import glob
Yuto Takano39639672021-08-05 19:47:48 +010049import textwrap
Darryl Greend5802922018-05-08 15:30:59 +010050import os
51import sys
52import traceback
53import re
Yuto Takanob1417b42021-08-17 10:30:20 +010054import enum
Darryl Greend5802922018-05-08 15:30:59 +010055import shutil
56import subprocess
57import logging
58
Yuto Takano81528c02021-08-06 16:22:06 +010059# Naming patterns to check against. These are defined outside the NameCheck
60# class for ease of modification.
Yuto Takanobb7dca42021-08-05 19:57:58 +010061MACRO_PATTERN = r"^(MBEDTLS|PSA)_[0-9A-Z_]*[0-9A-Z]$"
Yuto Takano81528c02021-08-06 16:22:06 +010062CONSTANTS_PATTERN = MACRO_PATTERN
Yuto Takanoc1838932021-08-05 19:52:09 +010063IDENTIFIER_PATTERN = r"^(mbedtls|psa)_[0-9a-z_]*[0-9a-z]$"
Yuto Takano39639672021-08-05 19:47:48 +010064
Yuto Takanod93fa372021-08-06 23:05:55 +010065class Match(): # pylint: disable=too-few-public-methods
Yuto Takano81528c02021-08-06 16:22:06 +010066 """
67 A class representing a match, together with its found position.
68
69 Fields:
70 * filename: the file that the match was in.
71 * line: the full line containing the match.
Yuto Takano704b0f72021-08-17 10:41:23 +010072 * line_no: the line number.
73 * pos: a tuple of (start, end) positions on the line where the match is.
Yuto Takano81528c02021-08-06 16:22:06 +010074 * name: the match itself.
75 """
Yuto Takano704b0f72021-08-17 10:41:23 +010076 def __init__(self, filename, line, line_no, pos, name):
77 # pylint: disable=too-many-arguments
Yuto Takano39639672021-08-05 19:47:48 +010078 self.filename = filename
79 self.line = line
Yuto Takano704b0f72021-08-17 10:41:23 +010080 self.line_no = line_no
Yuto Takano39639672021-08-05 19:47:48 +010081 self.pos = pos
82 self.name = name
Yuto Takano39639672021-08-05 19:47:48 +010083
Yuto Takanoa4e75122021-08-06 17:23:28 +010084 def __str__(self):
Yuto Takanofb86ac72021-08-16 10:32:40 +010085 """
86 Return a formatted code listing representation of the erroneous line.
87 """
Yuto Takano704b0f72021-08-17 10:41:23 +010088 gutter = format(self.line_no, "4d")
89 underline = self.pos[0] * " " + (self.pos[1] - self.pos[0]) * "^"
Yuto Takano381fda82021-08-06 23:37:20 +010090
Yuto Takanoa4e75122021-08-06 17:23:28 +010091 return (
Yuto Takanofb86ac72021-08-16 10:32:40 +010092 " {0} |\n".format(" " * len(gutter)) +
Yuto Takano381fda82021-08-06 23:37:20 +010093 " {0} | {1}".format(gutter, self.line) +
Yuto Takanofb86ac72021-08-16 10:32:40 +010094 " {0} | {1}\n".format(" " * len(gutter), underline)
Yuto Takanoa4e75122021-08-06 17:23:28 +010095 )
Yuto Takanod93fa372021-08-06 23:05:55 +010096
Yuto Takanofc1e9ff2021-08-23 13:54:56 +010097class Problem(abc.ABC): # pylint: disable=too-few-public-methods
Yuto Takano81528c02021-08-06 16:22:06 +010098 """
Yuto Takanofc1e9ff2021-08-23 13:54:56 +010099 An abstract parent class representing a form of static analysis error.
100 It extends an Abstract Base Class, which means it is not instantiable, and
101 it also mandates certain abstract methods to be implemented in subclasses.
Yuto Takano81528c02021-08-06 16:22:06 +0100102 """
Yuto Takano5473be22021-08-17 10:14:01 +0100103 # Class variable to control the quietness of all problems
104 quiet = False
Yuto Takano39639672021-08-05 19:47:48 +0100105 def __init__(self):
106 self.textwrapper = textwrap.TextWrapper()
Yuto Takano81528c02021-08-06 16:22:06 +0100107 self.textwrapper.width = 80
Yuto Takanoa4e75122021-08-06 17:23:28 +0100108 self.textwrapper.initial_indent = " > "
Yuto Takano81528c02021-08-06 16:22:06 +0100109 self.textwrapper.subsequent_indent = " "
Yuto Takano39639672021-08-05 19:47:48 +0100110
Yuto Takanofc1e9ff2021-08-23 13:54:56 +0100111 def __str__(self):
112 """
113 Unified string representation method for all Problems.
114 """
115 if self.__class__.quiet:
116 return self.quiet_output()
117 return self.verbose_output()
118
119 @abc.abstractmethod
120 def quiet_output(self):
121 """
122 The output when --quiet is enabled.
123 """
124 pass
125
126 @abc.abstractmethod
127 def verbose_output(self):
128 """
129 The default output with explanation and code snippet if appropriate.
130 """
131 pass
132
Yuto Takanod93fa372021-08-06 23:05:55 +0100133class SymbolNotInHeader(Problem): # pylint: disable=too-few-public-methods
Yuto Takano81528c02021-08-06 16:22:06 +0100134 """
135 A problem that occurs when an exported/available symbol in the object file
136 is not explicitly declared in header files. Created with
137 NameCheck.check_symbols_declared_in_header()
138
139 Fields:
140 * symbol_name: the name of the symbol.
141 """
Yuto Takanod70d4462021-08-09 12:45:51 +0100142 def __init__(self, symbol_name):
Yuto Takano39639672021-08-05 19:47:48 +0100143 self.symbol_name = symbol_name
144 Problem.__init__(self)
145
Yuto Takanofc1e9ff2021-08-23 13:54:56 +0100146 def quiet_output(self):
147 return "{0}".format(self.symbol_name)
Yuto Takano55614b52021-08-07 01:00:18 +0100148
Yuto Takanofc1e9ff2021-08-23 13:54:56 +0100149 def verbose_output(self):
Yuto Takano39639672021-08-05 19:47:48 +0100150 return self.textwrapper.fill(
151 "'{0}' was found as an available symbol in the output of nm, "
152 "however it was not declared in any header files."
153 .format(self.symbol_name))
154
Yuto Takanod93fa372021-08-06 23:05:55 +0100155class PatternMismatch(Problem): # pylint: disable=too-few-public-methods
Yuto Takano81528c02021-08-06 16:22:06 +0100156 """
157 A problem that occurs when something doesn't match the expected pattern.
158 Created with NameCheck.check_match_pattern()
159
160 Fields:
161 * pattern: the expected regex pattern
162 * match: the Match object in question
163 """
Yuto Takanod70d4462021-08-09 12:45:51 +0100164 def __init__(self, pattern, match):
Yuto Takano39639672021-08-05 19:47:48 +0100165 self.pattern = pattern
166 self.match = match
167 Problem.__init__(self)
Yuto Takano81528c02021-08-06 16:22:06 +0100168
Yuto Takano55614b52021-08-07 01:00:18 +0100169
Yuto Takanofc1e9ff2021-08-23 13:54:56 +0100170 def quiet_output(self):
171 return (
172 "{0}:{1}:{2}"
173 .format(self.match.filename, self.match.line_no, self.match.name)
174 )
175
176 def verbose_output(self):
Yuto Takano39639672021-08-05 19:47:48 +0100177 return self.textwrapper.fill(
Yuto Takanoa4e75122021-08-06 17:23:28 +0100178 "{0}:{1}: '{2}' does not match the required pattern '{3}'."
179 .format(
180 self.match.filename,
Yuto Takano5f831712021-08-18 18:03:24 +0100181 self.match.line_no,
Yuto Takanoa4e75122021-08-06 17:23:28 +0100182 self.match.name,
Yuto Takanod70d4462021-08-09 12:45:51 +0100183 self.pattern
184 )
185 ) + "\n" + str(self.match)
Yuto Takano39639672021-08-05 19:47:48 +0100186
Yuto Takanod93fa372021-08-06 23:05:55 +0100187class Typo(Problem): # pylint: disable=too-few-public-methods
Yuto Takano81528c02021-08-06 16:22:06 +0100188 """
189 A problem that occurs when a word using MBED doesn't appear to be defined as
190 constants nor enum values. Created with NameCheck.check_for_typos()
191
192 Fields:
193 * match: the Match object of the MBED name in question.
194 """
Yuto Takanod70d4462021-08-09 12:45:51 +0100195 def __init__(self, match):
Yuto Takano39639672021-08-05 19:47:48 +0100196 self.match = match
197 Problem.__init__(self)
Yuto Takano81528c02021-08-06 16:22:06 +0100198
Yuto Takanofc1e9ff2021-08-23 13:54:56 +0100199 def quiet_output(self):
200 return (
201 "{0}:{1}:{2}"
202 .format(self.match.filename, self.match.line_no, self.match.name)
203 )
Yuto Takano55614b52021-08-07 01:00:18 +0100204
Yuto Takanofc1e9ff2021-08-23 13:54:56 +0100205 def verbose_output(self):
Yuto Takano39639672021-08-05 19:47:48 +0100206 return self.textwrapper.fill(
Yuto Takanoa4e75122021-08-06 17:23:28 +0100207 "{0}:{1}: '{2}' looks like a typo. It was not found in any "
208 "macros or any enums. If this is not a typo, put "
209 "//no-check-names after it."
Yuto Takano5f831712021-08-18 18:03:24 +0100210 .format(self.match.filename, self.match.line_no, self.match.name)
Yuto Takanod70d4462021-08-09 12:45:51 +0100211 ) + "\n" + str(self.match)
Darryl Greend5802922018-05-08 15:30:59 +0100212
Yuto Takano55c6c872021-08-09 15:35:19 +0100213class CodeParser():
Yuto Takano81528c02021-08-06 16:22:06 +0100214 """
Yuto Takano55c6c872021-08-09 15:35:19 +0100215 Class for retrieving files and parsing the code. This can be used
216 independently of the checks that NameChecker performs, for example for
217 list_internal_identifiers.py.
Yuto Takano81528c02021-08-06 16:22:06 +0100218 """
Yuto Takano55c6c872021-08-09 15:35:19 +0100219 def __init__(self, log):
220 self.log = log
Yuto Takanofc54dfb2021-08-07 17:18:28 +0100221 self.check_repo_path()
Yuto Takano977e07f2021-08-09 11:56:15 +0100222
Yuto Takano8e9a2192021-08-09 14:48:53 +0100223 # Memo for storing "glob expression": set(filepaths)
224 self.files = {}
225
Gilles Peskine89458d12021-09-27 19:20:17 +0200226 # Globally excluded filenames.
227 # Note that "*" can match directory separators in exclude lists.
228 self.excluded_files = ["*/bn_mul", "*/compat-2.x.h"]
Yuto Takano977e07f2021-08-09 11:56:15 +0100229
Yuto Takanofc54dfb2021-08-07 17:18:28 +0100230 @staticmethod
231 def check_repo_path():
232 """
233 Check that the current working directory is the project root, and throw
234 an exception if not.
235 """
236 if not all(os.path.isdir(d) for d in ["include", "library", "tests"]):
237 raise Exception("This script must be run from Mbed TLS root")
238
Yuto Takano55c6c872021-08-09 15:35:19 +0100239 def comprehensive_parse(self):
Yuto Takano39639672021-08-05 19:47:48 +0100240 """
Yuto Takano55c6c872021-08-09 15:35:19 +0100241 Comprehensive ("default") function to call each parsing function and
242 retrieve various elements of the code, together with the source location.
Darryl Greend5802922018-05-08 15:30:59 +0100243
Yuto Takano55c6c872021-08-09 15:35:19 +0100244 Returns a dict of parsed item key to the corresponding List of Matches.
Yuto Takano81528c02021-08-06 16:22:06 +0100245 """
246 self.log.info("Parsing source code...")
Yuto Takanod24e0372021-08-06 16:42:33 +0100247 self.log.debug(
Yuto Takano50953432021-08-09 14:54:36 +0100248 "The following files are excluded from the search: {}"
Yuto Takanod24e0372021-08-06 16:42:33 +0100249 .format(str(self.excluded_files))
250 )
Yuto Takano81528c02021-08-06 16:22:06 +0100251
Yuto Takano8e9a2192021-08-09 14:48:53 +0100252 all_macros = self.parse_macros([
253 "include/mbedtls/*.h",
254 "include/psa/*.h",
255 "library/*.h",
256 "tests/include/test/drivers/*.h",
Yuto Takanod70d4462021-08-09 12:45:51 +0100257 "3rdparty/everest/include/everest/everest.h",
258 "3rdparty/everest/include/everest/x25519.h"
Yuto Takano8e9a2192021-08-09 14:48:53 +0100259 ])
260 enum_consts = self.parse_enum_consts([
261 "include/mbedtls/*.h",
262 "library/*.h",
263 "3rdparty/everest/include/everest/everest.h",
264 "3rdparty/everest/include/everest/x25519.h"
265 ])
266 identifiers = self.parse_identifiers([
267 "include/mbedtls/*.h",
268 "include/psa/*.h",
269 "library/*.h",
270 "3rdparty/everest/include/everest/everest.h",
271 "3rdparty/everest/include/everest/x25519.h"
272 ])
273 mbed_words = self.parse_mbed_words([
274 "include/mbedtls/*.h",
275 "include/psa/*.h",
276 "library/*.h",
277 "3rdparty/everest/include/everest/everest.h",
278 "3rdparty/everest/include/everest/x25519.h",
279 "library/*.c",
Yuto Takano81528c02021-08-06 16:22:06 +0100280 "3rdparty/everest/library/everest.c",
Yuto Takanod70d4462021-08-09 12:45:51 +0100281 "3rdparty/everest/library/x25519.c"
Yuto Takano8e9a2192021-08-09 14:48:53 +0100282 ])
Yuto Takano81528c02021-08-06 16:22:06 +0100283 symbols = self.parse_symbols()
284
285 # Remove identifier macros like mbedtls_printf or mbedtls_calloc
286 identifiers_justname = [x.name for x in identifiers]
287 actual_macros = []
288 for macro in all_macros:
289 if macro.name not in identifiers_justname:
290 actual_macros.append(macro)
291
292 self.log.debug("Found:")
Yuto Takano9d9c6dc2021-08-16 10:43:45 +0100293 # Aligns the counts on the assumption that none exceeds 4 digits
294 self.log.debug(" {:4} Total Macros".format(len(all_macros)))
295 self.log.debug(" {:4} Non-identifier Macros".format(len(actual_macros)))
296 self.log.debug(" {:4} Enum Constants".format(len(enum_consts)))
297 self.log.debug(" {:4} Identifiers".format(len(identifiers)))
298 self.log.debug(" {:4} Exported Symbols".format(len(symbols)))
Yuto Takano55c6c872021-08-09 15:35:19 +0100299 return {
Yuto Takano81528c02021-08-06 16:22:06 +0100300 "macros": actual_macros,
301 "enum_consts": enum_consts,
302 "identifiers": identifiers,
303 "symbols": symbols,
Yuto Takanod93fa372021-08-06 23:05:55 +0100304 "mbed_words": mbed_words
Yuto Takano81528c02021-08-06 16:22:06 +0100305 }
306
Gilles Peskine89458d12021-09-27 19:20:17 +0200307 def is_file_excluded(self, path, exclude_wildcards):
Gilles Peskine8a832242021-09-28 10:12:49 +0200308 """Whether the given file path is excluded."""
Gilles Peskine89458d12021-09-27 19:20:17 +0200309 # exclude_wildcards may be None. Also, consider the global exclusions.
310 exclude_wildcards = (exclude_wildcards or []) + self.excluded_files
311 for pattern in exclude_wildcards:
312 if fnmatch.fnmatch(path, pattern):
313 return True
314 return False
315
Yuto Takano55c6c872021-08-09 15:35:19 +0100316 def get_files(self, include_wildcards, exclude_wildcards):
317 """
318 Get all files that match any of the UNIX-style wildcards. While the
319 check_names script is designed only for use on UNIX/macOS (due to nm),
320 this function alone would work fine on Windows even with forward slashes
321 in the wildcard.
322
323 Args:
324 * include_wildcards: a List of shell-style wildcards to match filepaths.
325 * exclude_wildcards: a List of shell-style wildcards to exclude.
326
327 Returns a List of relative filepaths.
328 """
329 accumulator = set()
330
Yuto Takano55c6c872021-08-09 15:35:19 +0100331 for include_wildcard in include_wildcards:
Gilles Peskine89458d12021-09-27 19:20:17 +0200332 accumulator = accumulator.union(glob.iglob(include_wildcard))
Yuto Takano55c6c872021-08-09 15:35:19 +0100333
Gilles Peskine89458d12021-09-27 19:20:17 +0200334 return list(path for path in accumulator
335 if not self.is_file_excluded(path, exclude_wildcards))
Yuto Takano55c6c872021-08-09 15:35:19 +0100336
Yuto Takano8e9a2192021-08-09 14:48:53 +0100337 def parse_macros(self, include, exclude=None):
Yuto Takano39639672021-08-05 19:47:48 +0100338 """
339 Parse all macros defined by #define preprocessor directives.
340
341 Args:
Yuto Takano8e9a2192021-08-09 14:48:53 +0100342 * include: A List of glob expressions to look for files through.
343 * exclude: A List of glob expressions for excluding files.
Yuto Takano81528c02021-08-06 16:22:06 +0100344
345 Returns a List of Match objects for the found macros.
Yuto Takano39639672021-08-05 19:47:48 +0100346 """
Yuto Takanod93fa372021-08-06 23:05:55 +0100347 macro_regex = re.compile(r"# *define +(?P<macro>\w+)")
348 exclusions = (
Yuto Takano39639672021-08-05 19:47:48 +0100349 "asm", "inline", "EMIT", "_CRT_SECURE_NO_DEPRECATE", "MULADDC_"
350 )
351
Yuto Takano50953432021-08-09 14:54:36 +0100352 files = self.get_files(include, exclude)
353 self.log.debug("Looking for macros in {} files".format(len(files)))
Yuto Takanod93fa372021-08-06 23:05:55 +0100354
Yuto Takano50953432021-08-09 14:54:36 +0100355 macros = []
356 for header_file in files:
Yuto Takanoa083d152021-08-07 00:25:59 +0100357 with open(header_file, "r", encoding="utf-8") as header:
Yuto Takano8f457cf2021-08-06 17:54:58 +0100358 for line_no, line in enumerate(header):
Yuto Takanod93fa372021-08-06 23:05:55 +0100359 for macro in macro_regex.finditer(line):
Yuto Takanod70d4462021-08-09 12:45:51 +0100360 if macro.group("macro").startswith(exclusions):
361 continue
362
363 macros.append(Match(
364 header_file,
365 line,
Yuto Takano704b0f72021-08-17 10:41:23 +0100366 line_no,
367 macro.span("macro"),
Yuto Takanod70d4462021-08-09 12:45:51 +0100368 macro.group("macro")))
Darryl Greend5802922018-05-08 15:30:59 +0100369
Yuto Takano39639672021-08-05 19:47:48 +0100370 return macros
Darryl Greend5802922018-05-08 15:30:59 +0100371
Yuto Takano8e9a2192021-08-09 14:48:53 +0100372 def parse_mbed_words(self, include, exclude=None):
Yuto Takano39639672021-08-05 19:47:48 +0100373 """
Yuto Takanob47b5042021-08-07 00:42:54 +0100374 Parse all words in the file that begin with MBED, in and out of macros,
375 comments, anything.
Yuto Takano39639672021-08-05 19:47:48 +0100376
377 Args:
Yuto Takano8e9a2192021-08-09 14:48:53 +0100378 * include: A List of glob expressions to look for files through.
379 * exclude: A List of glob expressions for excluding files.
Yuto Takano81528c02021-08-06 16:22:06 +0100380
381 Returns a List of Match objects for words beginning with MBED.
Yuto Takano39639672021-08-05 19:47:48 +0100382 """
Yuto Takanob47b5042021-08-07 00:42:54 +0100383 # Typos of TLS are common, hence the broader check below than MBEDTLS.
Yuto Takanod93fa372021-08-06 23:05:55 +0100384 mbed_regex = re.compile(r"\bMBED.+?_[A-Z0-9_]*")
385 exclusions = re.compile(r"// *no-check-names|#error")
386
Yuto Takano50953432021-08-09 14:54:36 +0100387 files = self.get_files(include, exclude)
388 self.log.debug("Looking for MBED words in {} files".format(len(files)))
Yuto Takanod93fa372021-08-06 23:05:55 +0100389
Yuto Takano50953432021-08-09 14:54:36 +0100390 mbed_words = []
391 for filename in files:
Yuto Takanoa083d152021-08-07 00:25:59 +0100392 with open(filename, "r", encoding="utf-8") as fp:
Yuto Takano8f457cf2021-08-06 17:54:58 +0100393 for line_no, line in enumerate(fp):
Yuto Takanod93fa372021-08-06 23:05:55 +0100394 if exclusions.search(line):
Yuto Takanoc62b4082021-08-05 20:17:07 +0100395 continue
Yuto Takano81528c02021-08-06 16:22:06 +0100396
Yuto Takanod93fa372021-08-06 23:05:55 +0100397 for name in mbed_regex.finditer(line):
398 mbed_words.append(Match(
Yuto Takano39639672021-08-05 19:47:48 +0100399 filename,
400 line,
Yuto Takano704b0f72021-08-17 10:41:23 +0100401 line_no,
402 name.span(0),
403 name.group(0)))
Yuto Takano39639672021-08-05 19:47:48 +0100404
Yuto Takanod93fa372021-08-06 23:05:55 +0100405 return mbed_words
Yuto Takano39639672021-08-05 19:47:48 +0100406
Yuto Takano8e9a2192021-08-09 14:48:53 +0100407 def parse_enum_consts(self, include, exclude=None):
Yuto Takano39639672021-08-05 19:47:48 +0100408 """
409 Parse all enum value constants that are declared.
410
411 Args:
Yuto Takano8e9a2192021-08-09 14:48:53 +0100412 * include: A List of glob expressions to look for files through.
413 * exclude: A List of glob expressions for excluding files.
Yuto Takano39639672021-08-05 19:47:48 +0100414
Yuto Takano81528c02021-08-06 16:22:06 +0100415 Returns a List of Match objects for the findings.
Yuto Takano39639672021-08-05 19:47:48 +0100416 """
Yuto Takano50953432021-08-09 14:54:36 +0100417 files = self.get_files(include, exclude)
418 self.log.debug("Looking for enum consts in {} files".format(len(files)))
Yuto Takanod93fa372021-08-06 23:05:55 +0100419
Yuto Takanob1417b42021-08-17 10:30:20 +0100420 # Emulate a finite state machine to parse enum declarations.
421 # OUTSIDE_KEYWORD = outside the enum keyword
422 # IN_BRACES = inside enum opening braces
423 # IN_BETWEEN = between enum keyword and opening braces
424 states = enum.Enum("FSM", ["OUTSIDE_KEYWORD", "IN_BRACES", "IN_BETWEEN"])
Yuto Takano50953432021-08-09 14:54:36 +0100425 enum_consts = []
426 for header_file in files:
Yuto Takanob1417b42021-08-17 10:30:20 +0100427 state = states.OUTSIDE_KEYWORD
Yuto Takanoa083d152021-08-07 00:25:59 +0100428 with open(header_file, "r", encoding="utf-8") as header:
Yuto Takano8f457cf2021-08-06 17:54:58 +0100429 for line_no, line in enumerate(header):
Yuto Takano13ecd992021-08-06 16:56:52 +0100430 # Match typedefs and brackets only when they are at the
431 # beginning of the line -- if they are indented, they might
432 # be sub-structures within structs, etc.
Yuto Takanob1417b42021-08-17 10:30:20 +0100433 if (state == states.OUTSIDE_KEYWORD and
Yuto Takano35906912021-08-17 11:05:43 +0100434 re.search(r"^(typedef +)?enum +{", line)):
Yuto Takanob1417b42021-08-17 10:30:20 +0100435 state = states.IN_BRACES
436 elif (state == states.OUTSIDE_KEYWORD and
437 re.search(r"^(typedef +)?enum", line)):
438 state = states.IN_BETWEEN
439 elif (state == states.IN_BETWEEN and
440 re.search(r"^{", line)):
441 state = states.IN_BRACES
442 elif (state == states.IN_BRACES and
443 re.search(r"^}", line)):
444 state = states.OUTSIDE_KEYWORD
445 elif (state == states.IN_BRACES and
446 not re.search(r"^ *#", line)):
Yuto Takano90bc0262021-08-16 11:34:10 +0100447 enum_const = re.search(r"^ *(?P<enum_const>\w+)", line)
Yuto Takanod70d4462021-08-09 12:45:51 +0100448 if not enum_const:
449 continue
450
451 enum_consts.append(Match(
452 header_file,
453 line,
Yuto Takano704b0f72021-08-17 10:41:23 +0100454 line_no,
455 enum_const.span("enum_const"),
Yuto Takanod70d4462021-08-09 12:45:51 +0100456 enum_const.group("enum_const")))
Yuto Takano81528c02021-08-06 16:22:06 +0100457
Yuto Takano39639672021-08-05 19:47:48 +0100458 return enum_consts
Darryl Greend5802922018-05-08 15:30:59 +0100459
Yuto Takano8e9a2192021-08-09 14:48:53 +0100460 def parse_identifiers(self, include, exclude=None):
Yuto Takano39639672021-08-05 19:47:48 +0100461 """
Yuto Takano8246eb82021-08-16 10:37:24 +0100462 Parse all lines of a header where a function/enum/struct/union/typedef
Yuto Takanob1417b42021-08-17 10:30:20 +0100463 identifier is declared, based on some regex and heuristics. Highly
464 dependent on formatting style.
Darryl Greend5802922018-05-08 15:30:59 +0100465
Yuto Takano39639672021-08-05 19:47:48 +0100466 Args:
Yuto Takano8e9a2192021-08-09 14:48:53 +0100467 * include: A List of glob expressions to look for files through.
468 * exclude: A List of glob expressions for excluding files.
Yuto Takano81528c02021-08-06 16:22:06 +0100469
470 Returns a List of Match objects with identifiers.
Yuto Takano39639672021-08-05 19:47:48 +0100471 """
Yuto Takanod93fa372021-08-06 23:05:55 +0100472 identifier_regex = re.compile(
473 # Match " something(a" or " *something(a". Functions.
474 # Assumptions:
475 # - function definition from return type to one of its arguments is
Yuto Takano55c6c872021-08-09 15:35:19 +0100476 # all on one line
Yuto Takanod93fa372021-08-06 23:05:55 +0100477 # - function definition line only contains alphanumeric, asterisk,
478 # underscore, and open bracket
479 r".* \**(\w+) *\( *\w|"
Yuto Takano55c6c872021-08-09 15:35:19 +0100480 # Match "(*something)(".
Yuto Takanod93fa372021-08-06 23:05:55 +0100481 r".*\( *\* *(\w+) *\) *\(|"
482 # Match names of named data structures.
483 r"(?:typedef +)?(?:struct|union|enum) +(\w+)(?: *{)?$|"
484 # Match names of typedef instances, after closing bracket.
Yuto Takanod70d4462021-08-09 12:45:51 +0100485 r"}? *(\w+)[;[].*"
486 )
Yuto Takano35906912021-08-17 11:05:43 +0100487 # The regex below is indented for clarity.
Yuto Takanod70d4462021-08-09 12:45:51 +0100488 exclusion_lines = re.compile(
489 r"^("
Yuto Takano35906912021-08-17 11:05:43 +0100490 r"extern +\"C\"|" # pylint: disable=bad-continuation
Yuto Takano90bc0262021-08-16 11:34:10 +0100491 r"(typedef +)?(struct|union|enum)( *{)?$|"
492 r"} *;?$|"
493 r"$|"
494 r"//|"
495 r"#"
Yuto Takanod70d4462021-08-09 12:45:51 +0100496 r")"
497 )
Yuto Takanod93fa372021-08-06 23:05:55 +0100498
Yuto Takano50953432021-08-09 14:54:36 +0100499 files = self.get_files(include, exclude)
500 self.log.debug("Looking for identifiers in {} files".format(len(files)))
501
502 identifiers = []
503 for header_file in files:
Yuto Takanoa083d152021-08-07 00:25:59 +0100504 with open(header_file, "r", encoding="utf-8") as header:
Yuto Takano39639672021-08-05 19:47:48 +0100505 in_block_comment = False
Yuto Takano55c6c872021-08-09 15:35:19 +0100506 # The previous line variable is used for concatenating lines
Yuto Takanob1417b42021-08-17 10:30:20 +0100507 # when identifiers are formatted and spread across multiple
508 # lines.
Yuto Takanod93fa372021-08-06 23:05:55 +0100509 previous_line = ""
Darryl Greend5802922018-05-08 15:30:59 +0100510
Yuto Takano8f457cf2021-08-06 17:54:58 +0100511 for line_no, line in enumerate(header):
Yuto Takano81528c02021-08-06 16:22:06 +0100512 # Skip parsing this line if a block comment ends on it,
513 # but don't skip if it has just started -- there is a chance
514 # it ends on the same line.
Yuto Takano39639672021-08-05 19:47:48 +0100515 if re.search(r"/\*", line):
Yuto Takano81528c02021-08-06 16:22:06 +0100516 in_block_comment = not in_block_comment
517 if re.search(r"\*/", line):
518 in_block_comment = not in_block_comment
Yuto Takano39639672021-08-05 19:47:48 +0100519 continue
520
Yuto Takano81528c02021-08-06 16:22:06 +0100521 if in_block_comment:
Yuto Takanod93fa372021-08-06 23:05:55 +0100522 previous_line = ""
Yuto Takano81528c02021-08-06 16:22:06 +0100523 continue
524
Yuto Takano90bc0262021-08-16 11:34:10 +0100525 if exclusion_lines.search(line):
Yuto Takanod93fa372021-08-06 23:05:55 +0100526 previous_line = ""
Yuto Takano81528c02021-08-06 16:22:06 +0100527 continue
528
Yuto Takanocfc9e4a2021-08-06 20:02:32 +0100529 # If the line contains only space-separated alphanumeric
530 # characters (or underscore, asterisk, or, open bracket),
531 # and nothing else, high chance it's a declaration that
532 # continues on the next line
Yuto Takano90bc0262021-08-16 11:34:10 +0100533 if re.search(r"^([\w\*\(]+\s+)+$", line):
Yuto Takanod93fa372021-08-06 23:05:55 +0100534 previous_line += line
Yuto Takano81528c02021-08-06 16:22:06 +0100535 continue
536
537 # If previous line seemed to start an unfinished declaration
Yuto Takanocfc9e4a2021-08-06 20:02:32 +0100538 # (as above), concat and treat them as one.
539 if previous_line:
Yuto Takano90bc0262021-08-16 11:34:10 +0100540 line = previous_line.strip() + " " + line.strip() + "\n"
Yuto Takanod93fa372021-08-06 23:05:55 +0100541 previous_line = ""
Yuto Takano81528c02021-08-06 16:22:06 +0100542
Yuto Takano8246eb82021-08-16 10:37:24 +0100543 # Skip parsing if line has a space in front = heuristic to
Yuto Takano81528c02021-08-06 16:22:06 +0100544 # skip function argument lines (highly subject to formatting
545 # changes)
546 if line[0] == " ":
Yuto Takano39639672021-08-05 19:47:48 +0100547 continue
Yuto Takano6f38ab32021-08-05 21:07:14 +0100548
Yuto Takanod93fa372021-08-06 23:05:55 +0100549 identifier = identifier_regex.search(line)
Yuto Takano39639672021-08-05 19:47:48 +0100550
Yuto Takanod70d4462021-08-09 12:45:51 +0100551 if not identifier:
552 continue
553
554 # Find the group that matched, and append it
555 for group in identifier.groups():
556 if not group:
557 continue
558
559 identifiers.append(Match(
560 header_file,
561 line,
Yuto Takano704b0f72021-08-17 10:41:23 +0100562 line_no,
563 identifier.span(),
Yuto Takanod70d4462021-08-09 12:45:51 +0100564 group))
Yuto Takano39639672021-08-05 19:47:48 +0100565
566 return identifiers
567
568 def parse_symbols(self):
569 """
570 Compile the Mbed TLS libraries, and parse the TLS, Crypto, and x509
571 object files using nm to retrieve the list of referenced symbols.
Yuto Takano81528c02021-08-06 16:22:06 +0100572 Exceptions thrown here are rethrown because they would be critical
573 errors that void several tests, and thus needs to halt the program. This
574 is explicitly done for clarity.
Yuto Takano39639672021-08-05 19:47:48 +0100575
Yuto Takano81528c02021-08-06 16:22:06 +0100576 Returns a List of unique symbols defined and used in the libraries.
577 """
578 self.log.info("Compiling...")
Yuto Takano39639672021-08-05 19:47:48 +0100579 symbols = []
580
581 # Back up the config and atomically compile with the full configratuion.
Yuto Takanod70d4462021-08-09 12:45:51 +0100582 shutil.copy(
583 "include/mbedtls/mbedtls_config.h",
584 "include/mbedtls/mbedtls_config.h.bak"
585 )
Darryl Greend5802922018-05-08 15:30:59 +0100586 try:
Yuto Takano81528c02021-08-06 16:22:06 +0100587 # Use check=True in all subprocess calls so that failures are raised
588 # as exceptions and logged.
Yuto Takano39639672021-08-05 19:47:48 +0100589 subprocess.run(
Yuto Takano81528c02021-08-06 16:22:06 +0100590 ["python3", "scripts/config.py", "full"],
Yuto Takanobcc3d992021-08-06 23:14:58 +0100591 universal_newlines=True,
Yuto Takano39639672021-08-05 19:47:48 +0100592 check=True
Darryl Greend5802922018-05-08 15:30:59 +0100593 )
594 my_environment = os.environ.copy()
595 my_environment["CFLAGS"] = "-fno-asynchronous-unwind-tables"
Yuto Takano4b7d23d2021-08-17 10:48:22 +0100596 # Run make clean separately to lib to prevent unwanted behavior when
597 # make is invoked with parallelism.
Yuto Takano39639672021-08-05 19:47:48 +0100598 subprocess.run(
Yuto Takano4b7d23d2021-08-17 10:48:22 +0100599 ["make", "clean"],
600 universal_newlines=True,
601 check=True
602 )
603 subprocess.run(
604 ["make", "lib"],
Darryl Greend5802922018-05-08 15:30:59 +0100605 env=my_environment,
Yuto Takanobcc3d992021-08-06 23:14:58 +0100606 universal_newlines=True,
Yuto Takano39639672021-08-05 19:47:48 +0100607 stdout=subprocess.PIPE,
Darryl Greend5802922018-05-08 15:30:59 +0100608 stderr=subprocess.STDOUT,
Yuto Takano39639672021-08-05 19:47:48 +0100609 check=True
Darryl Greend5802922018-05-08 15:30:59 +0100610 )
Yuto Takano39639672021-08-05 19:47:48 +0100611
612 # Perform object file analysis using nm
Yuto Takanod70d4462021-08-09 12:45:51 +0100613 symbols = self.parse_symbols_from_nm([
614 "library/libmbedcrypto.a",
615 "library/libmbedtls.a",
616 "library/libmbedx509.a"
617 ])
Yuto Takano39639672021-08-05 19:47:48 +0100618
619 subprocess.run(
Darryl Greend5802922018-05-08 15:30:59 +0100620 ["make", "clean"],
Yuto Takanobcc3d992021-08-06 23:14:58 +0100621 universal_newlines=True,
Yuto Takano39639672021-08-05 19:47:48 +0100622 check=True
Darryl Greend5802922018-05-08 15:30:59 +0100623 )
624 except subprocess.CalledProcessError as error:
Yuto Takano25eeb7b2021-08-06 21:27:59 +0100625 self.log.debug(error.output)
Yuto Takano81528c02021-08-06 16:22:06 +0100626 raise error
Yuto Takano39639672021-08-05 19:47:48 +0100627 finally:
Yuto Takano6fececf2021-08-07 17:28:23 +0100628 # Put back the original config regardless of there being errors.
629 # Works also for keyboard interrupts.
Yuto Takanod70d4462021-08-09 12:45:51 +0100630 shutil.move(
631 "include/mbedtls/mbedtls_config.h.bak",
632 "include/mbedtls/mbedtls_config.h"
633 )
Yuto Takano39639672021-08-05 19:47:48 +0100634
635 return symbols
636
637 def parse_symbols_from_nm(self, object_files):
638 """
639 Run nm to retrieve the list of referenced symbols in each object file.
640 Does not return the position data since it is of no use.
641
Yuto Takano81528c02021-08-06 16:22:06 +0100642 Args:
Yuto Takano55c6c872021-08-09 15:35:19 +0100643 * object_files: a List of compiled object filepaths to search through.
Yuto Takano81528c02021-08-06 16:22:06 +0100644
645 Returns a List of unique symbols defined and used in any of the object
646 files.
Yuto Takano39639672021-08-05 19:47:48 +0100647 """
Yuto Takanod93fa372021-08-06 23:05:55 +0100648 nm_undefined_regex = re.compile(r"^\S+: +U |^$|^\S+:$")
649 nm_valid_regex = re.compile(r"^\S+( [0-9A-Fa-f]+)* . _*(?P<symbol>\w+)")
Yuto Takano12a7ecd2021-08-07 00:40:29 +0100650 exclusions = ("FStar", "Hacl")
Yuto Takano39639672021-08-05 19:47:48 +0100651
652 symbols = []
653
Yuto Takano81528c02021-08-06 16:22:06 +0100654 # Gather all outputs of nm
Yuto Takano39639672021-08-05 19:47:48 +0100655 nm_output = ""
656 for lib in object_files:
657 nm_output += subprocess.run(
658 ["nm", "-og", lib],
Yuto Takanobcc3d992021-08-06 23:14:58 +0100659 universal_newlines=True,
Yuto Takano39639672021-08-05 19:47:48 +0100660 stdout=subprocess.PIPE,
661 stderr=subprocess.STDOUT,
662 check=True
663 ).stdout
Yuto Takano81528c02021-08-06 16:22:06 +0100664
Yuto Takano39639672021-08-05 19:47:48 +0100665 for line in nm_output.splitlines():
Yuto Takano90bc0262021-08-16 11:34:10 +0100666 if not nm_undefined_regex.search(line):
667 symbol = nm_valid_regex.search(line)
Yuto Takano12a7ecd2021-08-07 00:40:29 +0100668 if (symbol and not symbol.group("symbol").startswith(exclusions)):
Yuto Takanoe77f6992021-08-05 20:22:59 +0100669 symbols.append(symbol.group("symbol"))
Yuto Takano39639672021-08-05 19:47:48 +0100670 else:
671 self.log.error(line)
Yuto Takano81528c02021-08-06 16:22:06 +0100672
Yuto Takano39639672021-08-05 19:47:48 +0100673 return symbols
674
Yuto Takano55c6c872021-08-09 15:35:19 +0100675class NameChecker():
676 """
677 Representation of the core name checking operation performed by this script.
678 """
679 def __init__(self, parse_result, log):
680 self.parse_result = parse_result
681 self.log = log
682
Yuto Takano55614b52021-08-07 01:00:18 +0100683 def perform_checks(self, quiet=False):
Yuto Takano39639672021-08-05 19:47:48 +0100684 """
Yuto Takano55c6c872021-08-09 15:35:19 +0100685 A comprehensive checker that performs each check in order, and outputs
686 a final verdict.
Yuto Takano81528c02021-08-06 16:22:06 +0100687
688 Args:
Yuto Takano55614b52021-08-07 01:00:18 +0100689 * quiet: whether to hide detailed problem explanation.
Yuto Takano39639672021-08-05 19:47:48 +0100690 """
Yuto Takano81528c02021-08-06 16:22:06 +0100691 self.log.info("=============")
Yuto Takano5473be22021-08-17 10:14:01 +0100692 Problem.quiet = quiet
Yuto Takano39639672021-08-05 19:47:48 +0100693 problems = 0
Yuto Takano5473be22021-08-17 10:14:01 +0100694 problems += self.check_symbols_declared_in_header()
Yuto Takano39639672021-08-05 19:47:48 +0100695
Yuto Takanod70d4462021-08-09 12:45:51 +0100696 pattern_checks = [
697 ("macros", MACRO_PATTERN),
698 ("enum_consts", CONSTANTS_PATTERN),
699 ("identifiers", IDENTIFIER_PATTERN)
700 ]
Yuto Takano39639672021-08-05 19:47:48 +0100701 for group, check_pattern in pattern_checks:
Yuto Takano5473be22021-08-17 10:14:01 +0100702 problems += self.check_match_pattern(group, check_pattern)
Yuto Takano39639672021-08-05 19:47:48 +0100703
Yuto Takano5473be22021-08-17 10:14:01 +0100704 problems += self.check_for_typos()
Yuto Takano39639672021-08-05 19:47:48 +0100705
706 self.log.info("=============")
707 if problems > 0:
708 self.log.info("FAIL: {0} problem(s) to fix".format(str(problems)))
Yuto Takano55614b52021-08-07 01:00:18 +0100709 if quiet:
710 self.log.info("Remove --quiet to see explanations.")
Yuto Takanofc54dfb2021-08-07 17:18:28 +0100711 else:
712 self.log.info("Use --quiet for minimal output.")
Yuto Takano55c6c872021-08-09 15:35:19 +0100713 return 1
Yuto Takano39639672021-08-05 19:47:48 +0100714 else:
715 self.log.info("PASS")
Yuto Takano55c6c872021-08-09 15:35:19 +0100716 return 0
Darryl Greend5802922018-05-08 15:30:59 +0100717
Yuto Takano5473be22021-08-17 10:14:01 +0100718 def check_symbols_declared_in_header(self):
Yuto Takano39639672021-08-05 19:47:48 +0100719 """
720 Perform a check that all detected symbols in the library object files
721 are properly declared in headers.
Yuto Takano977e07f2021-08-09 11:56:15 +0100722 Assumes parse_names_in_source() was called before this.
Darryl Greend5802922018-05-08 15:30:59 +0100723
Yuto Takano81528c02021-08-06 16:22:06 +0100724 Returns the number of problems that need fixing.
Yuto Takano39639672021-08-05 19:47:48 +0100725 """
726 problems = []
Yuto Takanod93fa372021-08-06 23:05:55 +0100727
Yuto Takano39639672021-08-05 19:47:48 +0100728 for symbol in self.parse_result["symbols"]:
729 found_symbol_declared = False
730 for identifier_match in self.parse_result["identifiers"]:
731 if symbol == identifier_match.name:
732 found_symbol_declared = True
733 break
Yuto Takano81528c02021-08-06 16:22:06 +0100734
Yuto Takano39639672021-08-05 19:47:48 +0100735 if not found_symbol_declared:
Yuto Takanod70d4462021-08-09 12:45:51 +0100736 problems.append(SymbolNotInHeader(symbol))
Yuto Takano39639672021-08-05 19:47:48 +0100737
Yuto Takano5473be22021-08-17 10:14:01 +0100738 self.output_check_result("All symbols in header", problems)
Yuto Takano39639672021-08-05 19:47:48 +0100739 return len(problems)
740
Yuto Takano5473be22021-08-17 10:14:01 +0100741 def check_match_pattern(self, group_to_check, check_pattern):
Yuto Takano81528c02021-08-06 16:22:06 +0100742 """
743 Perform a check that all items of a group conform to a regex pattern.
Yuto Takano977e07f2021-08-09 11:56:15 +0100744 Assumes parse_names_in_source() was called before this.
Yuto Takano81528c02021-08-06 16:22:06 +0100745
746 Args:
Yuto Takano81528c02021-08-06 16:22:06 +0100747 * group_to_check: string key to index into self.parse_result.
748 * check_pattern: the regex to check against.
749
750 Returns the number of problems that need fixing.
751 """
Yuto Takano39639672021-08-05 19:47:48 +0100752 problems = []
Yuto Takanod93fa372021-08-06 23:05:55 +0100753
Yuto Takano39639672021-08-05 19:47:48 +0100754 for item_match in self.parse_result[group_to_check]:
Yuto Takano90bc0262021-08-16 11:34:10 +0100755 if not re.search(check_pattern, item_match.name):
Yuto Takano39639672021-08-05 19:47:48 +0100756 problems.append(PatternMismatch(check_pattern, item_match))
Yuto Takano90bc0262021-08-16 11:34:10 +0100757 # Double underscore should not be used for names
758 if re.search(r".*__.*", item_match.name):
Yuto Takano704b0f72021-08-17 10:41:23 +0100759 problems.append(
760 PatternMismatch("no double underscore allowed", item_match))
Yuto Takano81528c02021-08-06 16:22:06 +0100761
762 self.output_check_result(
763 "Naming patterns of {}".format(group_to_check),
Yuto Takano55614b52021-08-07 01:00:18 +0100764 problems)
Yuto Takano39639672021-08-05 19:47:48 +0100765 return len(problems)
Darryl Greend5802922018-05-08 15:30:59 +0100766
Yuto Takano5473be22021-08-17 10:14:01 +0100767 def check_for_typos(self):
Yuto Takano81528c02021-08-06 16:22:06 +0100768 """
769 Perform a check that all words in the soure code beginning with MBED are
770 either defined as macros, or as enum constants.
Yuto Takano977e07f2021-08-09 11:56:15 +0100771 Assumes parse_names_in_source() was called before this.
Yuto Takano81528c02021-08-06 16:22:06 +0100772
Yuto Takano81528c02021-08-06 16:22:06 +0100773 Returns the number of problems that need fixing.
774 """
Yuto Takano39639672021-08-05 19:47:48 +0100775 problems = []
Yuto Takano39639672021-08-05 19:47:48 +0100776
Yuto Takanod70d4462021-08-09 12:45:51 +0100777 # Set comprehension, equivalent to a list comprehension wrapped by set()
Yuto Takanod93fa372021-08-06 23:05:55 +0100778 all_caps_names = {
779 match.name
780 for match
781 in self.parse_result["macros"] + self.parse_result["enum_consts"]}
Ronald Cron7975fae2021-09-13 14:50:42 +0200782 typo_exclusion = re.compile(r"XXX|__|_$|^MBEDTLS_.*CONFIG_FILE$|"
783 r"MBEDTLS_TEST_LIBTESTDRIVER*")
Yuto Takano39639672021-08-05 19:47:48 +0100784
Yuto Takanod93fa372021-08-06 23:05:55 +0100785 for name_match in self.parse_result["mbed_words"]:
Yuto Takano81528c02021-08-06 16:22:06 +0100786 found = name_match.name in all_caps_names
787
788 # Since MBEDTLS_PSA_ACCEL_XXX defines are defined by the
789 # PSA driver, they will not exist as macros. However, they
790 # should still be checked for typos using the equivalent
791 # BUILTINs that exist.
792 if "MBEDTLS_PSA_ACCEL_" in name_match.name:
793 found = name_match.name.replace(
794 "MBEDTLS_PSA_ACCEL_",
795 "MBEDTLS_PSA_BUILTIN_") in all_caps_names
796
Yuto Takanod93fa372021-08-06 23:05:55 +0100797 if not found and not typo_exclusion.search(name_match.name):
Yuto Takanod70d4462021-08-09 12:45:51 +0100798 problems.append(Typo(name_match))
Yuto Takano39639672021-08-05 19:47:48 +0100799
Yuto Takano5473be22021-08-17 10:14:01 +0100800 self.output_check_result("Likely typos", problems)
Yuto Takano81528c02021-08-06 16:22:06 +0100801 return len(problems)
802
Yuto Takano5473be22021-08-17 10:14:01 +0100803 def output_check_result(self, name, problems):
Yuto Takano81528c02021-08-06 16:22:06 +0100804 """
805 Write out the PASS/FAIL status of a performed check depending on whether
806 there were problems.
Yuto Takanod70d4462021-08-09 12:45:51 +0100807
808 Args:
Yuto Takanod70d4462021-08-09 12:45:51 +0100809 * name: the name of the test
810 * problems: a List of encountered Problems
Yuto Takano81528c02021-08-06 16:22:06 +0100811 """
Yuto Takano39639672021-08-05 19:47:48 +0100812 if problems:
Yuto Takano55614b52021-08-07 01:00:18 +0100813 self.log.info("{}: FAIL\n".format(name))
814 for problem in problems:
815 self.log.warning(str(problem))
Darryl Greend5802922018-05-08 15:30:59 +0100816 else:
Yuto Takano81528c02021-08-06 16:22:06 +0100817 self.log.info("{}: PASS".format(name))
Darryl Greend5802922018-05-08 15:30:59 +0100818
Yuto Takano39639672021-08-05 19:47:48 +0100819def main():
820 """
Yuto Takano55c6c872021-08-09 15:35:19 +0100821 Perform argument parsing, and create an instance of CodeParser and
822 NameChecker to begin the core operation.
Yuto Takano39639672021-08-05 19:47:48 +0100823 """
Yuto Takanof005c332021-08-09 13:56:36 +0100824 parser = argparse.ArgumentParser(
Yuto Takano39639672021-08-05 19:47:48 +0100825 formatter_class=argparse.RawDescriptionHelpFormatter,
826 description=(
827 "This script confirms that the naming of all symbols and identifiers "
828 "in Mbed TLS are consistent with the house style and are also "
829 "self-consistent.\n\n"
Yuto Takanof005c332021-08-09 13:56:36 +0100830 "Expected to be run from the MbedTLS root directory.")
831 )
832 parser.add_argument(
833 "-v", "--verbose",
834 action="store_true",
835 help="show parse results"
836 )
837 parser.add_argument(
838 "-q", "--quiet",
839 action="store_true",
840 help="hide unnecessary text, explanations, and highlighs"
841 )
Darryl Greend5802922018-05-08 15:30:59 +0100842
Yuto Takanof005c332021-08-09 13:56:36 +0100843 args = parser.parse_args()
Darryl Greend5802922018-05-08 15:30:59 +0100844
Yuto Takano55c6c872021-08-09 15:35:19 +0100845 # Configure the global logger, which is then passed to the classes below
846 log = logging.getLogger()
847 log.setLevel(logging.DEBUG if args.verbose else logging.INFO)
848 log.addHandler(logging.StreamHandler())
849
Darryl Greend5802922018-05-08 15:30:59 +0100850 try:
Yuto Takano55c6c872021-08-09 15:35:19 +0100851 code_parser = CodeParser(log)
852 parse_result = code_parser.comprehensive_parse()
Yuto Takanod93fa372021-08-06 23:05:55 +0100853 except Exception: # pylint: disable=broad-except
Darryl Greend5802922018-05-08 15:30:59 +0100854 traceback.print_exc()
855 sys.exit(2)
856
Yuto Takano55c6c872021-08-09 15:35:19 +0100857 name_checker = NameChecker(parse_result, log)
858 return_code = name_checker.perform_checks(quiet=args.quiet)
859
860 sys.exit(return_code)
861
Darryl Greend5802922018-05-08 15:30:59 +0100862if __name__ == "__main__":
Yuto Takano39639672021-08-05 19:47:48 +0100863 main()