diff --git a/clang/test/utils/update_cc_test_checks/Inputs/basic-cplusplus.cpp b/clang/test/utils/update_cc_test_checks/Inputs/basic-cplusplus.cpp new file mode 100644 --- /dev/null +++ b/clang/test/utils/update_cc_test_checks/Inputs/basic-cplusplus.cpp @@ -0,0 +1,25 @@ +// Basic C++ test for update_cc_test_checks +// RUN: %clang_cc1 -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s | FileCheck %s + +class Foo { + int x; + +public: + explicit Foo(int x); + ~Foo(); + inline int function_defined_inline(int arg) const { + return arg + x; + } + inline int function_defined_out_of_line(int arg) const; +}; + +Foo::Foo(int x) : x(x) {} +Foo::~Foo() {} +int Foo::function_defined_out_of_line(int arg) const { return x - arg; } + +// Call the inline methods to ensure the LLVM IR is generated: +int main() { + Foo f(1); + f.function_defined_inline(2); + f.function_defined_out_of_line(3); +} diff --git a/clang/test/utils/update_cc_test_checks/Inputs/basic-cplusplus.cpp.expected b/clang/test/utils/update_cc_test_checks/Inputs/basic-cplusplus.cpp.expected new file mode 100644 --- /dev/null +++ b/clang/test/utils/update_cc_test_checks/Inputs/basic-cplusplus.cpp.expected @@ -0,0 +1,80 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py +// Basic C++ test for update_cc_test_checks +// RUN: %clang_cc1 -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s | FileCheck %s + +class Foo { + int x; + +public: + explicit Foo(int x); + ~Foo(); +// CHECK-LABEL: @_ZNK3Foo23function_defined_inlineEi( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[THIS_ADDR:%.*]] = alloca %class.Foo*, align 8 +// CHECK-NEXT: [[ARG_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store %class.Foo* [[THIS:%.*]], %class.Foo** [[THIS_ADDR]], align 8 +// CHECK-NEXT: store i32 [[ARG:%.*]], i32* [[ARG_ADDR]], align 4 +// CHECK-NEXT: [[THIS1:%.*]] = load %class.Foo*, %class.Foo** [[THIS_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* [[ARG_ADDR]], align 4 +// CHECK-NEXT: [[X:%.*]] = getelementptr inbounds [[CLASS_FOO:%.*]], %class.Foo* [[THIS1]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, i32* [[X]], align 4 +// CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP0]], [[TMP1]] +// CHECK-NEXT: ret i32 [[ADD]] +// + inline int function_defined_inline(int arg) const { + return arg + x; + } + inline int function_defined_out_of_line(int arg) const; +}; + +// CHECK-LABEL: @_ZN3FooC1Ei( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[THIS_ADDR:%.*]] = alloca %class.Foo*, align 8 +// CHECK-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store %class.Foo* [[THIS:%.*]], %class.Foo** [[THIS_ADDR]], align 8 +// CHECK-NEXT: store i32 [[X:%.*]], i32* [[X_ADDR]], align 4 +// CHECK-NEXT: [[THIS1:%.*]] = load %class.Foo*, %class.Foo** [[THIS_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* [[X_ADDR]], align 4 +// CHECK-NEXT: call void @_ZN3FooC2Ei(%class.Foo* [[THIS1]], i32 [[TMP0]]) +// CHECK-NEXT: ret void +// +Foo::Foo(int x) : x(x) {} +// CHECK-LABEL: @_ZN3FooD1Ev( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[THIS_ADDR:%.*]] = alloca %class.Foo*, align 8 +// CHECK-NEXT: store %class.Foo* [[THIS:%.*]], %class.Foo** [[THIS_ADDR]], align 8 +// CHECK-NEXT: [[THIS1:%.*]] = load %class.Foo*, %class.Foo** [[THIS_ADDR]], align 8 +// CHECK-NEXT: call void @_ZN3FooD2Ev(%class.Foo* [[THIS1]]) #2 +// CHECK-NEXT: ret void +// +Foo::~Foo() {} +// CHECK-LABEL: @_ZNK3Foo28function_defined_out_of_lineEi( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[THIS_ADDR:%.*]] = alloca %class.Foo*, align 8 +// CHECK-NEXT: [[ARG_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store %class.Foo* [[THIS:%.*]], %class.Foo** [[THIS_ADDR]], align 8 +// CHECK-NEXT: store i32 [[ARG:%.*]], i32* [[ARG_ADDR]], align 4 +// CHECK-NEXT: [[THIS1:%.*]] = load %class.Foo*, %class.Foo** [[THIS_ADDR]], align 8 +// CHECK-NEXT: [[X:%.*]] = getelementptr inbounds [[CLASS_FOO:%.*]], %class.Foo* [[THIS1]], i32 0, i32 0 +// CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* [[X]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, i32* [[ARG_ADDR]], align 4 +// CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[TMP0]], [[TMP1]] +// CHECK-NEXT: ret i32 [[SUB]] +// +int Foo::function_defined_out_of_line(int arg) const { return x - arg; } + +// Call the inline methods to ensure the LLVM IR is generated: +// CHECK-LABEL: @main( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[F:%.*]] = alloca [[CLASS_FOO:%.*]], align 4 +// CHECK-NEXT: call void @_ZN3FooC1Ei(%class.Foo* [[F]], i32 1) +// CHECK-NEXT: [[CALL:%.*]] = call i32 @_ZNK3Foo23function_defined_inlineEi(%class.Foo* [[F]], i32 2) +// CHECK-NEXT: [[CALL1:%.*]] = call i32 @_ZNK3Foo28function_defined_out_of_lineEi(%class.Foo* [[F]], i32 3) +// CHECK-NEXT: call void @_ZN3FooD1Ev(%class.Foo* [[F]]) #2 +// CHECK-NEXT: ret i32 0 +// +int main() { + Foo f(1); + f.function_defined_inline(2); + f.function_defined_out_of_line(3); +} diff --git a/clang/test/utils/update_cc_test_checks/basic-cplusplus.test b/clang/test/utils/update_cc_test_checks/basic-cplusplus.test new file mode 100644 --- /dev/null +++ b/clang/test/utils/update_cc_test_checks/basic-cplusplus.test @@ -0,0 +1,7 @@ +## Test that CHECK lines are generated before the definion and not the declaration + +# RUN: cp %S/Inputs/basic-cplusplus.cpp %t.cpp && %update_cc_test_checks %t.cpp +# RUN: diff -u %S/Inputs/basic-cplusplus.cpp.expected %t.cpp +## Check that re-running update_cc_test_checks doesn't change the output +# RUN: %update_cc_test_checks %t.cpp +# RUN: diff -u %S/Inputs/basic-cplusplus.cpp.expected %t.cpp diff --git a/llvm/utils/update_cc_test_checks.py b/llvm/utils/update_cc_test_checks.py --- a/llvm/utils/update_cc_test_checks.py +++ b/llvm/utils/update_cc_test_checks.py @@ -61,11 +61,14 @@ def parse_clang_ast_json(node): node_kind = node['kind'] # Recurse for the following nodes that can contain nested function decls: - if node_kind in ('NamespaceDecl', 'LinkageSpecDecl', 'TranslationUnitDecl'): - for inner in node['inner']: - parse_clang_ast_json(inner) + if node_kind in ('NamespaceDecl', 'LinkageSpecDecl', 'TranslationUnitDecl', + 'CXXRecordDecl'): + if 'inner' in node: + for inner in node['inner']: + parse_clang_ast_json(inner) # Otherwise we ignore everything except functions: - if node['kind'] != 'FunctionDecl': + if node_kind not in ('FunctionDecl', 'CXXMethodDecl', 'CXXConstructorDecl', + 'CXXDestructorDecl', 'CXXConversionDecl'): return if node.get('isImplicit') is True and node.get('storageClass') == 'extern': common.debug('Skipping builtin function:', node['name'], '@', node['loc'])