Changeset View
Changeset View
Standalone View
Standalone View
duchain/expressionvisitor.cpp
Show First 20 Lines • Show All 109 Lines • ▼ Show 20 Line(s) | 93 | { | |||
---|---|---|---|---|---|
110 | } | 110 | } | ||
111 | } | 111 | } | ||
112 | 112 | | |||
113 | void ExpressionVisitor::visitCall(CallAst* node) | 113 | void ExpressionVisitor::visitCall(CallAst* node) | ||
114 | { | 114 | { | ||
115 | foreach ( ExpressionAst* c, node->arguments ) { | 115 | foreach ( ExpressionAst* c, node->arguments ) { | ||
116 | AstDefaultVisitor::visitNode(c); | 116 | AstDefaultVisitor::visitNode(c); | ||
117 | } | 117 | } | ||
118 | | ||||
119 | ExpressionVisitor v(this); | 118 | ExpressionVisitor v(this); | ||
120 | v.visitNode(node->function); | 119 | v.visitNode(node->function); | ||
121 | Declaration* actualDeclaration = 0; | 120 | auto declaration = Helper::resolveAliasDeclaration(v.lastDeclaration().data()); | ||
122 | FunctionType::Ptr unidentifiedFunctionType; | 121 | if ( ! v.isAlias() && v.lastType() ) { | ||
123 | if ( ! v.m_isAlias && v.lastType() && v.lastType()->whichType() == AbstractType::TypeFunction ) { | 122 | if ( auto functionType = v.lastType().cast<FunctionType>() ) { | ||
124 | unidentifiedFunctionType = v.lastType().cast<FunctionType>(); | 123 | encounter(functionType->returnType()); | ||
125 | } | 124 | return; | ||
126 | else if ( ! v.m_isAlias && v.lastType() && v.lastType()->whichType() == AbstractType::TypeStructure ) { | | |||
127 | // use __call__ | | |||
128 | DUChainReadLocker lock; | | |||
129 | auto c = v.lastType().cast<StructureType>()->internalContext(topContext()); | | |||
130 | if ( c ) { | | |||
131 | auto decls = c->findDeclarations(QualifiedIdentifier("__call__")); | | |||
132 | if ( ! decls.isEmpty() ) { | | |||
133 | auto decl = dynamic_cast<FunctionDeclaration*>(decls.first()); | | |||
134 | if ( decl ) { | | |||
135 | unidentifiedFunctionType = decl->abstractType().cast<FunctionType>(); | | |||
136 | } | | |||
137 | } | 125 | } | ||
126 | if ( auto classType = v.lastType().cast<StructureType>() ) { | ||||
127 | declaration = classType->declaration(topContext()); | ||||
138 | } | 128 | } | ||
139 | } | 129 | } | ||
140 | else { | 130 | if ( ! declaration ) { | ||
141 | actualDeclaration = v.lastDeclaration().data(); | 131 | encounterUnknown(); | ||
142 | } | | |||
143 | | ||||
144 | if ( unidentifiedFunctionType ) { | | |||
145 | encounter(unidentifiedFunctionType->returnType()); | | |||
146 | return; | 132 | return; | ||
147 | } | 133 | } | ||
148 | else if ( !actualDeclaration ) { | 134 | ClassDeclaration* classDecl = dynamic_cast<ClassDeclaration*>(declaration); | ||
149 | setConfident(false); | | |||
150 | return encounterUnknown(); | | |||
151 | } | | |||
152 | | ||||
153 | DUChainReadLocker lock; | 135 | DUChainReadLocker lock; | ||
154 | actualDeclaration = Helper::resolveAliasDeclaration(actualDeclaration); | 136 | auto function = Helper::functionForCalled(declaration, v.isAlias()); | ||
155 | ClassDeclaration* classDecl = dynamic_cast<ClassDeclaration*>(actualDeclaration); | | |||
156 | auto function = Helper::functionForCalled(actualDeclaration); | | |||
157 | lock.unlock(); | 137 | lock.unlock(); | ||
158 | 138 | | |||
159 | if ( function.declaration && function.declaration->type<FunctionType>() ) { | 139 | AbstractType::Ptr type; | ||
160 | // try to deduce type from a decorator | 140 | Declaration* decl; | ||
161 | checkForDecorators(node, function.declaration, classDecl, function.isConstructor); | 141 | | ||
142 | if ( function.isConstructor && classDecl ) { | ||||
143 | // Don't use return type from constructor. | ||||
144 | // It's wrong for builtins, or classes without their own __init__ methods(). | ||||
145 | type = classDecl->abstractType(); | ||||
146 | decl = classDecl; | ||||
162 | } | 147 | } | ||
163 | else if ( classDecl ) { | 148 | else if ( function.declaration && function.declaration->type<FunctionType>() ) { | ||
164 | return encounter(classDecl->abstractType(), DeclarationPointer(classDecl)); | 149 | // But do use the return value of normal functions or __call__(). | ||
150 | type = function.declaration->type<FunctionType>()->returnType(); | ||||
151 | decl = function.declaration; | ||||
165 | } | 152 | } | ||
166 | else { | 153 | else { | ||
167 | if ( actualDeclaration ) { | 154 | qCDebug(KDEV_PYTHON_DUCHAIN) << "Declaration is not a class or function declaration"; | ||
168 | qCDebug(KDEV_PYTHON_DUCHAIN) << "Declaraton is not a class or function declaration"; | 155 | encounterUnknown(); | ||
169 | } | 156 | return; | ||
170 | return encounterUnknown(); | | |||
171 | } | 157 | } | ||
158 | if ( function.declaration ) { | ||||
159 | auto docstring = function.declaration->comment(); | ||||
160 | if ( ! docstring.isEmpty() ) { | ||||
161 | // Our documentation data uses special docstrings that override the return type | ||||
162 | // of some functions (including constructors). | ||||
163 | type = docstringTypeOverride(node, type, docstring); | ||||
172 | } | 164 | } | ||
173 | | ||||
174 | void ExpressionVisitor::checkForDecorators(CallAst* node, FunctionDeclaration* funcDecl, ClassDeclaration* classDecl, bool isConstructor) | | |||
175 | { | | |||
176 | AbstractType::Ptr type; | | |||
177 | Declaration* useDeclaration = nullptr; | | |||
178 | if ( isConstructor && classDecl ) { | | |||
179 | type = classDecl->abstractType(); | | |||
180 | useDeclaration = classDecl; | | |||
181 | } | 165 | } | ||
182 | else { | 166 | encounter(type, DeclarationPointer(decl)); | ||
183 | type = funcDecl->type<FunctionType>()->returnType(); | | |||
184 | useDeclaration = funcDecl; | | |||
185 | } | 167 | } | ||
186 | 168 | | |||
169 | AbstractType::Ptr ExpressionVisitor::docstringTypeOverride( | ||||
170 | CallAst* node, const AbstractType::Ptr normalType, const QString& docstring) | ||||
171 | { | ||||
172 | auto docstringType = normalType; | ||||
187 | auto listOfTuples = [&](AbstractType::Ptr key, AbstractType::Ptr value) { | 173 | auto listOfTuples = [&](AbstractType::Ptr key, AbstractType::Ptr value) { | ||
188 | auto newType = typeObjectForIntegralType<ListType>("list"); | 174 | auto newType = typeObjectForIntegralType<ListType>("list"); | ||
189 | IndexedContainer::Ptr newContents = typeObjectForIntegralType<IndexedContainer>("tuple"); | 175 | IndexedContainer::Ptr newContents = typeObjectForIntegralType<IndexedContainer>("tuple"); | ||
190 | if ( ! newType || ! newContents ) { | 176 | if ( ! newType || ! newContents ) { | ||
191 | return AbstractType::Ptr(new IntegralType(IntegralType::TypeMixed)); | 177 | return AbstractType::Ptr(new IntegralType(IntegralType::TypeMixed)); | ||
192 | } | 178 | } | ||
193 | if ( ! key ) { | 179 | if ( ! key ) { | ||
194 | key = AbstractType::Ptr(new IntegralType(IntegralType::TypeMixed)); | 180 | key = AbstractType::Ptr(new IntegralType(IntegralType::TypeMixed)); | ||
Show All 14 Lines | 194 | knownDecoratorHints["getsType"] = [&](QStringList /*arguments*/, QString /*currentHint*/) { | |||
209 | if ( node->function->astType != Ast::AttributeAstType ) { | 195 | if ( node->function->astType != Ast::AttributeAstType ) { | ||
210 | return false; | 196 | return false; | ||
211 | } | 197 | } | ||
212 | ExpressionVisitor baseTypeVisitor(this); | 198 | ExpressionVisitor baseTypeVisitor(this); | ||
213 | // when calling foo.bar[3].baz.iteritems(), find the type of "foo.bar[3].baz" | 199 | // when calling foo.bar[3].baz.iteritems(), find the type of "foo.bar[3].baz" | ||
214 | baseTypeVisitor.visitNode(static_cast<AttributeAst*>(node->function)->value); | 200 | baseTypeVisitor.visitNode(static_cast<AttributeAst*>(node->function)->value); | ||
215 | if ( auto t = baseTypeVisitor.lastType().cast<ListType>() ) { | 201 | if ( auto t = baseTypeVisitor.lastType().cast<ListType>() ) { | ||
216 | qCDebug(KDEV_PYTHON_DUCHAIN) << "Found container, using type"; | 202 | qCDebug(KDEV_PYTHON_DUCHAIN) << "Found container, using type"; | ||
217 | AbstractType::Ptr newType = t->contentType().abstractType(); | 203 | docstringType = t->contentType().abstractType(); | ||
218 | encounter(newType, DeclarationPointer(useDeclaration)); | | |||
219 | return true; | 204 | return true; | ||
220 | } | 205 | } | ||
221 | return false; | 206 | return false; | ||
222 | }; | 207 | }; | ||
223 | 208 | | |||
224 | knownDecoratorHints["getsList"] = [&](QStringList /*arguments*/, QString currentHint) { | 209 | knownDecoratorHints["getsList"] = [&](QStringList /*arguments*/, QString currentHint) { | ||
225 | if ( node->function->astType != Ast::AttributeAstType ) { | 210 | if ( node->function->astType != Ast::AttributeAstType ) { | ||
226 | return false; | 211 | return false; | ||
Show All 11 Lines | 217 | if ( auto t = baseTypeVisitor.lastType().cast<ListType>() ) { | |||
238 | AbstractType::Ptr contentType; | 223 | AbstractType::Ptr contentType; | ||
239 | if ( currentHint == "getsList" ) { | 224 | if ( currentHint == "getsList" ) { | ||
240 | contentType = t->contentType().abstractType(); | 225 | contentType = t->contentType().abstractType(); | ||
241 | } | 226 | } | ||
242 | else if ( auto map = MapType::Ptr::dynamicCast(t) ) { | 227 | else if ( auto map = MapType::Ptr::dynamicCast(t) ) { | ||
243 | contentType = map->keyType().abstractType(); | 228 | contentType = map->keyType().abstractType(); | ||
244 | } | 229 | } | ||
245 | newType->addContentType<Python::UnsureType>(contentType); | 230 | newType->addContentType<Python::UnsureType>(contentType); | ||
246 | AbstractType::Ptr resultingType = newType.cast<AbstractType>(); | 231 | docstringType = newType.cast<AbstractType>(); | ||
247 | encounter(resultingType, DeclarationPointer(useDeclaration)); | | |||
248 | return true; | 232 | return true; | ||
249 | } | 233 | } | ||
250 | return false; | 234 | return false; | ||
251 | }; | 235 | }; | ||
252 | knownDecoratorHints["getListOfKeys"] = knownDecoratorHints["getsList"]; | 236 | knownDecoratorHints["getListOfKeys"] = knownDecoratorHints["getsList"]; | ||
253 | 237 | | |||
254 | knownDecoratorHints["enumerate"] = [&](QStringList /*arguments*/, QString /*currentHint*/) { | 238 | knownDecoratorHints["enumerate"] = [&](QStringList /*arguments*/, QString /*currentHint*/) { | ||
255 | if ( node->function->astType != Ast::NameAstType || node->arguments.size() < 1 ) { | 239 | if ( node->function->astType != Ast::NameAstType || node->arguments.size() < 1 ) { | ||
256 | return false; | 240 | return false; | ||
257 | } | 241 | } | ||
258 | ExpressionVisitor enumeratedTypeVisitor(this); | 242 | ExpressionVisitor enumeratedTypeVisitor(this); | ||
259 | enumeratedTypeVisitor.visitNode(node->arguments.first()); | 243 | enumeratedTypeVisitor.visitNode(node->arguments.first()); | ||
260 | 244 | | |||
261 | DUChainWriteLocker lock; | 245 | DUChainWriteLocker lock; | ||
262 | auto intType = typeObjectForIntegralType<AbstractType>("int"); | 246 | auto intType = typeObjectForIntegralType<AbstractType>("int"); | ||
263 | auto enumerated = enumeratedTypeVisitor.lastType(); | 247 | auto enumerated = enumeratedTypeVisitor.lastType(); | ||
264 | auto result = listOfTuples(intType, Helper::contentOfIterable(enumerated, topContext())); | 248 | docstringType = listOfTuples(intType, Helper::contentOfIterable(enumerated, topContext())); | ||
265 | encounter(result, DeclarationPointer(useDeclaration)); | | |||
266 | return true; | 249 | return true; | ||
267 | }; | 250 | }; | ||
268 | 251 | | |||
269 | knownDecoratorHints["getsListOfBoth"] = [&](QStringList /*arguments*/, QString /*currentHint*/) { | 252 | knownDecoratorHints["getsListOfBoth"] = [&](QStringList /*arguments*/, QString /*currentHint*/) { | ||
270 | qCDebug(KDEV_PYTHON_DUCHAIN) << "Got getsListOfBoth decorator, checking container"; | 253 | qCDebug(KDEV_PYTHON_DUCHAIN) << "Got getsListOfBoth decorator, checking container"; | ||
271 | if ( node->function->astType != Ast::AttributeAstType ) { | 254 | if ( node->function->astType != Ast::AttributeAstType ) { | ||
272 | return false; | 255 | return false; | ||
273 | } | 256 | } | ||
274 | ExpressionVisitor baseTypeVisitor(this); | 257 | ExpressionVisitor baseTypeVisitor(this); | ||
275 | // when calling foo.bar[3].baz.iteritems(), find the type of "foo.bar[3].baz" | 258 | // when calling foo.bar[3].baz.iteritems(), find the type of "foo.bar[3].baz" | ||
276 | baseTypeVisitor.visitNode(static_cast<AttributeAst*>(node->function)->value); | 259 | baseTypeVisitor.visitNode(static_cast<AttributeAst*>(node->function)->value); | ||
277 | DUChainWriteLocker lock; | 260 | DUChainWriteLocker lock; | ||
278 | if ( auto t = baseTypeVisitor.lastType().cast<MapType>() ) { | 261 | if ( auto t = baseTypeVisitor.lastType().cast<MapType>() ) { | ||
279 | qCDebug(KDEV_PYTHON_DUCHAIN) << "Got container:" << t->toString(); | 262 | qCDebug(KDEV_PYTHON_DUCHAIN) << "Got container:" << t->toString(); | ||
280 | auto resultingType = listOfTuples(t->keyType().abstractType(), t->contentType().abstractType()); | 263 | docstringType = listOfTuples(t->keyType().abstractType(), t->contentType().abstractType()); | ||
281 | encounter(resultingType, DeclarationPointer(useDeclaration)); | | |||
282 | return true; | 264 | return true; | ||
283 | } | 265 | } | ||
284 | return false; | 266 | return false; | ||
285 | }; | 267 | }; | ||
286 | 268 | | |||
287 | knownDecoratorHints["returnContentEqualsContentOf"] = [&](QStringList arguments, QString /*currentHint*/) { | 269 | knownDecoratorHints["returnContentEqualsContentOf"] = [&](QStringList arguments, QString /*currentHint*/) { | ||
288 | int argNum = ! arguments.isEmpty() ? arguments.at(0).toInt() : 0; | 270 | int argNum = ! arguments.isEmpty() ? arguments.at(0).toInt() : 0; | ||
289 | qCDebug(KDEV_PYTHON_DUCHAIN) << "Found argument dependent decorator, checking argument type" << argNum; | 271 | qCDebug(KDEV_PYTHON_DUCHAIN) << "Found argument dependent decorator, checking argument type" << argNum; | ||
290 | if ( argNum >= node->arguments.length() ) { | 272 | if ( argNum >= node->arguments.length() ) { | ||
291 | return false; | 273 | return false; | ||
292 | } | 274 | } | ||
293 | ExpressionAst* relevantArgument = node->arguments.at(argNum); | 275 | ExpressionAst* relevantArgument = node->arguments.at(argNum); | ||
294 | ExpressionVisitor v(this); | 276 | ExpressionVisitor v(this); | ||
295 | v.visitNode(relevantArgument); | 277 | v.visitNode(relevantArgument); | ||
296 | if ( ! v.lastType() ) { | 278 | if ( ! v.lastType() ) { | ||
297 | return false; | 279 | return false; | ||
298 | } | 280 | } | ||
299 | ListType::Ptr realTarget; | 281 | ListType::Ptr realTarget; | ||
300 | if ( auto target = ListType::Ptr::dynamicCast(type) ) { | 282 | if ( auto target = ListType::Ptr::dynamicCast(normalType) ) { | ||
301 | realTarget = target; | 283 | realTarget = target; | ||
302 | } | 284 | } | ||
303 | if ( auto source = ListType::Ptr::dynamicCast(v.lastType()) ) { | 285 | if ( auto source = ListType::Ptr::dynamicCast(v.lastType()) ) { | ||
304 | if ( ! realTarget ) { | 286 | if ( ! realTarget ) { | ||
305 | // if the function does not force a return type, just copy the source (like for reversed()) | 287 | // if the function does not force a return type, just copy the source (like for reversed()) | ||
306 | realTarget = source; | 288 | realTarget = source; | ||
307 | } | 289 | } | ||
308 | auto newType = ListType::Ptr::staticCast(AbstractType::Ptr(realTarget->clone())); | 290 | auto newType = ListType::Ptr::staticCast(AbstractType::Ptr(realTarget->clone())); | ||
309 | Q_ASSERT(newType); | 291 | Q_ASSERT(newType); | ||
310 | newType->addContentType<Python::UnsureType>(source->contentType().abstractType()); | 292 | newType->addContentType<Python::UnsureType>(source->contentType().abstractType()); | ||
311 | encounter(AbstractType::Ptr::staticCast(newType), DeclarationPointer(useDeclaration)); | 293 | docstringType = AbstractType::Ptr::staticCast(newType); | ||
312 | return true; | 294 | return true; | ||
313 | } | 295 | } | ||
314 | return false; | 296 | return false; | ||
315 | }; | 297 | }; | ||
316 | 298 | | |||
317 | auto docstring = funcDecl->comment(); | | |||
318 | if ( ! docstring.isEmpty() ) { | | |||
319 | foreach ( const QString& currentHint, knownDecoratorHints.keys() ) { | 299 | foreach ( const QString& currentHint, knownDecoratorHints.keys() ) { | ||
320 | QStringList arguments; | 300 | QStringList arguments; | ||
321 | if ( ! Helper::docstringContainsHint(docstring, currentHint, &arguments) ) { | 301 | if ( ! Helper::docstringContainsHint(docstring, currentHint, &arguments) ) { | ||
322 | continue; | 302 | continue; | ||
323 | } | 303 | } | ||
324 | // If the hint word appears in the docstring, run the evaluation function. | 304 | // If the hint word appears in the docstring, run the evaluation function. | ||
325 | if ( knownDecoratorHints[currentHint](arguments, currentHint) ) { | 305 | if ( knownDecoratorHints[currentHint](arguments, currentHint) ) { | ||
326 | // We indeed found something, so we're done. | 306 | // We indeed found something, so we're done. | ||
327 | return; | 307 | return docstringType; | ||
328 | } | | |||
329 | } | 308 | } | ||
330 | } | 309 | } | ||
331 | 310 | | |||
332 | // if none of the above decorator-finding methods worked, just use the ordinary return type. | 311 | // if none of the above decorator-finding methods worked, just use the ordinary return type. | ||
333 | return encounter(type, DeclarationPointer(useDeclaration)); | 312 | return docstringType; | ||
334 | } | 313 | } | ||
335 | 314 | | |||
336 | void ExpressionVisitor::visitSubscript(SubscriptAst* node) | 315 | void ExpressionVisitor::visitSubscript(SubscriptAst* node) | ||
337 | { | 316 | { | ||
338 | AstDefaultVisitor::visitNode(node->value); | 317 | AstDefaultVisitor::visitNode(node->value); | ||
339 | 318 | | |||
340 | auto valueTypes = Helper::filterType<AbstractType>(lastType(), [](AbstractType::Ptr) { return true; }); | 319 | auto valueTypes = Helper::filterType<AbstractType>(lastType(), [](AbstractType::Ptr) { return true; }); | ||
341 | AbstractType::Ptr result(new IntegralType(IntegralType::TypeMixed)); | 320 | AbstractType::Ptr result(new IntegralType(IntegralType::TypeMixed)); | ||
▲ Show 20 Lines • Show All 365 Lines • Show Last 20 Lines |