File: clang-import_test.py

package info (click to toggle)
cppcheck 2.18.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 26,132 kB
  • sloc: cpp: 268,935; python: 20,890; ansic: 8,090; sh: 1,045; makefile: 1,008; xml: 1,005; cs: 291
file content (305 lines) | stat: -rw-r--r-- 9,897 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305

# python -m pytest test-clang-import.py

import os
import re
import subprocess
import sys
import pytest
from testutils import cppcheck, assert_cppcheck

try:
    # TODO: handle exitcode?
    subprocess.call(['clang', '--version'])
except OSError:
    pytest.skip("'clang' does not exist", allow_module_level=True)


# the IDs differ with Visual Studio
if sys.platform == 'win32':
    pytest.skip(allow_module_level=True)


def __get_debug_section(title, stdout):
    s = re.sub(r'0x[0-9a-fA-F]+', '0x12345678', stdout)
    s = re.sub(r'nestedIn: Struct', 'nestedIn: Class', s)
    s = re.sub(r'classDef: struct', 'classDef: class', s)
    s = re.sub(r'isInline: [a-z]+', 'isInline: ---', s)
    s = re.sub(r'needInitialization: .*', 'needInitialization: ---', s)
    s = re.sub(r'functionOf: .*', 'functionOf: ---', s)
    s = re.sub(r'0x12345678 Struct', '0x12345678 Class', s)

    if title == '##AST':
        # TODO set types
        s = re.sub(r"return '[a-zA-Z0-9: *]+'", "return", s)

    pos1 = s.find(title)
    assert pos1 > 0, 'title not found'
    pos1 = s.find('\n', pos1) + 1
    assert pos1 > 0
    pos2 = s.find("\n##", pos1)
    if pos2 < 0:
        return s[pos1:]
    return s[pos1:pos2-1]


def __check_symbol_database(tmpdir, code):
    testfile = os.path.join(tmpdir, 'test.cpp')
    with open(testfile, 'w+t') as f:
        f.write(code)
    ret1, stdout1, _ = cppcheck(['-q', '--clang', '--debug-symdb', testfile])
    ret2, stdout2, _ = cppcheck(['-q', '--debug-symdb', testfile])
    assert 0 == ret1, stdout1
    assert 0 == ret2, stdout2
    assert __get_debug_section('### Symbol database', stdout1) == __get_debug_section('### Symbol database', stdout2)


def __check_ast(tmpdir, code):
    testfile = os.path.join(tmpdir, 'test.cpp')
    with open(testfile, 'w+t') as f:
        f.write(code)
    ret1, stdout1, _ = cppcheck(['-q', '--clang', '--debug-ast', testfile])
    ret2, stdout2, _ = cppcheck(['-q', '--debug-ast', testfile])
    assert 0 == ret1, stdout1
    assert 0 == ret2, stdout1
    assert __get_debug_section('##AST', stdout1) == __get_debug_section('##AST', stdout2)



def test_symbol_database_1(tmpdir):
    __check_symbol_database(tmpdir, 'int main(){return 0;}')

def test_symbol_database_2(tmpdir):
    __check_symbol_database(tmpdir, 'struct Foo { void f(); }; void Foo::f() {}')

def test_symbol_database_3(tmpdir):
    __check_symbol_database(tmpdir, 'struct Fred { int a; }; int b; void f(int c, int d) { int e; }')

def test_symbol_database_4(tmpdir):
    __check_symbol_database(tmpdir, 'void f(const int x) {}')

def test_symbol_database_5(tmpdir):
    __check_symbol_database(tmpdir, 'void f(int);')

def test_symbol_database_6(tmpdir):
    __check_symbol_database(tmpdir, 'inline static int foo(int x) { return x; }')

def test_symbol_database_7(tmpdir):
    __check_symbol_database(tmpdir, 'struct S {int x;}; void f(struct S *s) {}')

def test_symbol_database_class_access_1(tmpdir):
    __check_symbol_database(tmpdir, 'class Fred { void foo ( ) {} } ;')

def test_symbol_database_class_access_2(tmpdir):
    __check_symbol_database(tmpdir, 'class Fred { protected: void foo ( ) {} } ;')

