[clang] 0e79a94 - [Utils] Support class template specializations in update_cc_test_checks

Jessica Clarke via cfe-commits cfe-commits at lists.llvm.org
Wed Jul 28 08:03:57 PDT 2021


Author: Jessica Clarke
Date: 2021-07-28T16:03:41+01:00
New Revision: 0e79a94836d7127a87c36fdca43ffaf6a17d8964

URL: https://github.com/llvm/llvm-project/commit/0e79a94836d7127a87c36fdca43ffaf6a17d8964
DIFF: https://github.com/llvm/llvm-project/commit/0e79a94836d7127a87c36fdca43ffaf6a17d8964.diff

LOG: [Utils] Support class template specializations in update_cc_test_checks

ClassTemplateSpecializationDecl not within a ClassTemplateDecl
represents an explicit instatiation of a template and so should be
handled as if it were a normal CXXRecordDecl. Unfortunately, having an
equivalent for FunctionTemplateDecl remains a TODO in ASTDumper's
VisitFunctionTemplateDecl, with all the explicit instantiations just
being emitted inside the FunctionTemplateDecl along with all the other
specializations, meaning we can't easily support explicit function
instantiations in update_cc_test_checks.

Reviewed By: arichardson

Differential Revision: https://reviews.llvm.org/D106243

Added: 
    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

Modified: 
    llvm/utils/update_cc_test_checks.py

Removed: 
    


################################################################################
diff  --git a/clang/test/utils/update_cc_test_checks/Inputs/explicit-template-instantiation.cpp b/clang/test/utils/update_cc_test_checks/Inputs/explicit-template-instantiation.cpp
new file mode 100644
index 0000000000000..859e0c73ec119
--- /dev/null
+++ b/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>;

diff  --git a/clang/test/utils/update_cc_test_checks/Inputs/explicit-template-instantiation.cpp.expected b/clang/test/utils/update_cc_test_checks/Inputs/explicit-template-instantiation.cpp.expected
new file mode 100644
index 0000000000000..fc132c0f7fa79
--- /dev/null
+++ b/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>;

diff  --git a/clang/test/utils/update_cc_test_checks/explicit-template-instantiation.test b/clang/test/utils/update_cc_test_checks/explicit-template-instantiation.test
new file mode 100644
index 0000000000000..bcecffac19cea
--- /dev/null
+++ b/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

diff  --git a/llvm/utils/update_cc_test_checks.py b/llvm/utils/update_cc_test_checks.py
index 74c601faba10a..e3a9354591f1b 100755
--- a/llvm/utils/update_cc_test_checks.py
+++ b/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 @@ def get_line2spell_and_mangled(args, clang_args):
 
   # 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 @@ def parse_clang_ast_json(node):
           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 @@ def main():
                              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 @@ def main():
 
       # 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 @@ def check_generator(my_output_lines, prefixes, func):
         # 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()


        


More information about the cfe-commits mailing list