Skip to content
This repository has been archived by the owner on Oct 13, 2021. It is now read-only.

Commit

Permalink
Merge pull request #540 from kleintom/template_qualname
Browse files Browse the repository at this point in the history
Use the original template pattern for function template qualnames.
  • Loading branch information
kleintom committed May 17, 2016
2 parents 2d77439 + 4d6cf9f commit 8f01075
Show file tree
Hide file tree
Showing 8 changed files with 194 additions and 21 deletions.
31 changes: 26 additions & 5 deletions dxr/plugins/clang/dxr-index.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ class IndexConsumer : public ASTConsumer,
// which would otherwise be ambiguous.
std::string getQualifiedName(const NamedDecl &d) {
std::string ret;
const FunctionDecl *fd = nullptr;
const DeclContext *ctx = d.getDeclContext();
if (ctx->isFunctionOrMethod() && isa<NamedDecl>(ctx)) {
// This is a local variable.
Expand All @@ -268,20 +269,40 @@ class IndexConsumer : public ASTConsumer,
ret = getQualifiedName(*cast<NamedDecl>(ctx)) + "::" + d.getNameAsString();
}
else {
ret = d.getQualifiedNameAsString();
if ((fd = dyn_cast<FunctionDecl>(&d))) { // A function
if (fd->isTemplateInstantiation()) {
// Use the original template pattern, not the substituted concrete
// types, for the qualname:
fd = fd->getTemplateInstantiationPattern();
#if 0 // Activate this section to give most full (but no partial!) function
// template specializations the same qualname as the base template.
} else if (FunctionTemplateSpecializationInfo *ftsi =
fd->getTemplateSpecializationInfo()) {
// This gives a function template and its full specializations the
// same qualname. Unfortunately I don't know how to do the same for
// *partial* class template specializations, so for now the original
// template and each partial specialization gets its own qualname.
fd = ftsi->getTemplate()->getTemplatedDecl();
#endif
}
// Canonicalize the decl - otherwise you can get different qualnames
// depending on the location of your decl (which would be bad).
fd = fd->getCanonicalDecl();
ret = fd->getQualifiedNameAsString();
}
else {
ret = d.getQualifiedNameAsString();
}
}

if (const FunctionDecl *fd = dyn_cast<FunctionDecl>(&d)) {
if (fd) {
// This is a function. getQualifiedNameAsString will return a string
// like "ANamespace::AFunction". To this we append the list of parameters
// so that we can distinguish correctly between
// void ANamespace::AFunction(int);
// and
// void ANamespace::AFunction(float);
ret += "(";
// Use the canonical decl - otherwise you can get different param strings
// depending on the location of your decl (which is bad).
fd = fd->getCanonicalDecl();
const FunctionType *ft = fd->getType()->castAs<FunctionType>();
if (const FunctionProtoType *fpt = dyn_cast<FunctionProtoType>(ft)) {
unsigned num_params = fd->getNumParams();
Expand Down
2 changes: 1 addition & 1 deletion dxr/plugins/clang/tests/test_qualnames/code/foo.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#include "main.h"
#include "foo.h"

using namespace bear;
using namespace sonic;
Expand Down
2 changes: 1 addition & 1 deletion dxr/plugins/clang/tests/test_qualnames/code/main.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#include "main.h"
#include "foo.h"

using namespace bear;
using namespace sonic;
Expand Down
2 changes: 1 addition & 1 deletion dxr/plugins/clang/tests/test_qualnames/code/makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ CXXFLAGS := -std=c++11 # for class enums

all: code

code: main.o foo.o
code: main.o foo.o template_foo.o
$(CXX) -o $@ $^

%.o: %.cpp
Expand Down
26 changes: 26 additions & 0 deletions dxr/plugins/clang/tests/test_qualnames/code/template_foo.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#include "template_foo.h"

//// Template instantiation and specialization:
void doit() {
char aa = 'a';

fud(Baz<int>());
fud(aa);
fud(3);

Baz<int> bb;
guz(bb);

Gub gg;
gg.bug(2);
gg.bug(aa);

Gleb<char> gl;
gl.lerb(true);
gl.lerb(3);

Derb<int> cc;
cc.flarb(3);
Derb<char *> dd;
dd.flarb(&aa);
}
40 changes: 40 additions & 0 deletions dxr/plugins/clang/tests/test_qualnames/code/template_foo.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//// Qualnames involving template instantiation and specialization:
template<class T>
void fud(T ) {}
// Full specialization:
template<>
inline void fud(int ) {}

template<typename T>
class Baz {};

template<typename T>
void guz(Baz<T> ) {}

struct Gub {
template<typename T>
void bug(T ) {}
};
// Full specialization:
template<> inline void Gub::bug(char ) {}

template<typename T>
struct Gleb {
template<typename U>
void lerb(U ) {}
};
// Full specialization (seems like you can't partially specialize a templated
// method of a templated class):
template<>
template<>
inline void Gleb<char>::lerb(int ) {}

template<typename T>
struct Derb {
void flarb(T ) {}
};
// Partial specialization:
template<typename T>
struct Derb<T*> {
void flarb(T * ) {}
};
112 changes: 99 additions & 13 deletions dxr/plugins/clang/tests/test_qualnames/test_qualnames.py
Original file line number Diff line number Diff line change
@@ -1,46 +1,49 @@
"""Test that the plugin returns consistent qualnames for function parameters
referenced in varying namespace environments.
referenced in varying namespace and template environments."""

"""
from urllib import quote_plus

from dxr.testing import DxrInstanceTestCase, menu_on

def callers_query(qualname):
if ' ' in qualname:
return '/code/search?q=%2Bcallers%3A%22{0}%22'.format(quote_plus(qualname))
else:
return '/code/search?q=%2Bcallers%3A{0}'.format(quote_plus(qualname))


class ConsistencyTests(DxrInstanceTestCase):
def _qualname_check(self, name, qualname, call_line, line):
def _qualname_check(self, name, qualname, content, line):
"""Test function qualname consistency across function refs at header,
implementation, and call sites.
:arg name: The function's name (without namespace)
:arg qualname: The qualname of the function as fed to 'callers:'
:arg call_line: The expected line from the result of the 'callers:'
:arg content: The expected line of text from the result of the 'callers:'
search
:arg line: The expected line number of the single result from the
'callers:' search
"""
callers_query = (
'/code/search?q=%2Bcallers%3A%22{0}%22'.format(quote_plus(qualname)) if
' ' in qualname else
'/code/search?q=%2Bcallers%3A{0}'.format(quote_plus(qualname)))
query = callers_query(qualname)
# Check that the header ref uses the right qualname:
menu_on(self.source_page('main.h'),
menu_on(self.source_page('foo.h'),
name,
{'html': 'Find callers',
'href': callers_query})
'href': query})
# Check that the implementation ref uses the right qualname:
menu_on(self.source_page('foo.cpp'),
name,
{'html': 'Find callers',
'href': callers_query})
'href': query})
# Check that a call site uses the right qualname:
menu_on(self.source_page('main.cpp'),
name,
{'html': 'Find callers',
'href': callers_query})
'href': query})
# Check that we actually find the caller:
self.found_line_eq('+callers:"{0}"'.format(qualname),
call_line,
content,
line)

def test_class_qualname_consistency(self):
Expand Down Expand Up @@ -78,3 +81,86 @@ def test_bool_qualname_consistency(self):
'f_bool(bool)',
'<b>f_bool(true)</b>;',
12)

# Template instantiation and specialization qualname checks:

def _qualname_instances(self, name, qualname,
header_instances, impl_instances):
"""Test that the 'Find callers' menu item at each listed header and
implementation instance of name has the correct '+callers:' query.
"""
query = callers_query(qualname)
header_page = self.source_page('template_foo.h')
impl_page = self.source_page('template_foo.cpp')
for header_instance in header_instances:
# Check that the header ref uses the right qualname:
menu_on(header_page,
name,
{'html': 'Find callers',
'href': query},
text_instance=header_instance)
for impl_instance in impl_instances:
# Check that the implementation ref uses the right qualname:
menu_on(impl_page,
name,
{'html': 'Find callers',
'href': query},
text_instance=impl_instance)

def _callers_check(self, qualname, lines):
self.found_lines_eq('+callers:"{0}"'.format(qualname), lines)

def test_template_function(self):
"""Test consistency for a templated function and a full specialization."""
base_qualname = 'fud(T)'
self._qualname_instances('fud', base_qualname, [1], [1, 2])
self._callers_check(base_qualname, [('<b>fud(Baz&lt;int&gt;())</b>;', 7),
('<b>fud(aa)</b>;', 8)])

# The full specialization gets its own qualname (for now):
spec_qualname = 'fud(int)'
self._qualname_instances('fud', spec_qualname, [2], [3])
self._callers_check(spec_qualname, [('<b>fud(3)</b>;', 9)])

def test_templated_parameter(self):
"""Test consistency for a templated function where a function parameter
is itself templated."""
qualname = 'guz(Baz<T>)'
self._qualname_instances('guz', qualname, [1], [1])
self._callers_check(qualname, [('<b>guz(bb)</b>;', 12)])

def test_templated_method(self):
"""Test consistency for a templated function of a class and a full
specialization."""
base_qualname = 'Gub::bug(T)'
self._qualname_instances('bug', base_qualname, [1], [1])
self._callers_check(base_qualname, [('<b>gg.bug(2)</b>;', 15)])

# The full specialization gets its own qualname (for now):
spec_qualname = 'Gub::bug(char)'
self._qualname_instances('bug', spec_qualname, [2], [2])
self._callers_check(spec_qualname, [('<b>gg.bug(aa)</b>;', 16)])

def test_templated_method_of_templated_class(self):
"""Test consistency for a templated method of a templated class and a
full specialization."""
base_qualname = 'Gleb::lerb(U)'
self._qualname_instances('lerb', base_qualname, [1], [1])
self._callers_check(base_qualname, [('<b>gl.lerb(true)</b>;', 19)])

# The full specialization gets its own qualname (for now):
spec_qualname = 'Gleb<char>::lerb(int)'
self._qualname_instances('lerb', spec_qualname, [2], [2])
self._callers_check(spec_qualname, [('<b>gl.lerb(3)</b>;', 20)])

def test_partial_specialization(self):
"""Test consistency for a templated class and a partial specialization."""
base_qualname = 'Derb::flarb(T)'
self._qualname_instances('flarb', base_qualname, [1], [1])
self._callers_check(base_qualname, [('<b>cc.flarb(3)</b>;', 23)])

# The partial specialization gets its own qualname (for now):
spec_qualname = 'Derb<type-parameter-0-0 *>::flarb(T *)' # (ouch)
self._qualname_instances('flarb', spec_qualname, [2], [2])
self._callers_check(spec_qualname, [('<b>dd.flarb(&amp;aa)</b>;', 25)])

0 comments on commit 8f01075

Please sign in to comment.