def test_symbol_database_class_access_3(tmpdir):
    __check_symbol_database(tmpdir, 'class Fred { public: void foo ( ) {} } ;')

def test_symbol_database_operator(tmpdir):
    __check_symbol_database(tmpdir, 'struct Fred { void operator=(int x); };')

def test_symbol_database_struct_1(tmpdir):
    __check_symbol_database(tmpdir, 'struct S {};')

def test_ast_calculations(tmpdir):
    __check_ast(tmpdir, 'int x = 5; int y = (x + 4) * 2;')
    __check_ast(tmpdir, 'long long dostuff(int x) { return x ? 3 : 5; }')

def test_ast_control_flow(tmpdir):
    __check_ast(tmpdir, 'void foo(int x) { if (x > 5){} }')
    __check_ast(tmpdir, 'int dostuff() { for (int x = 0; x < 10; x++); }')
    __check_ast(tmpdir, 'void foo(int x) { switch (x) {case 1: break; } }')
    __check_ast(tmpdir, 'void foo(int a, int b, int c) { foo(a,b,c); }')

def test_ast(tmpdir):
    __check_ast(tmpdir, 'struct S { int x; }; S* foo() { return new S(); }')

def test_log(tmpdir):
    test_file = os.path.join(tmpdir, 'test.cpp')
    with open(test_file, 'wt'):
        pass

    args = ['--clang', test_file]
    out_lines = [
        'Checking {} ...'.format(test_file).replace('\\', '/'),
    ]

    assert_cppcheck(args, ec_exp=0, err_exp=[], out_exp=out_lines)


def test_warning(tmpdir):  # #12424
    test_file = os.path.join(tmpdir, 'test_2')
    with open(test_file, 'wt') as f:
        f.write('''void f() {}''')

    exitcode, stdout, stderr = cppcheck(['-q', '--enable=warning', '--clang', test_file])
    assert exitcode == 0, stderr # do not assert
    assert stdout == ''
    assert stderr == ''


def __test_cmd(tmp_path, file_name, extra_args, stdout_exp_1, content=''):
    test_file = tmp_path / file_name
    with open(test_file, 'wt') as f:
        f.write(content)

    args = [
        '--enable=information',
        '--verbose',
        '--clang',
        file_name
    ]

    args += extra_args

    if stdout_exp_1:
        stdout_exp_1 += ' '

    exitcode, stdout, stderr = cppcheck(args, cwd=tmp_path)
    assert exitcode == 0, stderr if not stdout else stdout
    assert stderr == ''
    assert stdout.splitlines() == [
        'Checking {} ...'.format(file_name),
        'clang -fsyntax-only -Xclang -ast-dump -fno-color-diagnostics {}{}'.format(stdout_exp_1, file_name)
    ]


def test_cmd_c(tmp_path):
    __test_cmd(tmp_path, 'test.c', [], '-x c')


def test_cmd_cpp(tmp_path):
    __test_cmd(tmp_path, 'test.cpp', [], '-x c++')


# files with unknown extensions are treated as C++
def test_cmd_unk(tmp_path):
    __test_cmd(tmp_path, 'test.cplusplus', [], '-x c++')


# headers are treated as C by default
def test_cmd_hdr(tmp_path):
    __test_cmd(tmp_path, 'test.h', [], '-x c')


def test_cmd_hdr_probe(tmp_path):
    __test_cmd(tmp_path, 'test.h', ['--cpp-header-probe'], '-x c++', '// -*- C++ -*-')


def test_cmd_inc(tmp_path):
    inc_path = tmp_path / 'inc'
    os.makedirs(inc_path)
    __test_cmd(tmp_path, 'test.cpp',['-Iinc'], '-x c++ -Iinc/')


def test_cmd_def(tmp_path):
    __test_cmd(tmp_path, 'test.cpp',['-DDEF'], '-x c++ -DDEF=1')


def test_cmd_include(tmp_path):
    inc_file = tmp_path / 'inc.h'
    with open(inc_file, 'wt'):
        pass
    __test_cmd(tmp_path, 'test.cpp',['--include=inc.h'], '-x c++ --include inc.h')


def test_cmd_enforce_c(tmp_path):  # #13128
    __test_cmd(tmp_path, 'test.cpp',['-x', 'c'], '-x c')


