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 #530 from kleintom/clang_qualname
Browse files Browse the repository at this point in the history
Canonicalize clang function decls before pulling parameter names.
  • Loading branch information
kleintom committed Apr 29, 2016
2 parents 066907f + 42849f6 commit 5baa850
Show file tree
Hide file tree
Showing 7 changed files with 166 additions and 6 deletions.
24 changes: 18 additions & 6 deletions dxr/plugins/clang/dxr-index.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ class IndexConsumer : public ASTConsumer,
LangOptions &features;
DiagnosticConsumer *inner;
static std::string tmpdir; // Place to save all the csv files to
PrintingPolicy printPolicy;

const FileInfoPtr &getFileInfo(const std::string &filename) {
std::map<std::string, FileInfoPtr>::iterator it;
Expand Down Expand Up @@ -169,11 +170,19 @@ class IndexConsumer : public ASTConsumer,
}
public:
IndexConsumer(CompilerInstance &ci)
: ci(ci), sm(ci.getSourceManager()), features(ci.getLangOpts()) {
: ci(ci), sm(ci.getSourceManager()), features(ci.getLangOpts()),
printPolicy(features) {

inner = ci.getDiagnostics().takeClient();
ci.getDiagnostics().setClient(this, false);
ci.getPreprocessor().addPPCallbacks(new PreprocThunk(this));
// We have a bool type, so print "bool" instead of "_Bool" when we print
// types.
printPolicy.Bool = true;
// Print just 'mytype' instead of 'class mytype' or 'enum mytype' etc.;
// the tag doesn't help us and probably makes it harder to craft
// handwritten queries.
printPolicy.SuppressTagKeyword = true;
}

#if CLANG_AT_LEAST(3, 3)
Expand Down Expand Up @@ -263,13 +272,16 @@ class IndexConsumer : public ASTConsumer,
// 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();
for (unsigned i = 0; i < num_params; ++i) {
if (i)
ret += ", ";
ret += fd->getParamDecl(i)->getType().getAsString();
ret += fd->getParamDecl(i)->getType().getAsString(printPolicy);
}

if (fpt->isVariadic()) {
Expand Down Expand Up @@ -491,15 +503,15 @@ class IndexConsumer : public ASTConsumer,
std::string functionQualName = getQualifiedName(*d);
recordValue("qualname", functionQualName);
#if CLANG_AT_LEAST(3, 5)
recordValue("type", d->getCallResultType().getAsString());
recordValue("type", d->getCallResultType().getAsString(printPolicy));
#else
recordValue("type", d->getResultType().getAsString());
recordValue("type", d->getResultType().getAsString(printPolicy));
#endif
std::string args("(");
for (FunctionDecl::param_iterator it = d->param_begin();
it != d->param_end(); it++) {
args += ", ";
args += (*it)->getType().getAsString();
args += (*it)->getType().getAsString(printPolicy);
}
if (d->getNumParams() > 0)
args.erase(1, 2);
Expand Down Expand Up @@ -615,7 +627,7 @@ class IndexConsumer : public ASTConsumer,
recordValue("qualname", getQualifiedName(*d));
recordValue("loc", locationToString(location));
recordValue("locend", locationToString(afterToken(location)));
recordValue("type", d->getType().getAsString(), true);
recordValue("type", d->getType().getAsString(printPolicy), true);
const std::string &value = getValueForValueDecl(d);
if (!value.empty())
recordValue("value", value, true);
Expand Down
11 changes: 11 additions & 0 deletions dxr/plugins/clang/tests/test_qualnames/code/foo.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#include "main.h"

using namespace bear;
using namespace sonic;

// "Find callers" on these functions used to return 0 results.
void f_class(t_class ) {}
void f_enum_class(const t_enum_class * const ) {}
void f_template(t_template<fur> ) {}
void sonic::f_typedef(streamOfInts ) {} // (This one has always worked.)
void f_bool(bool ) {}
13 changes: 13 additions & 0 deletions dxr/plugins/clang/tests/test_qualnames/code/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#include "main.h"

using namespace bear;
using namespace sonic;

int main() {
f_class(t_class());
t_enum_class oversized_feature = t_enum_class::paw;
f_enum_class(&oversized_feature);
f_template(t_template<fur>());
f_typedef(streamOfInts());
f_bool(true);
}
23 changes: 23 additions & 0 deletions dxr/plugins/clang/tests/test_qualnames/code/main.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// The namespace and translation unit setups of this tree provide some of the
// conditions under which clang used to give different parameter type names
// depending on where in the source tree the parameter was requested.

namespace bear {

class t_class {};
enum class t_enum_class { paw };

} // namespace bear

template<class T> class t_template { };
typedef t_template<int> streamOfInts;

// "Find callers" on these function decls used to return 0 results.
void f_class(bear::t_class );
void f_enum_class(const bear::t_enum_class * const );
namespace sonic {
void f_typedef(streamOfInts ); // (This one has always worked.)
class fur {};
} // namespace sonic
void f_template(t_template<sonic::fur> );
void f_bool(bool );
12 changes: 12 additions & 0 deletions dxr/plugins/clang/tests/test_qualnames/code/makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
CXXFLAGS := -std=c++11 # for class enums

all: code

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

%.o: %.cpp
$(CXX) $(CXXFLAGS) -c -o $@ $^

clean:
rm -f code *.o
9 changes: 9 additions & 0 deletions dxr/plugins/clang/tests/test_qualnames/dxr.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[DXR]
enabled_plugins = pygmentize clang
es_index = dxr_test_{format}_{tree}_{unique}
es_alias = dxr_test_{format}_{tree}
es_catalog_index = dxr_test_catalog

[code]
source_folder = code
build_command = make clean; make -j $jobs
80 changes: 80 additions & 0 deletions dxr/plugins/clang/tests/test_qualnames/test_qualnames.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
"""Test that the plugin returns consistent qualnames for function parameters
referenced in varying namespace environments.
"""
from urllib import quote_plus

from dxr.testing import DxrInstanceTestCase, menu_on

class ConsistencyTests(DxrInstanceTestCase):
def _qualname_check(self, name, qualname, call_line, 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:'
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)))
# Check that the header ref uses the right qualname:
menu_on(self.source_page('main.h'),
name,
{'html': 'Find callers',
'href': callers_query})
# Check that the implementation ref uses the right qualname:
menu_on(self.source_page('foo.cpp'),
name,
{'html': 'Find callers',
'href': callers_query})
# Check that a call site uses the right qualname:
menu_on(self.source_page('main.cpp'),
name,
{'html': 'Find callers',
'href': callers_query})
# Check that we actually find the caller:
self.found_line_eq('+callers:"{0}"'.format(qualname),
call_line,
line)

