Warning, file /kdevelop/kdev-python/duchain/types/unsuretype.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002     SPDX-FileCopyrightText: 2011 Sven Brauch <svenbrauch@googlemail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "unsuretype.h"
0008 #include "helpers.h"
0009 #include "indexedcontainer.h"
0010 
0011 #include <duchaindebug.h>
0012 
0013 #include <language/duchain/types/typeregister.h>
0014 #include <language/duchain/types/typesystem.h>
0015 #include <language/duchain/types/typealiastype.h>
0016 #include <language/duchain/types/containertypes.h>
0017 #include <language/duchain/types/unsuretype.h>
0018 #include <language/duchain/parsingenvironment.h>
0019 #include <language/duchain/duchainlock.h>
0020 
0021 #include <KLocalizedString>
0022 
0023 namespace Python {
0024 
0025 REGISTER_TYPE(UnsureType);
0026 
0027 UnsureType::UnsureType() : KDevelop::UnsureType(createData<UnsureType>())
0028 {
0029 }
0030 
0031 UnsureType::UnsureType(const UnsureType& rhs)
0032     : KDevelop::UnsureType(copyData<UnsureType>(*rhs.d_func()))
0033 {
0034 
0035 }
0036 
0037 UnsureType::UnsureType(KDevelop::UnsureTypeData& data)
0038     : KDevelop::UnsureType(data)
0039 {
0040 
0041 }
0042 
0043 const QList<AbstractType::Ptr> UnsureType::typesRecursive() const
0044 {
0045     QList<AbstractType::Ptr> results;
0046     FOREACH_FUNCTION ( const IndexedType& type, d_func()->m_types ) {
0047         AbstractType::Ptr current = type.abstractType();
0048         AbstractType::Ptr resolved = Helper::resolveAliasType(current);
0049         if ( resolved->whichType() == AbstractType::TypeUnsure ) {
0050             results.append(resolved.staticCast<UnsureType>()->typesRecursive());
0051         }
0052         else
0053             results.append(current);
0054     }
0055     return results;
0056 }
0057 
0058 QString UnsureType::toString() const
0059 {
0060     QString typeList;
0061     QVector<AbstractType::Ptr> types;
0062     auto is_new_type = [&types](const IndexedType newType) {
0063         foreach ( const auto& type, types ) {
0064             if ( type->indexed() == newType ) {
0065                 return false;
0066             }
0067         }
0068         return true;
0069     };
0070 
0071     foreach ( AbstractType::Ptr type, typesRecursive() ) {
0072         if ( ! type ) {
0073             qCWarning(KDEV_PYTHON_DUCHAIN) << "Invalid type: " << type.data();
0074             continue;
0075         }
0076 
0077         const auto new_type = Helper::resolveAliasType(type);
0078         if ( is_new_type(new_type->indexed()) ) {
0079             types.append(new_type);
0080         }
0081     }
0082 
0083     auto count_and_remove = [&types](std::function<bool(AbstractType::Ptr)> match) -> bool {
0084         auto count = std::count_if(types.begin(), types.end(), match);
0085         if ( count < 3 ) {
0086             // nothing worth collapsing
0087             return false;
0088         }
0089         auto end = std::remove_if(types.begin(), types.end(), match);
0090         types.erase(end, types.end());
0091         return true;
0092     };
0093 
0094     QStringList collapsedTypes;
0095     if ( types.size() > 2 ) {
0096         // try to collapse the list, if possible
0097         using T = const AbstractType::Ptr&;
0098         auto have_callable = count_and_remove([](T t) { return t->whichType() == AbstractType::TypeFunction; });
0099         if ( have_callable ) {
0100             // TODO collapse arguments / return type
0101             collapsedTypes.append(i18nc("some object that can be called, in programming", "<callable>"));
0102         }
0103         auto have_iterable = count_and_remove([](T t) { return t.dynamicCast<IndexedContainer>() || t.dynamicCast<ListType>(); });
0104         if ( have_iterable ) {
0105             // TODO collapse element count / types
0106             collapsedTypes.append(i18nc("a set with some elements", "<iterable>"));
0107         }
0108     }
0109 
0110     int count = 0;
0111     foreach ( const auto& type, types ) {
0112         if ( count )
0113             typeList += ", ";
0114         count += 1;
0115 
0116         typeList += type->toString();
0117     }
0118     foreach ( const auto& collapsed, collapsedTypes ) {
0119         if ( count )
0120             typeList += ", ";
0121         count += 1;
0122 
0123         typeList += collapsed;
0124     }
0125 
0126     if ( count == 0 || count > 7 )
0127         return i18nc("refers to a type (in program code) which is not known", "mixed");
0128     if ( count == 1 )
0129         return typeList;
0130     return i18nc("refers to a type (in program code) which can have multiple values", "unsure (%1)", typeList);
0131 }
0132 
0133 KDevelop::AbstractType* UnsureType::clone() const
0134 {
0135     UnsureType* n = new UnsureType(*this);
0136     return n;
0137 }
0138 
0139 AbstractType::WhichType UnsureType::whichType() const
0140 {
0141     return AbstractType::TypeUnsure;
0142 }
0143 
0144 void UnsureType::addType(const IndexedType& indexed) {
0145     auto type = indexed.abstractType();
0146     auto hinted = type.dynamicCast<HintedType>(); // XXX: do we need a read locker here?
0147     if ( ! hinted ) {
0148         // if we aren't adding a HintedType the default implementation works
0149         KDevelop::UnsureType::addType(indexed);
0150         return;
0151     }
0152     auto& list = d_func_dynamic()->m_typesList();
0153     DUChainReadLocker lock;
0154     if (!hinted->isValid()) { // needs a read lock (as does most of the rest of the function)
0155         // can happen if the user saves the currently open document again before parsing has finished
0156         return;
0157     }
0158     // If there is already a HintedType in the list referring to the underlying type
0159     // we only add it if the context it was created in is the same.
0160     // Additionally, we also remove all HintedType instances that are no longer valid
0161     // to make sure the list doesn't grow infinitely large
0162     const auto newHintedTarget = hinted->type()->indexed();
0163     bool alreadyExists = false;
0164     for ( int j = 0; j < list.size(); j++ ) {
0165         const IndexedType oldIndexed = list.at(j);
0166         if (oldIndexed == indexed) {
0167             alreadyExists = true;
0168         }
0169         const auto& old = oldIndexed.abstractType();
0170         if ( auto oldHinted = old.dynamicCast<HintedType>() ) {
0171             if ( !alreadyExists ) {
0172                 // only do these checks if we haven't already determined that it is a duplicate
0173                 auto oldHintedTarget = oldHinted->type()->indexed();
0174                 if ( oldHintedTarget == newHintedTarget ) {
0175                     if ( hinted->createdBy() == oldHinted->createdBy()) {
0176                         alreadyExists = true;
0177                     }
0178                 }
0179             }
0180             if ( ! oldHinted->isValid() ) {
0181                 // std::remove_if + erase() would be faster than remove() but this list won't have many entries
0182                 // and the memcpy() cost is probably massively offset by the IndexedType -> AbstractType
0183                 // lookup that we would have to duplicated if we had a separate check for duplicates loop
0184                 list.remove(j);
0185                 j--;
0186                 continue;
0187             }
0188         }
0189     }
0190     if ( !alreadyExists ) {
0191         list.append(indexed);
0192     }
0193 // #define CHECK_DUPLICATES
0194 #ifdef CHECK_DUPLICATES
0195     if (list.size() > 1) {
0196         QString types;
0197         bool foundDuplicates = false;
0198         QStringList checkDuplicates;
0199         FOREACH_FUNCTION(const IndexedType& type2, d_func()->m_types) {
0200             auto t = type2.abstractType();
0201             auto str = t->toString();
0202             types += "\n    " + QString::number(type2.index());
0203             auto hinted = t.dynamicCast<HintedType>();
0204             while (hinted) {
0205                 auto target = hinted->type();
0206                 types += " (aka " + QString::number(target->indexed().index()) + ": " + target->toString()
0207                         +  " and context " + QString::number(hinted->createdBy().index()) + ")";
0208                 hinted = target.dynamicCast<HintedType>();
0209             }
0210             types += " - " + t->toString() + " of type "  + typeid(*t).name();
0211             if (!foundDuplicates) {
0212                 if (checkDuplicates.contains(str)) {
0213                     foundDuplicates = true;
0214                 } else {
0215                     checkDuplicates.append(str);
0216                 }
0217             }
0218         }
0219         if (foundDuplicates) {
0220             qWarning().nospace().noquote() << "found potential duplicates when adding " << typeid(*type).name()
0221                 << " " << type->toString()
0222                 << "(index = " << indexed.index() << ") ->" << types;
0223         }
0224     }
0225 #endif
0226 }
0227 
0228 bool UnsureType::equals(const AbstractType* rhs) const
0229 {
0230     if ( this == rhs ) {
0231         return true;
0232     }
0233     if ( ! dynamic_cast<const UnsureType*>(rhs) ) {
0234         return false;
0235     }
0236     if ( ! KDevelop::UnsureType::equals(rhs) ) {
0237         return false;
0238     }
0239     return true;
0240 }
0241 
0242 uint UnsureType::hash() const
0243 {
0244     return KDevelop::UnsureType::hash() + 1;
0245 }
0246 
0247 }