def test_cmd_enforce_cpp(tmp_path):  # #13128
    __test_cmd(tmp_path, 'test.c',['-x', 'c++'], '-x c++')


def test_cmd_std_c(tmp_path):  # #13129
    __test_cmd(tmp_path, 'test.cpp',['--std=c89', '--std=c++14'], '-x c++ -std=c++14')


# TODO: remove when we inject the build-dir into all tests
def test_cmd_std_c_builddir(tmp_path):  # #13129
    build_dir = tmp_path / 'b1'
    os.makedirs(build_dir)
    __test_cmd(tmp_path, 'test.cpp',['--std=c89', '--std=c++14', '--cppcheck-build-dir={}'.format(build_dir)], '-x c++ -std=c++14')


def test_cmd_std_cpp(tmp_path):  # #13129
    __test_cmd(tmp_path, 'test.c',['--std=c89', '--std=c++14'], '-x c -std=c89')


def test_cmd_std_c_enforce(tmp_path):  # #13128/#13129
    __test_cmd(tmp_path, 'test.cpp',['--language=c', '--std=c89', '--std=c++14'], '-x c -std=c89')


def test_cmd_std_cpp_enforce(tmp_path):  # #13128/#13129
    __test_cmd(tmp_path, 'test.c',['--language=c++', '--std=c89', '--std=c++14'], '-x c++ -std=c++14')


def test_cmd_std_c_enforce_alias(tmp_path):  # #13128/#13129/#13130
    __test_cmd(tmp_path, 'test.c',['--language=c', '--std=gnu99', '--std=gnu++11'], '-x c -std=gnu99')


def test_cmd_std_c_enforce_alias_2(tmp_path):  # #13128/#13129/#13130
    __test_cmd(tmp_path, 'test.c',['--language=c', '--std=iso9899:1999', '--std=gnu++11'], '-x c -std=iso9899:1999')


def test_cmd_std_cpp_enforce_alias(tmp_path):  # #13128/#13129/#13130
    __test_cmd(tmp_path, 'test.c',['--language=c++', '--std=gnu99', '--std=gnu++11'], '-x c++ -std=gnu++11')


def test_debug_clang_output(tmp_path):
    test_file = tmp_path / 'test.c'
    with open(test_file, 'wt') as f:
        f.write(
"""
void f() {}
""")

    args = [
        '-q',
        '--clang',
        '--debug-clang-output',
        str(test_file)
    ]

    exitcode, stdout, stderr = cppcheck(args)
    assert exitcode == 0, stderr if not stdout else stdout
    assert stderr == ''
    assert stdout.startswith('TranslationUnitDecl'), stdout
    assert stdout.find(str(test_file)) != -1, stdout


def test_debug_clang_output_failure_exitcode(tmp_path):
    # the given code will cause clang to fail with an exitcode
    #
    # Failed to execute 'clang -fsyntax-only -Xclang -ast-dump -fno-color-diagnostics -x c++ a.cpp 2>&1' - (exitcode: 1 / output: a.cpp:3:12: error: indirection requires pointer operand ('int' invalid)
    # 3 |     (void)(*0);
    # |            ^~
    # 1 error generated.
    # TranslationUnitDecl 0x6127d5d9d4e8 <<invalid sloc>> <invalid sloc>
    # ...
    test_file = tmp_path / 'test.c'
    with open(test_file, 'wt') as f:
        f.write(
"""void f()
{
    (void)(*0);
}
""")

    args = [
        '-q',
        '--clang',
        '--debug-clang-output',
        '--no-cppcheck-build-dir',  # TODO: test without this?
        str(test_file)
    ]

    exitcode, stdout, stderr = cppcheck(args)
    assert exitcode == 0, stderr if not stdout else stdout
    stderr_lines = stderr.splitlines()
    assert len(stderr_lines) > 5, stderr_lines
    assert (stderr_lines[0] ==
            "Failed to execute 'clang -fsyntax-only -Xclang -ast-dump -fno-color-diagnostics -x c {} 2>&1' - (exitcode: 1 / output: {}:3:12: error: indirection requires pointer operand ('int' invalid)".format(test_file, test_file))
    assert stdout.find('TranslationUnitDecl') != -1, stdout
    assert stdout.find(str(test_file)) != -1, stdout