xref: /aosp_15_r20/external/cronet/base/allocator/partition_allocator/PRESUBMIT.py (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1# Copyright 2024 The Chromium Authors
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4"""Chromium presubmit script for base/allocator/partition_allocator.
5
6See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
7for more details on the presubmit API built into depot_tools.
8"""
9
10PRESUBMIT_VERSION = '2.0.0'
11
12_PARTITION_ALLOC_BASE_PATH = 'base/allocator/partition_allocator/src/'
13
14
15# This is adapted from Chromium's PRESUBMIT.py. The differences are:
16# - Base path: It is relative to the partition_alloc's source directory instead
17#              of chromium.
18# - Stricter: A single format is allowed: `PATH_ELEM_FILE_NAME_H_`.
19def CheckForIncludeGuards(input_api, output_api):
20    """Check that header files have proper include guards"""
21
22    def guard_for_file(file):
23        local_path = file.LocalPath()
24        if input_api.is_windows:
25            local_path = local_path.replace('\\', '/')
26        assert local_path.startswith(_PARTITION_ALLOC_BASE_PATH)
27        guard = input_api.os_path.normpath(
28            local_path[len(_PARTITION_ALLOC_BASE_PATH):])
29        guard = guard + '_'
30        guard = guard.upper()
31        guard = input_api.re.sub(r'[+\\/.-]', '_', guard)
32        return guard
33
34    def is_partition_alloc_header_file(f):
35        # We only check header files.
36        return f.LocalPath().endswith('.h')
37
38    errors = []
39
40    for f in input_api.AffectedSourceFiles(is_partition_alloc_header_file):
41        expected_guard = guard_for_file(f)
42
43        # Unlike the Chromium's top-level PRESUBMIT.py, we enforce a stricter
44        # rule which accepts only `PATH_ELEM_FILE_NAME_H_` per coding style.
45        guard_name_pattern = input_api.re.escape(expected_guard)
46        guard_pattern = input_api.re.compile(r'#ifndef\s+(' +
47                                             guard_name_pattern + ')')
48
49        guard_name = None
50        guard_line_number = None
51        seen_guard_end = False
52        for line_number, line in enumerate(f.NewContents()):
53            if guard_name is None:
54                match = guard_pattern.match(line)
55                if match:
56                    guard_name = match.group(1)
57                    guard_line_number = line_number
58                continue
59
60            # The line after #ifndef should have a #define of the same name.
61            if line_number == guard_line_number + 1:
62                expected_line = '#define %s' % guard_name
63                if line != expected_line:
64                    errors.append(
65                        output_api.PresubmitPromptWarning(
66                            'Missing "%s" for include guard' % expected_line,
67                            ['%s:%d' % (f.LocalPath(), line_number + 1)],
68                            'Expected: %r\nGot: %r' % (expected_line, line)))
69
70            if not seen_guard_end and line == '#endif  // %s' % guard_name:
71                seen_guard_end = True
72                continue
73
74            if seen_guard_end:
75                if line.strip() != '':
76                    errors.append(
77                        output_api.PresubmitPromptWarning(
78                            'Include guard %s not covering the whole file' %
79                            (guard_name), [f.LocalPath()]))
80                    break  # Nothing else to check and enough to warn once.
81
82        if guard_name is None:
83            errors.append(
84                output_api.PresubmitPromptWarning(
85                    'Missing include guard in %s\n'
86                    'Recommended name: %s\n' %
87                    (f.LocalPath(), expected_guard)))
88
89    return errors
90