1 /*
2     SPDX-FileCopyrightText: 2012 Olivier de Gaalon <olivier.jg@gmail.com>
3 
4     SPDX-License-Identifier: LGPL-2.0-only
5 */
6 
7 #ifndef KDEVPLATFORM_JSONDECLARATIONTESTS_H
8 #define KDEVPLATFORM_JSONDECLARATIONTESTS_H
9 
10 #include "language/duchain/ducontext.h"
11 #include "language/duchain/declaration.h"
12 #include "language/duchain/indexeddeclaration.h"
13 #include "language/duchain/identifier.h"
14 #include "language/duchain/abstractfunctiondeclaration.h"
15 #include "language/duchain/types/typeutils.h"
16 #include "language/duchain/types/identifiedtype.h"
17 #include <language/duchain/types/functiontype.h>
18 #include "language/duchain/duchain.h"
19 #include "language/duchain/functiondefinition.h"
20 #include "language/duchain/definitions.h"
21 #include <language/duchain/classfunctiondeclaration.h>
22 #include "jsontesthelpers.h"
23 
24 /**
25  * JSON Object Specification:
26  *   DeclTestObject: Mapping of (string) declaration test names to values
27  *   TypeTestObject: Mapping of (string) type test names to values
28  *   CtxtTestObject: Mapping of (string) context test names to values
29  *
30  * Quick Reference:
31  *   useCount : int
32  *   useRanges : string array
33  *   identifier : string
34  *   qualifiedIdentifier : string
35  *   internalContext : CtxtTestObject
36  *   internalFunctionContext : CtxtTestObject
37  *   type : TypeTestObject
38  *   unaliasedType : TypeTestObject
39  *   targetType : TypeTestObject
40  *   returnType : TypeTestObject
41  *   isAbstract : bool
42  *   isMutable : bool
43  *   isVirtual : bool
44  *   isStatic : bool
45  *   declaration : DeclTestObject
46  *   definition : DeclTestObject
47  *   identifiedTypeDeclaration : DeclTestObject
48  *   null : bool
49  *   defaultParameter : string
50  *   toString : string
51  *   range : string
52  *   kind : string
53  *   isDeprecated : bool
54  *   isDefinition : bool
55  *   isExplicitlyTyped : bool
56  */
57 
58 namespace KDevelop {
59 template<>
objectInformation(Declaration * decl)60 QString TestSuite<Declaration*>::objectInformation(Declaration* decl)
61 {
62     VERIFY_NOT_NULL(decl);
63     return QStringLiteral("(Declaration on line %1 in %2)")
64            .arg(decl->range().start.line + 1)
65            .arg(decl->topContext()->url().str());
66 }
67 
68 namespace DeclarationTests {
69 using namespace JsonTestHelpers;
70 
71 ///JSON type: int
72 ///@returns whether the declaration's number of uses matches the given value
DeclarationTest(useCount)73 DeclarationTest(useCount)
74 {
75     int uses = 0;
76     const auto declarationUses = decl->uses();
77     for (const auto& useRanges : declarationUses) {
78         uses += useRanges.size();
79     }
80 
81     return compareValues(uses, value, QStringLiteral("Declaration's use count "));
82 }
83 ///JSON type: string array
84 ///@returns whether the declaration's ranges match the given value
DeclarationTest(useRanges)85 DeclarationTest(useRanges)
86 {
87     QStringList ranges;
88     const auto declarationUses = decl->uses();
89     for (const auto& useRanges : declarationUses) {
90         for (const RangeInRevision range : useRanges) {
91             ranges << rangeStr(range);
92         }
93     }
94 
95     const QStringList testValues = value.toStringList();
96     return ranges == testValues ? SUCCESS()
97            : QStringLiteral("Declaration's use ranges (\"%1\") don't match test data (\"%2\").").arg(ranges.join(
98                                                                                                          QStringLiteral(
99                                                                                                              ", ")),
100                                                                                                      testValues.join(
101                                                                                                          QStringLiteral(
102                                                                                                              ", ")));
103 }
104 ///JSON type: string
105 ///@returns whether the declaration's identifier matches the given value
DeclarationTest(identifier)106 DeclarationTest(identifier)
107 {
108     VERIFY_NOT_NULL(decl);
109     return compareValues(decl->identifier().toString(), value, QStringLiteral("Declaration's identifier"));
110 }
111 ///JSON type: string
112 ///@returns whether the declaration's qualified identifier matches the given value
DeclarationTest(qualifiedIdentifier)113 DeclarationTest(qualifiedIdentifier)
114 {
115     VERIFY_NOT_NULL(decl);
116     return compareValues(decl->qualifiedIdentifier().toString(), value,
117                          QStringLiteral("Declaration's qualified identifier"));
118 }
119 ///JSON type: CtxtTestObject
120 ///@returns whether the tests for the declaration's internal context pass
DeclarationTest(internalContext)121 DeclarationTest(internalContext)
122 {
123     VERIFY_NOT_NULL(decl);
124     return testObject(decl->internalContext(), value, QStringLiteral("Declaration's internal context"));
125 }
126 ///JSON type: CtxtTestObject
127 ///@returns whether the tests for the declaration's internal function context pass
DeclarationTest(internalFunctionContext)128 DeclarationTest(internalFunctionContext)
129 {
130     const QString NO_INTERNAL_CTXT = QStringLiteral("%1 has no internal function context.");
131     auto* absFuncDecl = dynamic_cast<AbstractFunctionDeclaration*>(decl);
132     if (!absFuncDecl || !absFuncDecl->internalFunctionContext())
133         return NO_INTERNAL_CTXT.arg(decl->qualifiedIdentifier().toString());
134     return testObject(absFuncDecl->internalFunctionContext(), value,
135                       QStringLiteral("Declaration's internal function context"));
136 }
137 /*FIXME: The type functions need some renaming and moving around
138  * Some (all?) functions from cpp's TypeUtils should be moved to the kdevplatform type utils
139  * targetType is exactly like realType except it also tosses pointers
140  * shortenTypeForViewing should go to type utils (and it's broken, it places const to the left of all *'s when it should be left or right of the type)
141  * UnaliasedType seems to be unable to understand aliases involving templates, perhaps a cpp version is in order
142  */
143 ///JSON type: TypeTestObject
144 ///@returns whether the tests for the declaration's type pass
DeclarationTest(type)145 DeclarationTest(type)
146 {
147     VERIFY_NOT_NULL(decl);
148     return testObject(decl->abstractType(), value, QStringLiteral("Declaration's type"));
149 }
150 ///JSON type: TypeTestObject
151 ///@returns whether the tests for the declaration's unaliased type pass (TypeUtils::unaliasedType)
DeclarationTest(unaliasedType)152 DeclarationTest(unaliasedType)
153 {
154     VERIFY_NOT_NULL(decl);
155     return testObject(TypeUtils::unAliasedType(decl->abstractType()), value,
156                       QStringLiteral("Declaration's unaliased type"));
157 }
158 ///JSON type: TypeTestObject
159 ///@returns whether the tests for the declaration's target type pass (TypeUtils::targetType)
DeclarationTest(targetType)160 DeclarationTest(targetType)
161 {
162     VERIFY_NOT_NULL(decl);
163     return testObject(TypeUtils::targetType(decl->abstractType(), decl->topContext()), value,
164                       QStringLiteral("Declaration's target type"));
165 }
166 ///JSON type: TestTypeObject
167 ///@returns the
DeclarationTest(returnType)168 DeclarationTest(returnType)
169 {
170     FunctionType::Ptr functionType = decl->abstractType().cast<FunctionType>();
171     AbstractType::Ptr returnType;
172     if (functionType) {
173         returnType = functionType->returnType();
174     }
175     return testObject(returnType, value, QStringLiteral("Declaration's return type"));
176 }
177 ///JSON type: DeclTestObject
178 ///@returns The IdentifiedType's declaration
DeclarationTest(identifiedTypeDeclaration)179 DeclarationTest(identifiedTypeDeclaration)
180 {
181     const QString UN_ID_ERROR = QStringLiteral("Unable to identify declaration of type \"%1\".");
182     AbstractType::Ptr type = TypeUtils::targetType(decl->abstractType(), decl->topContext());
183     auto* idType = dynamic_cast<IdentifiedType*>(type.data());
184     Declaration* idDecl = idType ? idType->declaration(decl->topContext()) : nullptr;
185     if (!idDecl)
186         return UN_ID_ERROR.arg(type->toString());
187 
188     return testObject(idDecl, value, QStringLiteral("IdentifiedType's declaration"));
189 }
190 ///JSON type: bool
191 ///@returns whether the (function) declaration's isVirtual matches the given value
DeclarationTest(isVirtual)192 DeclarationTest(isVirtual)
193 {
194     const QString NOT_A_FUNCTION = QStringLiteral("Non-function declaration cannot be virtual.");
195     auto* absFuncDecl = dynamic_cast<AbstractFunctionDeclaration*>(decl);
196     if (!absFuncDecl)
197         return NOT_A_FUNCTION;
198 
199     return compareValues(absFuncDecl->isVirtual(), value, QStringLiteral("Declaration's isVirtual"));
200 }
201 
202 ///JSON type: bool
203 ///@returns whether the (function) declaration's isAbstract matches the given value
DeclarationTest(isAbstract)204 DeclarationTest(isAbstract)
205 {
206     const QString NOT_A_FUNCTION = QStringLiteral("Non-class-function declaration cannot be abstract.");
207     auto* absFuncDecl = dynamic_cast<ClassFunctionDeclaration*>(decl);
208     if (!absFuncDecl)
209         return NOT_A_FUNCTION;
210 
211     return compareValues(absFuncDecl->isAbstract(), value, QStringLiteral("Declaration's isAbstract"));
212 }
213 ///JSON type: bool
214 ///@returns whether the (function) declaration's isAbstract matches the given value
DeclarationTest(isFinal)215 DeclarationTest(isFinal)
216 {
217     const QString NOT_A_FUNCTION = QStringLiteral("Non-class-function declaration cannot be final.");
218     auto* classFuncDecl = dynamic_cast<ClassFunctionDeclaration*>(decl);
219     if (!classFuncDecl)
220         return NOT_A_FUNCTION;
221 
222     return compareValues(classFuncDecl->isFinal(), value, QStringLiteral("Declaration's isFinal"));
223 }
224 ///JSON type: bool
225 ///@returns whether the (class-member) declaration's isStatic matches the given value
DeclarationTest(isStatic)226 DeclarationTest(isStatic)
227 {
228     const QString NOT_A_MEMBER = QStringLiteral("Non-class-member declaration cannot be static.");
229     auto memberDecl = dynamic_cast<ClassMemberDeclaration*>(decl);
230     if (!memberDecl)
231         return NOT_A_MEMBER;
232 
233     return compareValues(memberDecl->isStatic(), value, QStringLiteral("Declaration's isStatic"));
234 }
235 ///JSON type: bool
236 ///@returns whether the (class-member) declaration's isMutable matches the given value
DeclarationTest(isMutable)237 DeclarationTest(isMutable)
238 {
239     const QString NOT_A_MEMBER = QStringLiteral("Non-class-member declaration cannot be mutable.");
240     auto memberDecl = dynamic_cast<ClassMemberDeclaration*>(decl);
241     if (!memberDecl)
242         return NOT_A_MEMBER;
243 
244     return compareValues(memberDecl->isMutable(), value, QStringLiteral("Declaration's isMutable"));
245 }
246 ///JSON type: DeclTestObject
247 ///@returns whether the tests for the function declaration's definition pass
DeclarationTest(definition)248 DeclarationTest(definition)
249 {
250     KDevVarLengthArray<IndexedDeclaration> definitions = DUChain::definitions()->definitions(decl->id());
251     Declaration* declDef = nullptr;
252     if (!definitions.isEmpty())
253         declDef = definitions.at(0).declaration();
254     return testObject(declDef, value, QStringLiteral("Declaration's definition"));
255 }
256 ///JSON type: DeclTestObject
257 ///@returns whether the tests for the function definition's declaration pass
DeclarationTest(declaration)258 DeclarationTest(declaration)
259 {
260     auto* def = dynamic_cast<FunctionDefinition*>(decl);
261     Declaration* defDecl = nullptr;
262     if (def)
263         defDecl = def->declaration(decl->topContext());
264     return testObject(defDecl, value, QStringLiteral("Definition's declaration"));
265 }
266 ///JSON type: bool
267 ///@returns whether the declaration's nullity matches the given value
DeclarationTest(null)268 DeclarationTest(null)
269 {
270     return compareValues(decl == nullptr, value, QStringLiteral("Declaration's nullity"));
271 }
272 ///JSON type: bool
273 ///@returns whether the declaration's default parameter matches the given value
DeclarationTest(defaultParameter)274 DeclarationTest(defaultParameter)
275 {
276     const QString NOT_IN_FUNC_CTXT = QStringLiteral(
277         "Asked for a default parameter for a declaration outside of a function context.");
278     const QString OWNER_NOT_FUNC = QStringLiteral(
279         "Function context not owned by function declaration (what on earth did you do?).");
280     DUContext* context = decl->context();
281     if (!context || context->type() != DUContext::Function)
282         return NOT_IN_FUNC_CTXT;
283     auto* funcDecl = dynamic_cast<AbstractFunctionDeclaration*>(context->owner());
284     if (!funcDecl)
285         return OWNER_NOT_FUNC;
286     int argIndex = context->localDeclarations().indexOf(decl);
287     return compareValues(funcDecl->defaultParameterForArgument(argIndex).str(), value,
288                          QStringLiteral("Declaration's default parameter"));
289 }
290 
291 ///JSON type: string
292 ///@returns stringified declaration
DeclarationTest(toString)293 DeclarationTest(toString)
294 {
295     VERIFY_NOT_NULL(decl);
296     return compareValues(decl->toString(), value, QStringLiteral("Declaration's toString"));
297 }
298 
299 ///JSON type: string
300 ///@returns stringified declaration range
DeclarationTest(range)301 DeclarationTest(range)
302 {
303     VERIFY_NOT_NULL(decl);
304     return compareValues(rangeStr(decl->range()), value, QStringLiteral("Declaration's range"));
305 }
306 
307 ///JSON type: string
308 ///@returns stringified declaration kind
DeclarationTest(kind)309 DeclarationTest(kind)
310 {
311     VERIFY_NOT_NULL(decl);
312     QString kind;
313     switch (decl->kind()) {
314     case KDevelop::Declaration::Alias:
315         kind = QStringLiteral("Alias");
316         break;
317     case KDevelop::Declaration::Import:
318         kind = QStringLiteral("Import");
319         break;
320     case KDevelop::Declaration::Instance:
321         kind = QStringLiteral("Instance");
322         break;
323     case KDevelop::Declaration::Namespace:
324         kind = QStringLiteral("Namespace");
325         break;
326     case KDevelop::Declaration::NamespaceAlias:
327         kind = QStringLiteral("NamespaceAlias");
328         break;
329     case KDevelop::Declaration::Type:
330         kind = QStringLiteral("Type");
331         break;
332     case KDevelop::Declaration::Macro:
333         kind = QStringLiteral("Macro");
334         break;
335     }
336     return compareValues(kind, value, QStringLiteral("Declaration's kind"));
337 }
338 
339 ///JSON type: bool
340 ///@returns whether the declaration's isDeprecated matches the given value
DeclarationTest(isDeprecated)341 DeclarationTest(isDeprecated)
342 {
343     VERIFY_NOT_NULL(decl);
344     return compareValues(decl->isDeprecated(), value, QStringLiteral("Declaration's isDeprecated"));
345 }
346 
347 ///JSON type: bool
348 ///@returns whether the declaration's isDefinition matches the given value
DeclarationTest(isDefinition)349 DeclarationTest(isDefinition)
350 {
351     VERIFY_NOT_NULL(decl);
352     return compareValues(decl->isDefinition(), value, QStringLiteral("Declaration's isDefinition"));
353 }
354 
355 ///JSON type: bool
356 ///@returns whether the declaration's isExplicitlyTyped matches the given value
DeclarationTest(isExplicitlyTyped)357 DeclarationTest(isExplicitlyTyped)
358 {
359     VERIFY_NOT_NULL(decl);
360     return compareValues(decl->isExplicitlyTyped(), value, QStringLiteral("Declaration's isExplicitlyTyped"));
361 }
362 }
363 }
364 
365 #endif //KDEVPLATFORM_JSONDECLARATIONTESTS_H
366