d73bf50a1f6cf4a483d7e1b2d237062b4c3f1e3e
[rust-lightning] / ci / check-cfg-flags.py
1 #!/usr/bin/env python3
2 # Rust is fairly relaxed in checking the validity of arguments passed to #[cfg].
3 # While it should probably be more strict when checking features, it cannot be
4 # strict when checking loose cfg tags, because those can be anything and are
5 # simply passed to rustc via unconstrained arguments.
6 #
7 # Thus, we do it for rustc manually, but scanning all our source and checking
8 # that all our cfg tags match a known cfg tag.
9 import sys, glob, re
10
11 def check_feature(feature):
12     if feature == "std":
13         pass
14     elif feature == "no-std":
15         pass
16     elif feature == "possiblyrandom":
17         pass
18     elif feature == "getrandom":
19         pass
20     elif feature == "hashbrown":
21         pass
22     elif feature == "backtrace":
23         pass
24     elif feature == "grind_signatures":
25         pass
26     elif feature == "unsafe_revoked_tx_signing":
27         pass
28     elif feature == "futures":
29         pass
30     elif feature == "tokio":
31         pass
32     elif feature == "rest-client":
33         pass
34     elif feature == "rpc-client":
35         pass
36     elif feature == "serde":
37         pass
38     elif feature == "esplora-blocking":
39         pass
40     elif feature == "esplora-async":
41         pass
42     elif feature == "async-interface":
43         pass
44     elif feature == "electrum":
45         pass
46     elif feature == "time":
47         pass
48     elif feature == "_test_utils":
49         pass
50     elif feature == "_test_vectors":
51         pass
52     elif feature == "afl":
53         pass
54     elif feature == "honggfuzz":
55         pass
56     elif feature == "libfuzzer_fuzz":
57         pass
58     elif feature == "stdin_fuzz":
59         pass
60     elif feature == "max_level_off":
61         pass
62     elif feature == "max_level_error":
63         pass
64     elif feature == "max_level_warn":
65         pass
66     elif feature == "max_level_info":
67         pass
68     elif feature == "max_level_debug":
69         pass
70     elif feature == "max_level_trace":
71         pass
72     else:
73         print("Bad feature: " + feature)
74         assert False
75
76 def check_target_os(os):
77     if os == "windows":
78         pass
79     else:
80         assert False
81
82 def check_cfg_tag(cfg):
83     if cfg == "fuzzing":
84         pass
85     elif cfg == "secp256k1_fuzz":
86         pass
87     elif cfg == "hashes_fuzz":
88         pass
89     elif cfg == "test":
90         pass
91     elif cfg == "debug_assertions":
92         pass
93     elif cfg == "c_bindings":
94         pass
95     elif cfg == "ldk_bench":
96         pass
97     elif cfg == "taproot":
98         pass
99     elif cfg == "async_signing":
100         pass
101     elif cfg == "require_route_graph_test":
102         pass
103     elif cfg == "dual_funding":
104         pass
105     elif cfg == "splicing":
106         pass
107     elif cfg == "async_payments":
108         pass
109     else:
110         print("Bad cfg tag: " + cfg)
111         assert False
112
113 def check_cfg_args(cfg):
114     if cfg.startswith("all(") or cfg.startswith("any(") or cfg.startswith("not("):
115         brackets = 1
116         pos = 4
117         while pos < len(cfg):
118             if cfg[pos] == "(":
119                 brackets += 1
120             elif cfg[pos] == ")":
121                 brackets -= 1
122                 if brackets == 0:
123                     check_cfg_args(cfg[4:pos])
124                     if pos + 1 != len(cfg):
125                         assert cfg[pos + 1] == ","
126                         check_cfg_args(cfg[pos + 2:].strip())
127                     return
128             pos += 1
129         assert False
130         assert(cfg.endswith(")"))
131         check_cfg_args(cfg[4:len(cfg)-1])
132     else:
133         parts = [part.strip() for part in cfg.split(",", 1)]
134         if len(parts) > 1:
135             for part in parts:
136                 check_cfg_args(part)
137         elif cfg.startswith("feature") or cfg.startswith("target_os") or cfg.startswith("target_pointer_width"):
138             arg = cfg
139             if cfg.startswith("feature"):
140                 arg = arg[7:].strip()
141             elif cfg.startswith("target_os"):
142                 arg = arg[9:].strip()
143             else:
144                 arg = arg[20:].strip()
145             assert arg.startswith("=")
146             arg = arg[1:].strip()
147             assert arg.startswith("\"")
148             assert arg.endswith("\"")
149             arg = arg[1:len(arg)-1]
150             assert not "\"" in arg
151             if cfg.startswith("feature"):
152                 check_feature(arg)
153             elif cfg.startswith("target_os"):
154                 check_target_os(arg)
155             else:
156                 assert arg == "32" or arg == "64"
157         else:
158             check_cfg_tag(cfg.strip())
159
160 cfg_regex = re.compile("#\[cfg\((.*)\)\]")
161 for path in glob.glob(sys.path[0] + "/../**/*.rs", recursive = True):
162     with open(path, "r") as file:
163         while True:
164             line = file.readline()
165             if not line:
166                 break
167             if "#[cfg(" in line:
168                 if not line.strip().startswith("//"):
169                     cfg_part = cfg_regex.match(line.strip()).group(1)
170                     check_cfg_args(cfg_part)