jrtc27 updated this revision to Diff 359643.
jrtc27 added a comment.

Drop the --llvm-bin test; only basic-cplusplus.test does that (which happened 
to be the one I used as a reference), and that only needs to be done for one 
file as it has no relation to the input.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D106243/new/

https://reviews.llvm.org/D106243

Files:
  
clang/test/utils/update_cc_test_checks/Inputs/explicit-template-instantiation.cpp
  
clang/test/utils/update_cc_test_checks/Inputs/explicit-template-instantiation.cpp.expected
  clang/test/utils/update_cc_test_checks/explicit-template-instantiation.test
  llvm/utils/update_cc_test_checks.py

Index: llvm/utils/update_cc_test_checks.py
===================================================================
--- llvm/utils/update_cc_test_checks.py
+++ llvm/utils/update_cc_test_checks.py
@@ -33,8 +33,8 @@
     '%clangxx': ['--driver-mode=g++'],
 }
 
-def get_line2spell_and_mangled(args, clang_args):
-  ret = {}
+def get_line2func_list(args, clang_args):
+  ret = collections.defaultdict(list)
   # Use clang's JSON AST dump to get the mangled name
   json_dump_args = [args.clang] + clang_args + ['-fsyntax-only', '-o', '-']
   if '-cc1' not in json_dump_args:
@@ -55,26 +55,37 @@
 
   # Parse the clang JSON and add all children of type FunctionDecl.
   # TODO: Should we add checks for global variables being emitted?
-  def parse_clang_ast_json(node):
+  def parse_clang_ast_json(node, loc, search):
     node_kind = node['kind']
     # Recurse for the following nodes that can contain nested function decls:
     if node_kind in ('NamespaceDecl', 'LinkageSpecDecl', 'TranslationUnitDecl',
-                     'CXXRecordDecl'):
+                     'CXXRecordDecl', 'ClassTemplateSpecializationDecl'):
+      # Specializations must use the loc from the specialization, not the
+      # template, and search for the class's spelling as the specialization
+      # does not mention the method names in the source.
+      if node_kind == 'ClassTemplateSpecializationDecl':
+        inner_loc = node['loc']
+        inner_search = node['name']
+      else:
+        inner_loc = None
+        inner_search = None
       if 'inner' in node:
         for inner in node['inner']:
-          parse_clang_ast_json(inner)
+          parse_clang_ast_json(inner, inner_loc, inner_search)
     # Otherwise we ignore everything except functions:
     if node_kind not in ('FunctionDecl', 'CXXMethodDecl', 'CXXConstructorDecl',
                          'CXXDestructorDecl', 'CXXConversionDecl'):
       return
+    if loc is None:
+      loc = node['loc']
     if node.get('isImplicit') is True and node.get('storageClass') == 'extern':
-      common.debug('Skipping builtin function:', node['name'], '@', node['loc'])
+      common.debug('Skipping builtin function:', node['name'], '@', loc)
       return
-    common.debug('Found function:', node['kind'], node['name'], '@', node['loc'])
-    line = node['loc'].get('line')
+    common.debug('Found function:', node['kind'], node['name'], '@', loc)
+    line = loc.get('line')
     # If there is no line it is probably a builtin function -> skip
     if line is None:
-      common.debug('Skipping function without line number:', node['name'], '@', node['loc'])
+      common.debug('Skipping function without line number:', node['name'], '@', loc)
       return
 
     # If there is no 'inner' object, it is a function declaration and we can
@@ -88,20 +99,23 @@
           has_body = True
           break
     if not has_body:
-      common.debug('Skipping function without body:', node['name'], '@', node['loc'])
+      common.debug('Skipping function without body:', node['name'], '@', loc)
       return
     spell = node['name']
+    if search is None:
+      search = spell
     mangled = node.get('mangledName', spell)
-    ret[int(line)-1] = (spell, mangled)
+    ret[int(line)-1].append((spell, mangled, search))
 
   ast = json.loads(stdout)
   if ast['kind'] != 'TranslationUnitDecl':
     common.error('Clang AST dump JSON format changed?')
     sys.exit(2)
