File indexing completed on 2024-05-19 15:46:44
0001 /* 0002 SPDX-FileCopyrightText: 2013 Andrea Scarpino <scarpino@kde.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "helper.h" 0008 #include "functiondeclaration.h" 0009 #include "functiontype.h" 0010 #include "parsesession.h" 0011 #include "frameworks/nodejs.h" 0012 #include "qmljsducontext.h" 0013 0014 #include <language/duchain/duchain.h> 0015 #include <language/duchain/duchainlock.h> 0016 #include <language/duchain/duchainregister.h> 0017 #include <language/duchain/functiondeclaration.h> 0018 #include <language/duchain/classfunctiondeclaration.h> 0019 #include <language/duchain/namespacealiasdeclaration.h> 0020 #include <language/duchain/types/unsuretype.h> 0021 #include <language/duchain/types/integraltype.h> 0022 #include <language/duchain/types/structuretype.h> 0023 #include <language/duchain/types/functiontype.h> 0024 #include <language/duchain/types/typeutils.h> 0025 #include <language/duchain/types/typeregister.h> 0026 0027 namespace QmlJS 0028 { 0029 using namespace KDevelop; 0030 0031 AbstractType::Ptr mergeTypes(AbstractType::Ptr type, const AbstractType::Ptr& newType) 0032 { 0033 if (newType && newType->whichType() == AbstractType::TypeFunction) { 0034 return newType; 0035 } else { 0036 return TypeUtils::mergeTypes(std::move(type), newType); 0037 } 0038 } 0039 0040 DeclarationPointer getDeclaration(const QualifiedIdentifier& id, const DUContext* context, bool searchInParent) 0041 { 0042 DUChainReadLocker lock; 0043 if (context) { 0044 auto declarations = context->findDeclarations( 0045 id.indexedLast(), 0046 CursorInRevision(INT_MAX, INT_MAX), 0047 nullptr, 0048 searchInParent ? DUContext::NoSearchFlags : DUContext::DontSearchInParent 0049 ); 0050 0051 if (declarations.count() > 0) { 0052 return DeclarationPointer(declarations.last()); 0053 } 0054 } 0055 return DeclarationPointer(); 0056 } 0057 0058 DeclarationPointer getDeclarationOrSignal(const QualifiedIdentifier& id, const DUContext* context, bool searchInParent) 0059 { 0060 QString identifier = id.last().toString(); 0061 0062 if (identifier.startsWith(QLatin1String("on")) && identifier.size() > 2) { 0063 // The use may have typed the name of a QML slot (onFoo), try to get 0064 // the declaration of its corresponding signal (foo) 0065 identifier = identifier.at(2).toLower() + identifier.midRef(3); 0066 DeclarationPointer decl = getDeclaration(QualifiedIdentifier(identifier), context, searchInParent); 0067 0068 if (decl) { 0069 auto* classFuncDecl = dynamic_cast<ClassFunctionDeclaration *>(decl.data()); 0070 0071 if (classFuncDecl && classFuncDecl->isSignal()) { 0072 // Removing "on" has given the identifier of a QML signal, return 0073 // it instead of the name of its slot 0074 return decl; 0075 } 0076 } 0077 } 0078 0079 // No signal found, fall back to normal behavior 0080 return getDeclaration(id, context, searchInParent); 0081 } 0082 0083 QmlJS::AST::Statement* getQMLAttribute(QmlJS::AST::UiObjectMemberList* members, const QString& attribute) 0084 { 0085 for (QmlJS::AST::UiObjectMemberList *it = members; it; it = it->next) { 0086 // The member needs to be a script binding whose name matches attribute 0087 auto* binding = QmlJS::AST::cast<QmlJS::AST::UiScriptBinding*>(it->member); 0088 0089 if (binding && binding->qualifiedId && binding->qualifiedId->name == attribute) { 0090 return binding->statement; 0091 } 0092 } 0093 0094 return nullptr; 0095 } 0096 0097 QString getNodeValue(AST::Node* node) 0098 { 0099 auto identifier = QmlJS::AST::cast<QmlJS::AST::IdentifierExpression*>(node); 0100 auto identifier_name = QmlJS::AST::cast<QmlJS::AST::IdentifierPropertyName*>(node); 0101 auto string = QmlJS::AST::cast<QmlJS::AST::StringLiteral*>(node); 0102 auto string_name = QmlJS::AST::cast<QmlJS::AST::StringLiteralPropertyName*>(node); 0103 auto true_literal = QmlJS::AST::cast<QmlJS::AST::TrueLiteral*>(node); 0104 auto false_literal = QmlJS::AST::cast<QmlJS::AST::FalseLiteral*>(node); 0105 0106 if (identifier) { 0107 return identifier->name.toString(); 0108 } else if (identifier_name) { 0109 return identifier_name->id.toString(); 0110 } else if (string) { 0111 return string->value.toString(); 0112 } else if (string_name) { 0113 return string_name->id.toString(); 0114 } else if (true_literal) { 0115 return QStringLiteral("true"); 0116 } else if (false_literal) { 0117 return QStringLiteral("false"); 0118 } else { 0119 return QString(); 0120 } 0121 } 0122 0123 QMLAttributeValue getQMLAttributeValue(QmlJS::AST::UiObjectMemberList* members, const QString& attribute) 0124 { 0125 QMLAttributeValue res; 0126 QmlJS::AST::Statement* node = getQMLAttribute(members, attribute); 0127 0128 // The value of the binding must be an expression 0129 auto* statement = QmlJS::AST::cast<QmlJS::AST::ExpressionStatement*>(node); 0130 0131 if (!statement) { 0132 return res; 0133 } 0134 0135 // The expression must be an identifier or a string literal 0136 res.value = getNodeValue(statement->expression); 0137 0138 if (res.value.isEmpty()) { 0139 return res; 0140 } 0141 0142 res.location = statement->expression->firstSourceLocation(); 0143 0144 return res; 0145 } 0146 0147 DUContext* getInternalContext(const DeclarationPointer& declaration) 0148 { 0149 DUChainReadLocker lock; 0150 0151 if (!declaration) { 0152 return nullptr; 0153 } 0154 0155 // The internal context of declarations having a function type is the prototype 0156 // context of the function (if any), or the internal context of Function 0157 auto functionType = declaration->abstractType().dynamicCast<QmlJS::FunctionType>(); 0158 0159 if (functionType) { 0160 Declaration* decl = functionType->declaration(declaration->topContext()); 0161 QmlJS::FunctionDeclaration* funcDecl; 0162 0163 if (decl && (funcDecl = dynamic_cast<QmlJS::FunctionDeclaration*>(decl)) && 0164 funcDecl->prototypeContext()) { 0165 return funcDecl->prototypeContext(); 0166 } 0167 } 0168 0169 // The declaration can either be a class definition (its internal context 0170 // can be used) or an instance (use the internal context of its type) 0171 switch (declaration->kind()) { 0172 case Declaration::Type: 0173 case Declaration::Namespace: 0174 return declaration->internalContext(); 0175 0176 case Declaration::NamespaceAlias: 0177 { 0178 auto alias = declaration.dynamicCast<NamespaceAliasDeclaration>(); 0179 0180 return getInternalContext(getDeclaration(alias->importIdentifier(), alias->context())); 0181 } 0182 0183 default: 0184 { 0185 auto structureType = declaration->abstractType().dynamicCast<StructureType>(); 0186 auto integralType = declaration->abstractType().dynamicCast<IntegralType>(); 0187 0188 static const IndexedIdentifier indexedObject(Identifier(QStringLiteral("Object"))); 0189 if (structureType) { 0190 auto structureDeclaration = structureType->declaration(declaration->topContext()); 0191 0192 if (structureDeclaration != declaration.data()) { 0193 return getInternalContext( 0194 DeclarationPointer(structureDeclaration) 0195 ); 0196 } else { 0197 return nullptr; 0198 } 0199 } else if ((integralType || functionType) && declaration->indexedIdentifier() != indexedObject) { 0200 QString baseClass; 0201 0202 // Compute from which base Javascript class a type inherits 0203 if (integralType) { 0204 switch (integralType->dataType()) { 0205 case IntegralType::TypeBoolean: 0206 baseClass = QStringLiteral("Boolean"); 0207 break; 0208 case IntegralType::TypeString: 0209 baseClass = QStringLiteral("String"); 0210 break; 0211 case IntegralType::TypeInt: 0212 case IntegralType::TypeHalf: 0213 case IntegralType::TypeFloat: 0214 case IntegralType::TypeDouble: 0215 baseClass = QStringLiteral("Number"); 0216 break; 0217 case IntegralType::TypeArray: 0218 baseClass = QStringLiteral("Array"); 0219 break; 0220 default: 0221 baseClass = QStringLiteral("Object"); 0222 break; 0223 } 0224 } else if (functionType) { 0225 baseClass = QStringLiteral("Function"); 0226 } 0227 0228 return getInternalContext( 0229 NodeJS::instance().moduleMember(QStringLiteral("__builtin_ecmascript"), baseClass, declaration->topContext()->url()) 0230 ); 0231 } else { 0232 return nullptr; 0233 } 0234 } 0235 } 0236 } 0237 0238 Declaration* getOwnerOfContext(const DUContext* context) 0239 { 0240 if (context->owner()) { 0241 return context->owner(); 0242 } else if (context->type() == DUContext::Function && context->parentContext()) { 0243 return context->parentContext()->owner(); 0244 } else { 0245 return nullptr; 0246 } 0247 } 0248 0249 RangeInRevision emptyRangeOnLine(const AST::SourceLocation& location) 0250 { 0251 return RangeInRevision(location.startLine - 1, 0, location.startLine - 1, 0); 0252 } 0253 0254 void importDeclarationInContext(DUContext* context, const DeclarationPointer& declaration) 0255 { 0256 DUContext* importedContext = QmlJS::getInternalContext(declaration); 0257 0258 if (!importedContext || importedContext == context) { 0259 return; 0260 } 0261 0262 { 0263 DUChainWriteLocker lock; 0264 context->addImportedParentContext(importedContext); 0265 } 0266 } 0267 0268 void importObjectContext(DUContext* context, TopDUContext* topContext) 0269 { 0270 DeclarationPointer objectDeclaration = 0271 NodeJS::instance().moduleMember(QStringLiteral("__builtin_ecmascript"), QStringLiteral("Object"), topContext->url()); 0272 0273 if (objectDeclaration) { 0274 importDeclarationInContext(context, objectDeclaration); 0275 } 0276 } 0277 0278 bool isPrototypeIdentifier(const QString& identifier) 0279 { 0280 return (identifier == QLatin1String("prototype") || 0281 identifier == QLatin1String("__proto__")); 0282 } 0283 0284 bool isQmlFile(const DUContext* context) 0285 { 0286 DUChainReadLocker lock; 0287 return ParseSession::guessLanguageFromSuffix(context->topContext()->url().str()) == Dialect::Qml; 0288 } 0289 0290 void registerDUChainItems() 0291 { 0292 duchainRegisterType<QmlJSTopDUContext>(); 0293 duchainRegisterType<QmlJSNormalDUContext>(); 0294 duchainRegisterType<FunctionDeclaration>(); 0295 0296 TypeSystem::self().registerTypeClass<FunctionType>(); 0297 } 0298 0299 void unregisterDUChainItems() 0300 { 0301 TypeSystem::self().unregisterTypeClass<FunctionType>(); 0302 0303 // rest not supported, see comment in kdev-clang 0304 } 0305 0306 } // End of namespace QmlJS