def test_class_qualname_consistency(self):
"""Test consistency for a function with a class parameter."""
self._qualname_check('f_class',
'f_class(bear::t_class)',
'<b>f_class(t_class())</b>;',
7)

def test_enum_class_qualname_consistency(self):
"""Test consistency for a function with an enum class parameter."""
self._qualname_check('f_enum_class',
'f_enum_class(const bear::t_enum_class *const)',
'<b>f_enum_class(&amp;oversized_feature)</b>;',
9)

def test_template_qualname_consistency(self):
"""Test consistency for a function with a namespaced templated parameter."""
self._qualname_check('f_template',
'f_template(t_template<sonic::fur>)',
'<b>f_template(t_template&lt;fur&gt;())</b>;',
10)

def test_typedef_namespace_consisteny(self):
"""Test consistency for a namespaced function with a typedefed parameter."""
self._qualname_check('f_typedef',
'sonic::f_typedef(streamOfInts)',
'<b>f_typedef(streamOfInts())</b>;',
11)

def test_bool_qualname_consistency(self):
"""Test that we're using 'bool' instead of '_Bool' for all of our
bool parameter names."""
self._qualname_check('f_bool',
'f_bool(bool)',
'<b>f_bool(true)</b>;',
12)

0 comments on commit 5baa850

Please sign in to comment.