-  parse_clang_ast_json(ast)
+  parse_clang_ast_json(ast, None, None)
 
-  for line, func_name in sorted(ret.items()):
-    common.debug('line {}: found function {}'.format(line+1, func_name), file=sys.stderr)
+  for line, funcs in sorted(ret.items()):
+    for func in funcs:
+      common.debug('line {}: found function {}'.format(line+1, func), file=sys.stderr)
   if not ret:
     common.warn('Did not find any functions using', ' '.join(json_dump_args))
   return ret
@@ -222,7 +236,7 @@
                              comment_prefix='//', argparse_callback=infer_dependent_args):
     # Build a list of filechecked and non-filechecked RUN lines.
     run_list = []
-    line2spell_and_mangled_list = collections.defaultdict(list)
+    line2func_list = collections.defaultdict(list)
 
     subs = {
       '%s' : ti.path,
@@ -296,8 +310,8 @@
 
       # Invoke clang -Xclang -ast-dump=json to get mapping from start lines to
       # mangled names. Forward all clang args for now.
-      for k, v in get_line2spell_and_mangled(ti.args, clang_args).items():
-        line2spell_and_mangled_list[k].append(v)
+      for k, v in get_line2func_list(ti.args, clang_args).items():
+        line2func_list[k].extend(v)
 
     func_dict = builder.finish_and_get_func_dict()
     global_vars_seen_dict = {}
@@ -357,15 +371,16 @@
         # Skip special separator comments added by commmon.add_global_checks.
         if line.strip() == '//' + common.SEPARATOR:
           continue
-        if idx in line2spell_and_mangled_list:
+        if idx in line2func_list:
           added = set()
-          for spell, mangled in line2spell_and_mangled_list[idx]:
+          for spell, mangled, search in line2func_list[idx]:
             # One line may contain multiple function declarations.
             # Skip if the mangled name has been added before.
-            # The line number may come from an included file,
-            # we simply require the spelling name to appear on the line
-            # to exclude functions from other files.
-            if mangled in added or spell not in line:
+            # The line number may come from an included file, we simply require
+            # the search string (normally the function's spelling name, but is
+            # the class's spelling name for class specializations) to appear on
+            # the line to exclude functions from other files.
+            if mangled in added or search not in line:
               continue
             if args.functions is None or any(re.search(regex, spell) for regex in args.functions):
               last_line = output_lines[-1].strip()
Index: clang/test/utils/update_cc_test_checks/explicit-template-instantiation.test
===================================================================
--- /dev/null
+++ clang/test/utils/update_cc_test_checks/explicit-template-instantiation.test
@@ -0,0 +1,7 @@
+## Test that CHECK lines are generated for explicit template instantiatons
+
+# RUN: cp %S/Inputs/explicit-template-instantiation.cpp %t.cpp && %update_cc_test_checks %t.cpp
+# RUN: diff -u %S/Inputs/explicit-template-instantiation.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/explicit-template-instantiation.cpp.expected %t.cpp
Index: clang/test/utils/update_cc_test_checks/Inputs/explicit-template-instantiation.cpp.expected
===================================================================
--- /dev/null
+++ clang/test/utils/update_cc_test_checks/Inputs/explicit-template-instantiation.cpp.expected
@@ -0,0 +1,190 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py
+// RUN: %clang_cc1 -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s | FileCheck %s
+
+template <typename T>
+struct Foo {
+private:
+  T x;
+
+public:
+  Foo(T x) : x(x) {}
+  ~Foo() {}
+
+  T get() { return x; }
+  void set(T _x) { x = _x; }
+};
+
+template <typename T>
+struct Bar {
+private:
+  struct Foo<T> foo;
+
+public:
+  Bar(T x) : foo(x) {}
+  ~Bar() {}
+
+  T get() { return foo.get(); }
+  void set(T _x) { foo.set(_x); }
+};
+
+template <typename T>
+struct Baz : Foo<T> {
+public:
+  Baz(T x) : Foo<T>(x) {}
+  ~Baz() {}
+};
+
+// These two specializations should generate lines for all of Foo's methods.
+
+// CHECK-LABEL: @_ZN3FooIcEC1Ec(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca %struct.Foo*, align 8
+// CHECK-NEXT:    [[X_ADDR:%.*]] = alloca i8, align 1
+// CHECK-NEXT:    store %struct.Foo* [[THIS:%.*]], %struct.Foo** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    store i8 [[X:%.*]], i8* [[X_ADDR]], align 1
+// CHECK-NEXT:    [[THIS1:%.*]] = load %struct.Foo*, %struct.Foo** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load i8, i8* [[X_ADDR]], align 1
+// CHECK-NEXT:    call void @_ZN3FooIcEC2Ec(%struct.Foo* nonnull align 1 dereferenceable(1) [[THIS1]], i8 signext [[TMP0]])
+// CHECK-NEXT:    ret void
+//
+// CHECK-LABEL: @_ZN3FooIcED1Ev(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca %struct.Foo*, align 8
+// CHECK-NEXT:    store %struct.Foo* [[THIS:%.*]], %struct.Foo** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[THIS1:%.*]] = load %struct.Foo*, %struct.Foo** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    call void @_ZN3FooIcED2Ev(%struct.Foo* nonnull align 1 dereferenceable(1) [[THIS1]]) #[[ATTR2:[0-9]+]]
+// CHECK-NEXT:    ret void
+//
+// CHECK-LABEL: @_ZN3FooIcE3getEv(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca %struct.Foo*, align 8
+// CHECK-NEXT:    store %struct.Foo* [[THIS:%.*]], %struct.Foo** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[THIS1:%.*]] = load %struct.Foo*, %struct.Foo** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[X:%.*]] = getelementptr inbounds [[STRUCT_FOO:%.*]], %struct.Foo* [[THIS1]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP0:%.*]] = load i8, i8* [[X]], align 1
+// CHECK-NEXT:    ret i8 [[TMP0]]
+//
+// CHECK-LABEL: @_ZN3FooIcE3setEc(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca %struct.Foo*, align 8
+// CHECK-NEXT:    [[_X_ADDR:%.*]] = alloca i8, align 1
+// CHECK-NEXT:    store %struct.Foo* [[THIS:%.*]], %struct.Foo** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    store i8 [[_X:%.*]], i8* [[_X_ADDR]], align 1
+// CHECK-NEXT:    [[THIS1:%.*]] = load %struct.Foo*, %struct.Foo** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load i8, i8* [[_X_ADDR]], align 1
+// CHECK-NEXT:    [[X:%.*]] = getelementptr inbounds [[STRUCT_FOO:%.*]], %struct.Foo* [[THIS1]], i32 0, i32 0
+// CHECK-NEXT:    store i8 [[TMP0]], i8* [[X]], align 1
+// CHECK-NEXT:    ret void
+//
+template struct Foo<char>;
+
+// CHECK-LABEL: @_ZN3FooIsEC1Es(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca %struct.Foo.0*, align 8
+// CHECK-NEXT:    [[X_ADDR:%.*]] = alloca i16, align 2
+// CHECK-NEXT:    store %struct.Foo.0* [[THIS:%.*]], %struct.Foo.0** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    store i16 [[X:%.*]], i16* [[X_ADDR]], align 2
+// CHECK-NEXT:    [[THIS1:%.*]] = load %struct.Foo.0*, %struct.Foo.0** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load i16, i16* [[X_ADDR]], align 2
+// CHECK-NEXT:    call void @_ZN3FooIsEC2Es(%struct.Foo.0* nonnull align 2 dereferenceable(2) [[THIS1]], i16 signext [[TMP0]])
+// CHECK-NEXT:    ret void
+//
+// CHECK-LABEL: @_ZN3FooIsED1Ev(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca %struct.Foo.0*, align 8
+// CHECK-NEXT:    store %struct.Foo.0* [[THIS:%.*]], %struct.Foo.0** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[THIS1:%.*]] = load %struct.Foo.0*, %struct.Foo.0** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    call void @_ZN3FooIsED2Ev(%struct.Foo.0* nonnull align 2 dereferenceable(2) [[THIS1]]) #[[ATTR2]]
+// CHECK-NEXT:    ret void
+//
+// CHECK-LABEL: @_ZN3FooIsE3getEv(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca %struct.Foo.0*, align 8
+// CHECK-NEXT:    store %struct.Foo.0* [[THIS:%.*]], %struct.Foo.0** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[THIS1:%.*]] = load %struct.Foo.0*, %struct.Foo.0** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[X:%.*]] = getelementptr inbounds [[STRUCT_FOO_0:%.*]], %struct.Foo.0* [[THIS1]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP0:%.*]] = load i16, i16* [[X]], align 2
+// CHECK-NEXT:    ret i16 [[TMP0]]
+//
+// CHECK-LABEL: @_ZN3FooIsE3setEs(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca %struct.Foo.0*, align 8
+// CHECK-NEXT:    [[_X_ADDR:%.*]] = alloca i16, align 2
+// CHECK-NEXT:    store %struct.Foo.0* [[THIS:%.*]], %struct.Foo.0** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    store i16 [[_X:%.*]], i16* [[_X_ADDR]], align 2
+// CHECK-NEXT:    [[THIS1:%.*]] = load %struct.Foo.0*, %struct.Foo.0** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load i16, i16* [[_X_ADDR]], align 2
+// CHECK-NEXT:    [[X:%.*]] = getelementptr inbounds [[STRUCT_FOO_0:%.*]], %struct.Foo.0* [[THIS1]], i32 0, i32 0
+// CHECK-NEXT:    store i16 [[TMP0]], i16* [[X]], align 2
+// CHECK-NEXT:    ret void
+//
+template struct Foo<short>;
+
+// This should not generate lines for the implicit specialization of Foo, but
+// should generate lines for the explicit specialization of Bar.
+
+// CHECK-LABEL: @_ZN3BarIiEC1Ei(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca %struct.Bar*, align 8
+// CHECK-NEXT:    [[X_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store %struct.Bar* [[THIS:%.*]], %struct.Bar** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    store i32 [[X:%.*]], i32* [[X_ADDR]], align 4
+// CHECK-NEXT:    [[THIS1:%.*]] = load %struct.Bar*, %struct.Bar** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, i32* [[X_ADDR]], align 4
+// CHECK-NEXT:    call void @_ZN3BarIiEC2Ei(%struct.Bar* nonnull align 4 dereferenceable(4) [[THIS1]], i32 [[TMP0]])
+// CHECK-NEXT:    ret void
+//
+// CHECK-LABEL: @_ZN3BarIiED1Ev(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca %struct.Bar*, align 8
+// CHECK-NEXT:    store %struct.Bar* [[THIS:%.*]], %struct.Bar** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[THIS1:%.*]] = load %struct.Bar*, %struct.Bar** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    call void @_ZN3BarIiED2Ev(%struct.Bar* nonnull align 4 dereferenceable(4) [[THIS1]]) #[[ATTR2]]
+// CHECK-NEXT:    ret void
+//
+// CHECK-LABEL: @_ZN3BarIiE3getEv(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca %struct.Bar*, align 8
+// CHECK-NEXT:    store %struct.Bar* [[THIS:%.*]], %struct.Bar** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[THIS1:%.*]] = load %struct.Bar*, %struct.Bar** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[FOO:%.*]] = getelementptr inbounds [[STRUCT_BAR:%.*]], %struct.Bar* [[THIS1]], i32 0, i32 0
+// CHECK-NEXT:    [[CALL:%.*]] = call i32 @_ZN3FooIiE3getEv(%struct.Foo.1* nonnull align 4 dereferenceable(4) [[FOO]])
+// CHECK-NEXT:    ret i32 [[CALL]]
+//
+// CHECK-LABEL: @_ZN3BarIiE3setEi(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca %struct.Bar*, align 8
+// CHECK-NEXT:    [[_X_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store %struct.Bar* [[THIS:%.*]], %struct.Bar** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    store i32 [[_X:%.*]], i32* [[_X_ADDR]], align 4
+// CHECK-NEXT:    [[THIS1:%.*]] = load %struct.Bar*, %struct.Bar** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[FOO:%.*]] = getelementptr inbounds [[STRUCT_BAR:%.*]], %struct.Bar* [[THIS1]], i32 0, i32 0
+// CHECK-NEXT:    [[TMP0:%.*]] = load i32, i32* [[_X_ADDR]], align 4
+// CHECK-NEXT:    call void @_ZN3FooIiE3setEi(%struct.Foo.1* nonnull align 4 dereferenceable(4) [[FOO]], i32 [[TMP0]])
+// CHECK-NEXT:    ret void
+//
+template struct Bar<int>;
+
+// This should not generate lines for the implicit specialization of Foo, but
+// should generate lines for the explicit specialization of Baz.
+
+// CHECK-LABEL: @_ZN3BazIlEC1El(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca %struct.Baz*, align 8
+// CHECK-NEXT:    [[X_ADDR:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    store %struct.Baz* [[THIS:%.*]], %struct.Baz** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    store i64 [[X:%.*]], i64* [[X_ADDR]], align 8
+// CHECK-NEXT:    [[THIS1:%.*]] = load %struct.Baz*, %struct.Baz** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load i64, i64* [[X_ADDR]], align 8
+// CHECK-NEXT:    call void @_ZN3BazIlEC2El(%struct.Baz* nonnull align 8 dereferenceable(8) [[THIS1]], i64 [[TMP0]])
+// CHECK-NEXT:    ret void
+//
+// CHECK-LABEL: @_ZN3BazIlED1Ev(
+// CHECK-NEXT:  entry:
+// CHECK-NEXT:    [[THIS_ADDR:%.*]] = alloca %struct.Baz*, align 8
+// CHECK-NEXT:    store %struct.Baz* [[THIS:%.*]], %struct.Baz** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    [[THIS1:%.*]] = load %struct.Baz*, %struct.Baz** [[THIS_ADDR]], align 8
+// CHECK-NEXT:    call void @_ZN3BazIlED2Ev(%struct.Baz* nonnull align 8 dereferenceable(8) [[THIS1]]) #[[ATTR2]]
+// CHECK-NEXT:    ret void
+//
+template struct Baz<long>;
Index: clang/test/utils/update_cc_test_checks/Inputs/explicit-template-instantiation.cpp
===================================================================
--- /dev/null
+++ clang/test/utils/update_cc_test_checks/Inputs/explicit-template-instantiation.cpp
@@ -0,0 +1,50 @@
+// RUN: %clang_cc1 -triple=x86_64-unknown-linux-gnu -emit-llvm -o - %s | FileCheck %s
+
+template <typename T>
+struct Foo {
+private:
+  T x;
+
+public:
+  Foo(T x) : x(x) {}
+  ~Foo() {}
+
+  T get() { return x; }
+  void set(T _x) { x = _x; }
+};
+
+template <typename T>
+struct Bar {
+private:
+  struct Foo<T> foo;
+
+public:
+  Bar(T x) : foo(x) {}
+  ~Bar() {}
+
+  T get() { return foo.get(); }
+  void set(T _x) { foo.set(_x); }
+};
+
+template <typename T>
+struct Baz : Foo<T> {
+public:
+  Baz(T x) : Foo<T>(x) {}
+  ~Baz() {}
+};
+
+// These two specializations should generate lines for all of Foo's methods.
+
+template struct Foo<char>;
+
+template struct Foo<short>;
+
+// This should not generate lines for the implicit specialization of Foo, but
+// should generate lines for the explicit specialization of Bar.
+
+template struct Bar<int>;
+
+// This should not generate lines for the implicit specialization of Foo, but
+// should generate lines for the explicit specialization of Baz.
+
+template struct Baz<long>;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to