forked from qt-creator/qt-creator
C++: Improve accuracy in findSpecialization
* If a template type is specialized as a pointer, accept only pointers (of any
type)
* Same for references and arrays
* Only if the specialized type is not part of the template, match it
against the input.
Fixes resolving of partial specialization with pointers.
Use-cases:
// 1
struct b {};
struct a : b {};
template<class X, class Y> struct s { float f; };
template<class X> struct s<X, b*> { int i; };
template<class X> struct s<X, a*> { char j; };
void f()
{
s<int, a*> var;
var.j; // j not highlighted
}
// 2
template <typename T> struct Temp { T variable; };
template <typename T> struct Temp<T &> { T reference; };
void func()
{
Temp<int&> templ;
templ.reference; // reference not highlighted
}
// 3
class false_type {};
class true_type {};
template<class T1, class T2> class and_type { false_type f; };
template<> class and_type<true_type, true_type> { true_type t; };
void func2()
{
and_type<true_type, false_type> a;
a.f; // f not highlighted
}
Task-number: QTCREATORBUG-14036
Change-Id: Idee5e3f41d15c0772318d3837cbcd442cb80293a
Reviewed-by: Nikolai Kosjar <nikolai.kosjar@theqtcompany.com>
This commit is contained in:
committed by
Orgad Shaneh
parent
b61413fe0c
commit
997ab425ce
@@ -1144,9 +1144,29 @@ LookupScope *LookupScopePrivate::lookupType_helper(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static LookupScopePrivate *findSpecializationWithMatchingTemplateArgument(
|
||||
const Name *argumentName, LookupScopePrivate *reference)
|
||||
static const NamedType *dereference(const FullySpecifiedType &type)
|
||||
{
|
||||
FullySpecifiedType ty = type;
|
||||
forever {
|
||||
if (PointerType *pointer = ty->asPointerType())
|
||||
ty = pointer->elementType();
|
||||
else if (ReferenceType *reference = ty->asReferenceType())
|
||||
ty = reference->elementType();
|
||||
else if (ArrayType *array = ty->asArrayType())
|
||||
ty = array->elementType();
|
||||
else if (const NamedType *namedType = ty->asNamedType())
|
||||
return namedType;
|
||||
else
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool findTemplateArgument(const NamedType *namedType, LookupScopePrivate *reference)
|
||||
{
|
||||
if (!namedType)
|
||||
return false;
|
||||
const Name *argumentName = namedType->name();
|
||||
foreach (Symbol *s, reference->_symbols) {
|
||||
if (Class *clazz = s->asClass()) {
|
||||
if (Template *templateSpecialization = clazz->enclosingTemplate()) {
|
||||
@@ -1157,14 +1177,28 @@ static LookupScopePrivate *findSpecializationWithMatchingTemplateArgument(
|
||||
= templateSpecialization->templateParameterAt(i)->asTypenameArgument()) {
|
||||
if (const Name *name = tParam->name()) {
|
||||
if (compareName(name, argumentName))
|
||||
return reference;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool matchTypes(const FullySpecifiedType &instantiation,
|
||||
const FullySpecifiedType &specialization)
|
||||
{
|
||||
if (specialization.match(instantiation))
|
||||
return true;
|
||||
if (const NamedType *specName = specialization->asNamedType()) {
|
||||
if (const NamedType *initName = instantiation->asNamedType()) {
|
||||
if (specName->name()->identifier()->match(initName->name()->identifier()))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
LookupScopePrivate *LookupScopePrivate::findSpecialization(
|
||||
@@ -1172,7 +1206,6 @@ LookupScopePrivate *LookupScopePrivate::findSpecialization(
|
||||
const TemplateNameIdTable &specializations,
|
||||
LookupScopePrivate *origin)
|
||||
{
|
||||
// we go through all specialization and try to find that one with template argument as pointer
|
||||
for (TemplateNameIdTable::const_iterator cit = specializations.begin();
|
||||
cit != specializations.end(); ++cit) {
|
||||
const TemplateNameId *specializationNameId = cit->first;
|
||||
@@ -1183,45 +1216,31 @@ LookupScopePrivate *LookupScopePrivate::findSpecialization(
|
||||
// and initialization(in future it should be more clever)
|
||||
if (specializationTemplateArgumentCount != initializationTemplateArgumentCount)
|
||||
continue;
|
||||
for (unsigned i = 0; i < initializationTemplateArgumentCount; ++i) {
|
||||
bool match = true;
|
||||
for (unsigned i = 0; i < initializationTemplateArgumentCount && match; ++i) {
|
||||
const FullySpecifiedType &specializationTemplateArgument
|
||||
= specializationNameId->templateArgumentAt(i);
|
||||
FullySpecifiedType initializationTemplateArgument = templId->templateArgumentAt(i);
|
||||
TypeResolver typeResolver(*_factory);
|
||||
Scope *scope = 0;
|
||||
typeResolver.resolve(&initializationTemplateArgument, &scope, origin ? origin->q : 0);
|
||||
PointerType *specPointer = specializationTemplateArgument.type()->asPointerType();
|
||||
// specialization and initialization argument have to be a pointer
|
||||
// additionally type of pointer argument of specialization has to be namedType
|
||||
if (specPointer && initializationTemplateArgument.type()->isPointerType()
|
||||
&& specPointer->elementType().type()->isNamedType()) {
|
||||
return cit->second;
|
||||
}
|
||||
|
||||
ArrayType *specArray = specializationTemplateArgument.type()->asArrayType();
|
||||
if (specArray && initializationTemplateArgument.type()->isArrayType()) {
|
||||
if (const NamedType *argumentNamedType
|
||||
= specArray->elementType().type()->asNamedType()) {
|
||||
if (const Name *argumentName = argumentNamedType->name()) {
|
||||
if (LookupScopePrivate *reference
|
||||
= findSpecializationWithMatchingTemplateArgument(
|
||||
argumentName, cit->second)) {
|
||||
return reference;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (specializationTemplateArgument == initializationTemplateArgument)
|
||||
return cit->second;
|
||||
|
||||
if (const NamedType *specName = specializationTemplateArgument->asNamedType()) {
|
||||
if (const NamedType *initName = initializationTemplateArgument->asNamedType()) {
|
||||
if (specName->name()->identifier() == initName->name()->identifier())
|
||||
return cit->second;
|
||||
}
|
||||
if (findTemplateArgument(dereference(specializationTemplateArgument), cit->second)) {
|
||||
if (specializationTemplateArgument->isPointerType())
|
||||
match = initializationTemplateArgument->isPointerType();
|
||||
else if (specializationTemplateArgument->isReferenceType())
|
||||
match = initializationTemplateArgument->isReferenceType();
|
||||
else if (specializationTemplateArgument->isArrayType())
|
||||
match = initializationTemplateArgument->isArrayType();
|
||||
// Do not try exact match (typename T != class T {};)
|
||||
} else {
|
||||
// Real type specialization
|
||||
match = matchTypes(initializationTemplateArgument, specializationTemplateArgument);
|
||||
}
|
||||
}
|
||||
if (match)
|
||||
return cit->second;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -1306,22 +1306,29 @@ void CppToolsPlugin::test_completion_data()
|
||||
<< QLatin1String("Template1"));
|
||||
|
||||
QTest::newRow("template_specialization_with_pointer") << _(
|
||||
"template <typename T>\n"
|
||||
"struct Template\n"
|
||||
"template <typename T> struct Temp { T variable; };\n"
|
||||
"template <typename T> struct Temp<T *> { T *pointer; };\n"
|
||||
"void func()\n"
|
||||
"{\n"
|
||||
" T variable;\n"
|
||||
"};\n"
|
||||
"template <typename T>\n"
|
||||
"struct Template<T *>\n"
|
||||
"{\n"
|
||||
" T *pointer;\n"
|
||||
"};\n"
|
||||
"Template<int*> templ;\n"
|
||||
"@\n"
|
||||
" Temp<int*> templ;\n"
|
||||
" @\n"
|
||||
"}"
|
||||
) << _("templ.") << (QStringList()
|
||||
<< QLatin1String("Template")
|
||||
<< QLatin1String("Temp")
|
||||
<< QLatin1String("pointer"));
|
||||
|
||||
QTest::newRow("template_specialization_with_reference") << _(
|
||||
"template <typename T> struct Temp { T variable; };\n"
|
||||
"template <typename T> struct Temp<T &> { T reference; };\n"
|
||||
"void func()\n"
|
||||
"{\n"
|
||||
" Temp<int&> templ;\n"
|
||||
" @\n"
|
||||
"}"
|
||||
) << _("templ.") << (QStringList()
|
||||
<< QLatin1String("Temp")
|
||||
<< QLatin1String("reference"));
|
||||
|
||||
QTest::newRow("typedef_using_templates1") << _(
|
||||
"namespace NS1\n"
|
||||
"{\n"
|
||||
@@ -2637,6 +2644,22 @@ void CppToolsPlugin::test_completion_data()
|
||||
<< QLatin1String("i")
|
||||
<< QLatin1String("s"));
|
||||
|
||||
QTest::newRow("partial_specialization_with_pointer") << _(
|
||||
"struct b {};\n"
|
||||
"struct a : b {};\n"
|
||||
"template<class X, class Y> struct s { float f; };\n"
|
||||
"template<class X> struct s<X, b*> { int i; };\n"
|
||||
"template<class X> struct s<X, a*> { char j; };\n"
|
||||
"\n"
|
||||
"void f()\n"
|
||||
"{\n"
|
||||
" s<int, a*> var;\n"
|
||||
" @\n"
|
||||
"}\n"
|
||||
) << _("var.") << (QStringList()
|
||||
<< QLatin1String("j")
|
||||
<< QLatin1String("s"));
|
||||
|
||||
QTest::newRow("partial_specialization_templated_argument") << _(
|
||||
"template<class T> struct t {};\n"
|
||||
"\n"
|
||||
@@ -2652,6 +2675,20 @@ void CppToolsPlugin::test_completion_data()
|
||||
<< QLatin1String("i")
|
||||
<< QLatin1String("s"));
|
||||
|
||||
QTest::newRow("specialization_multiple_arguments") << _(
|
||||
"class false_type {};\n"
|
||||
"class true_type {};\n"
|
||||
"template<class T1, class T2> class and_type { false_type f; };\n"
|
||||
"template<> class and_type<true_type, true_type> { true_type t; };\n"
|
||||
"void func()\n"
|
||||
"{\n"
|
||||
" and_type<true_type, false_type> a;\n"
|
||||
" @;\n"
|
||||
"}\n"
|
||||
) << _("a.") << (QStringList()
|
||||
<< QLatin1String("f")
|
||||
<< QLatin1String("and_type"));
|
||||
|
||||
QTest::newRow("auto_declaration_in_if_condition") << _(
|
||||
"struct Foo { int bar; };\n"
|
||||
"void fun() {\n"
|
||||
|
||||
Reference in New Issue
Block a user