check_redundant_namespace_qualifiers.py
Go to the documentation of this file.
1 #!/usr/bin/env python3
2 # Copyright 2021 gRPC authors.
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 
16 # Eliminate the kind of redundant namespace qualifiers that tend to
17 # creep in when converting C to C++.
18 
19 import collections
20 import os
21 import re
22 import sys
23 
24 
25 def find_closing_mustache(contents, initial_depth):
26  """Find the closing mustache for a given number of open mustaches."""
27  depth = initial_depth
28  start_len = len(contents)
29  while contents:
30  # Skip over strings.
31  if contents[0] == '"':
32  contents = contents[1:]
33  while contents[0] != '"':
34  if contents.startswith('\\\\'):
35  contents = contents[2:]
36  elif contents.startswith('\\"'):
37  contents = contents[2:]
38  else:
39  contents = contents[1:]
40  contents = contents[1:]
41  # And characters that might confuse us.
42  elif contents.startswith("'{'") or contents.startswith(
43  "'\"'") or contents.startswith("'}'"):
44  contents = contents[3:]
45  # Skip over comments.
46  elif contents.startswith("//"):
47  contents = contents[contents.find('\n'):]
48  elif contents.startswith("/*"):
49  contents = contents[contents.find('*/') + 2:]
50  # Count up or down if we see a mustache.
51  elif contents[0] == '{':
52  contents = contents[1:]
53  depth += 1
54  elif contents[0] == '}':
55  contents = contents[1:]
56  depth -= 1
57  if depth == 0:
58  return start_len - len(contents)
59  # Skip over everything else.
60  else:
61  contents = contents[1:]
62  return None
63 
64 
65 def is_a_define_statement(match, body):
66  """See if the matching line begins with #define"""
67  # This does not yet help with multi-line defines
68  m = re.search(r"^#define.*{}$".format(match.group(0)), body[:match.end()],
69  re.MULTILINE)
70  return m is not None
71 
72 
73 def update_file(contents, namespaces):
74  """Scan the contents of a file, and for top-level namespaces in namespaces remove redundant usages."""
75  output = ''
76  while contents:
77  m = re.search(r'namespace ([a-zA-Z0-9_]*) {', contents)
78  if not m:
79  output += contents
80  break
81  output += contents[:m.end()]
82  contents = contents[m.end():]
83  end = find_closing_mustache(contents, 1)
84  if end is None:
85  print('Failed to find closing mustache for namespace {}'.format(
86  m.group(1)))
87  print('Remaining text:')
88  print(contents)
89  sys.exit(1)
90  body = contents[:end]
91  namespace = m.group(1)
92  if namespace in namespaces:
93  while body:
94  # Find instances of 'namespace::'
95  m = re.search(r'\b' + namespace + r'::\b', body)
96  if not m:
97  break
98  # Ignore instances of '::namespace::' -- these are usually meant to be there.
99  if m.start() >= 2 and body[m.start() - 2:].startswith('::'):
100  output += body[:m.end()]
101  # Ignore #defines, since they may be used anywhere
102  elif is_a_define_statement(m, body):
103  output += body[:m.end()]
104  else:
105  output += body[:m.start()]
106  body = body[m.end():]
107  output += body
108  contents = contents[end:]
109  return output
110 
111 
112 # self check before doing anything
113 _TEST = """
114 namespace bar {
115  namespace baz {
116  }
117 }
118 namespace foo {}
119 namespace foo {
120  foo::a;
121 }
122 """
123 _TEST_EXPECTED = """
124 namespace bar {
125  namespace baz {
126  }
127 }
128 namespace foo {}
129 namespace foo {
130  a;
131 }
132 """
133 output = update_file(_TEST, ['foo'])
134 if output != _TEST_EXPECTED:
135  import difflib
136  print('FAILED: self check')
137  print('\n'.join(
138  difflib.ndiff(_TEST_EXPECTED.splitlines(1), output.splitlines(1))))
139  sys.exit(1)
140 
141 # Main loop.
142 Config = collections.namedtuple('Config', ['dirs', 'namespaces'])
143 
144 _CONFIGURATION = (Config(['src/core', 'test/core'], ['grpc_core']),)
145 
146 changed = []
147 
148 for config in _CONFIGURATION:
149  for dir in config.dirs:
150  for root, dirs, files in os.walk(dir):
151  for file in files:
152  if file.endswith('.cc') or file.endswith('.h'):
153  path = os.path.join(root, file)
154  try:
155  with open(path) as f:
156  contents = f.read()
157  except IOError:
158  continue
159  updated = update_file(contents, config.namespaces)
160  if updated != contents:
161  changed.append(path)
162  with open(os.path.join(root, file), 'w') as f:
163  f.write(updated)
164 
165 if changed:
166  print('The following files were changed:')
167  for path in changed:
168  print(' ' + path)
169  sys.exit(1)
http2_test_server.format
format
Definition: http2_test_server.py:118
check_redundant_namespace_qualifiers.update_file
def update_file(contents, namespaces)
Definition: check_redundant_namespace_qualifiers.py:73
check_redundant_namespace_qualifiers.find_closing_mustache
def find_closing_mustache(contents, initial_depth)
Definition: check_redundant_namespace_qualifiers.py:25
check_redundant_namespace_qualifiers.is_a_define_statement
def is_a_define_statement(match, body)
Definition: check_redundant_namespace_qualifiers.py:65
check_redundant_namespace_qualifiers.Config
Config
Definition: check_redundant_namespace_qualifiers.py:142
open
#define open
Definition: test-fs.c:46
len
int len
Definition: abseil-cpp/absl/base/internal/low_level_alloc_test.cc:46


grpc
Author(s):
autogenerated on Thu Mar 13 2025 02:58:45