• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

moosetechnology / VerveineJ / 28791819875

06 Jul 2026 12:33PM UTC coverage: 52.667% (+0.04%) from 52.626%
28791819875

push

github

web-flow
Merge pull request #266 from moosetechnology/fix/265-handle-MethodReference

Fix/265 handle method reference

2025 of 4016 branches covered (50.42%)

Branch coverage included in aggregate %.

12 of 15 new or added lines in 3 files covered. (80.0%)

4471 of 8318 relevant lines covered (53.75%)

2.22 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

75.21
/app/src/main/java/fr/inria/verveine/extractor/java/visitors/refvisitors/VisitorInvocRef.java
1
package fr.inria.verveine.extractor.java.visitors.refvisitors;
2

3
import fr.inria.verveine.extractor.java.EntityDictionary;
4
import fr.inria.verveine.extractor.java.VerveineJOptions;
5
import fr.inria.verveine.extractor.java.utils.NodeTypeChecker;
6
import fr.inria.verveine.extractor.java.utils.StubBinding;
7
import fr.inria.verveine.extractor.java.utils.Util;
8
import fr.inria.verveine.extractor.java.visitors.GetVisitedEntityAbstractVisitor;
9

10
import org.eclipse.jdt.core.dom.Initializer;
11
import org.eclipse.jdt.core.dom.Type;
12
import org.eclipse.jdt.core.dom.*;
13
import org.moosetechnology.model.famix.famixjavaentities.*;
14
import org.moosetechnology.model.famix.famixtraits.*;
15

16
import java.io.RandomAccessFile;
17
import java.util.ArrayList;
18
import java.util.Arrays;
19
import java.util.Collection;
20
import java.util.List;
21

22
public class VisitorInvocRef extends GetVisitedEntityAbstractVisitor {
23

24
        /**
25
         * Useful to keep the FamixType created in the specific case of "new
26
         * SomeClass().someMethod()"
27
         */
28
        private final org.moosetechnology.model.famix.famixjavaentities.Type classInstanceCreated = null;
3✔
29

30
        /**
31
         * The source code of the visited AST.
32
         * Used to find back the contents of non-javadoc comments
33
         */
34
        protected RandomAccessFile source;
35

36
        /**
37
         * Whether a variable access is lhs (write) or not
38
         */
39
        protected boolean inAssignmentLHS = false;
3✔
40

41
        public VisitorInvocRef(EntityDictionary dico, VerveineJOptions options) {
42
                super(dico, options);
4✔
43
        }
1✔
44

45
        // VISITOR METHODS
46

47
        public boolean visit(CompilationUnit node) {
48
                //System.err.println("visit(CompilationUnit) ");
49
                visitCompilationUnit(node);
4✔
50
                return super.visit(node);
4✔
51
        }
52

53
        public void endVisit(CompilationUnit node) {
54
                //System.err.println("endVisit(CompilationUnit) ");
55
                endVisitCompilationUnit(node);
3✔
56
        }
1✔
57

58
        /*
59
         * Can only be a class or interface declaration
60
         * Local type: see comment of visit(ClassInstanceCreation node)
61
         */
62
        public boolean visit(TypeDeclaration node) {
63
                //System.err.println("visit(TypeDeclaration) ");
64
                if (visitTypeDeclaration(node) != null) {
4!
65
                        return super.visit(node);
4✔
66
                } else {
67
                        return false;
×
68
                }
69
        }
70

71
        public void endVisit(TypeDeclaration node) {
72
                //System.err.println("endVisit(TypeDeclaration) ");
73
                endVisitTypeDeclaration(node);
3✔
74
        }
1✔
75

76
        /**
77
         * Creates an invocation to the constructor of the class
78
         *
79
         * <pre>
80
         * ClassInstanceCreation ::=
81
         *         [ Expression . ]
82
         *             new [ &lt; Type { , Type } &gt; ]
83
         *             Type ( [ Expression { , Expression } ] )
84
         *             [ AnonymousClassDeclaration ]
85
         * </pre>
86
         */
87
        @SuppressWarnings("unchecked")
88
        public boolean visit(ClassInstanceCreation node) {
89
                //System.err.println("visit(ClassInstanceCreation) ");
90
                String typName;
91
                TType fmx;
92

93
                possiblyAnonymousClassDeclaration(node);
3✔
94

95
                if (node.getAnonymousClassDeclaration() != null) {
3✔
96
                        ITypeBinding bnd = (ITypeBinding) StubBinding.getDeclarationBinding(node.getAnonymousClassDeclaration());
5✔
97
                        fmx = this.dico.getFamixClass(bnd, Util.stringForAnonymousName(getAnonymousSuperTypeName(), context),
12✔
98
                                        /* owner */(ContainerEntity) context.top());
2✔
99
                        typName = fmx.getName();
3✔
100
                } else {
1✔
101
                        Type clazz = node.getType();
3✔
102
                        fmx = dico.referredType(clazz, (ContainerEntity) context.top(), true);
10✔
103

104
                        // create an invocation to the constructor
105
                        if (fmx == null) {
2!
NEW
106
                                typName = dico.findTypeName(clazz);
×
107
                        } else {
108
                                typName = fmx.getName();
3✔
109
                        }
110
                }
111

112
                methodInvocation(node.resolveConstructorBinding(), typName, /* receiver */null, /* methOwner */fmx,
9✔
113
                                (List<Expression>) node.arguments());
1✔
114
                Invocation lastInvocation = (Invocation) context.getLastInvocation();
5✔
115
                if (options.withAnchors(VerveineJOptions.AnchorOptions.assoc)
8!
116
                                && (lastInvocation != null)
117
                                && (lastInvocation.getSender() == context.topMethod())
6!
118
                                && (lastInvocation.getReceiver() == null)
3!
119
                                && (lastInvocation.getSignature().startsWith(typName))) {
4!
120
                        dico.addSourceAnchor(lastInvocation, node);
6✔
121
                }
122
                return super.visit(node);
4✔
123
        }
124

125
        public boolean visit(AnonymousClassDeclaration node) {
126
                //System.err.println("visit(AnonymousClassDeclaration) ");
127
                if (visitAnonymousClassDeclaration(node) != null) {
4!
128
                        return super.visit(node);
4✔
129
                } else {
130
                        return false;
×
131
                }
132
        }
133

134
        public void endVisit(AnonymousClassDeclaration node) {
135
                //System.err.println("endVisit(AnonymousClassDeclaration) ");
136
                endVisitAnonymousClassDeclaration(node);
3✔
137
        }
1✔
138

139
        public boolean visit(EnumDeclaration node) {
140
                //System.err.println("visit(EnumDeclaration) ");
141
                if (visitEnumDeclaration(node) != null) {
4!
142
                        return super.visit(node);
4✔
143
                } else {
144
                        return false;
×
145
                }
146
        }
147

148
        public void endVisit(EnumDeclaration node) {
149
                //System.err.println("endVisit(EnumDeclaration) ");
150
                endVisitEnumDeclaration(node);
3✔
151
        }
1✔
152

153
        public boolean visit(AnnotationTypeDeclaration node) {
154
                //System.err.println("visit(AnnotationTypeDeclaration) ");
155
                if (visitAnnotationTypeDeclaration(node) != null) {
4!
156
                        return super.visit(node);
4✔
157
                } else {
158
                        return false;
×
159
                }
160
        }
161

162
        public void endVisit(AnnotationTypeDeclaration node) {
163
                //System.err.println("endVisit(AnnotationTypeDeclaration) ");
164
                endVisitAnnotationTypeDeclaration(node);
3✔
165
        }
1✔
166

167
        public boolean visit(AnnotationTypeMemberDeclaration node) {
168
                //System.err.println("visit(AnnotationTypeMemberDeclaration) ");
169
                if (visitAnnotationTypeMemberDeclaration(node) != null) {
4!
170
                        return super.visit(node);
4✔
171
                } else {
172
                        return false;
×
173
                }
174
        }
175

176
        public void endVisit(AnnotationTypeMemberDeclaration node) {
177
                //System.err.println("endVisit(AnnotationTypeMemberDeclaration) ");
178
                this.context.popAnnotationMember();
4✔
179
                super.endVisit(node);
3✔
180
        }
1✔
181

182
        public boolean visit(MethodDeclaration node) {
183
                //System.err.println("visit(MethodDeclaration): " + node.getName().getIdentifier());
184
                TMethod fmx = visitMethodDeclaration(node);
4✔
185

186
                if (fmx != null) {
2!
187
                        if (node.getBody() != null) {
3✔
188
                                context.setLastInvocation(null);
4✔
189
                        }
190
                        return super.visit(node);
4✔
191
                } else {
192
                        return false;
×
193
                }
194
        }
195

196
        @Override
197
        public void endVisit(MethodDeclaration node) {
198
                //System.err.println("endVisit(MethodDeclaration) ");
199
                endVisitMethodDeclaration(node);
3✔
200
        }
1✔
201

202
        @Override
203
        public boolean visit(Initializer node) {
204
                //System.err.println("visit(Initializer) ");
205
                if (visitInitializer(node) != null) {
4!
206
                        return super.visit(node);
4✔
207
                } else {
208
                        return false;
×
209
                }
210
        }
211

212
        @Override
213
        public void endVisit(Initializer node) {
214
                //System.err.println("endVisit(Initializer) ");
215
                endVisitInitializer(node);
3✔
216
        }
1✔
217

218
        /**
219
         * FieldDeclaration ::=
220
         * [Javadoc] { ExtendedModifier } Type VariableDeclarationFragment
221
         * { , VariableDeclarationFragment } ;
222
         */
223
        @Override
224
        public boolean visit(FieldDeclaration node) {
225
                hasInitBlock(node); // to recover optional EntityDictionary.INIT_BLOCK_NAME method
4✔
226
                return true;
2✔
227
        }
228

229
        @Override
230
        public void endVisit(FieldDeclaration node) {
231
                endVisitFieldDeclaration(node);
3✔
232
        }
1✔
233

234
        public boolean visit(EnumConstantDeclaration node) {
235
        return visitEnumConstantDeclaration(node);
4✔
236
        }
237

238
        public void endVisit(EnumConstantDeclaration node) {
239
                endVisitEnumConstantDeclaration(node);
3✔
240
        }
1✔
241

242
        @SuppressWarnings("unchecked")
243
        public boolean visit(MethodInvocation node) {
244
                //System.err.println("visit(MethodInvocation): " + node.getName().getFullyQualifiedName());
245
                Expression callingExpr = node.getExpression();
3✔
246
                TNamedEntity receiver = getReceiver(callingExpr);
4✔
247

248
                IMethodBinding bnd = node.resolveMethodBinding();
3✔
249

250
                String calledName = node.getName().getFullyQualifiedName();
4✔
251

252
                if (bnd == null) {
2✔
253
                        methodInvocation(bnd, calledName, receiver, getInvokedMethodOwner(callingExpr, receiver), node.arguments());
13✔
254
                } else {
255
                        methodInvocation(bnd, calledName, receiver, /* owner */null, node.arguments());
9✔
256
                }
257

258
                // TODO could be TInvocation but it does not extends THassignature and we need
259
                // it a bit latter (see 'lastInvok.getSignature()')
260
                Invocation lastInvocation = (Invocation) context.getLastInvocation();
5✔
261
                if (options.withAnchors(VerveineJOptions.AnchorOptions.assoc)
8!
262
                                // check that lastInvocation correspond to current one
263
                                && (lastInvocation != null) && (lastInvocation.getSender() == context.topMethod())
6!
264
                                && (lastInvocation.getReceiver() == receiver)
4!
265
                                && (lastInvocation.getSignature().startsWith(calledName))) {
4!
266
                        dico.addSourceAnchor(lastInvocation, node);
6✔
267
                }
268

269
                return super.visit(node);
4✔
270
        }
271

272
        public boolean visit(ExpressionMethodReference node) {
273
                IMethodBinding bnd = node.resolveMethodBinding();
3✔
274
                Expression callingExpr = node.getExpression();
3✔
275

276
                methodInvocation(bnd, node.getName().getFullyQualifiedName(),null, getInvokedMethodOwner(callingExpr, null), null);
13✔
277
                return super.visit(node);
4✔
278
        }
279

280
        @SuppressWarnings("unchecked")
281
        public boolean visit(SuperMethodInvocation node) {
282
                //System.err.println("visit(SuperMethodInvocation) ");
283
                TNamedEntity receiver = this.dico.ensureFamixImplicitVariable(
7✔
284
                                EntityDictionary.SUPER_NAME,
285
                                this.context.topType(),
3✔
286
                                context.topMethod());
1✔
287
                IMethodBinding bnd = node.resolveMethodBinding();
3✔
288
                String calledName = node.getName().getFullyQualifiedName();
4✔
289

290
                if (bnd == null) {
2✔
291
                        TType superClass = (TType) ((TWithInheritances) this.context.topType()).getSuperInheritances().iterator().next().getSuperclass();
11✔
292
                        methodInvocation(bnd, calledName, receiver, superClass, node.arguments());
9✔
293
                } else {
1✔
294
                        methodInvocation(bnd, calledName, receiver, /* owner */null, node.arguments());
9✔
295
                }
296

297
                Invocation lastInvok = (Invocation) context.getLastInvocation();
5✔
298
                if (options.withAnchors(VerveineJOptions.AnchorOptions.assoc)
5!
299
                                // check that lastInvocation correspond to current one
300
                                && (lastInvok != null) && (lastInvok.getSender() == context.topMethod())
×
301
                                && (lastInvok.getReceiver() == receiver) && (lastInvok.getSignature().startsWith(calledName))) {
×
302
                        dico.addSourceAnchor(lastInvok, node);
×
303
                }
304

305
                return super.visit(node);
4✔
306
        }
307

308
        public boolean visit(ConstructorInvocation node) {
309
                //System.err.println("visit(ConstructorInvocation) ");
310
                // ConstructorInvocation (i.e. 'this(...)' ) happen in constructor, so the name
311
                // is the same
312

313
                int modifiers = (node.resolveConstructorBinding() != null) ? node.resolveConstructorBinding().getModifiers()
7!
314
                                : EntityDictionary.UNKNOWN_MODIFIERS;
1✔
315

316
                String name = context.topMethod().getName();
5✔
317
                TMethod invoked = dico.ensureFamixMethod(node.resolveConstructorBinding(), name,
11✔
318
                                /* paramTypes */null, /* retType */null, (TWithMethods) /* owner */context.topType(), modifiers);
3✔
319
                // constructor don't have return type so no need to create a reference from this
320
                // class to the "declared return type" class when classSummary is TRUE
321
                // also no parameters specified here, so no references to create for them either
322

323
                String signature = node.toString();
3✔
324
                if (signature.endsWith("\n")) {
4!
325
                        signature = signature.substring(0, signature.length() - 1);
8✔
326
                }
327
                if (signature.endsWith(";")) {
4!
328
                        signature = signature.substring(0, signature.length() - 1);
8✔
329
                }
330
                ImplicitVariable receiver = dico.ensureFamixImplicitVariable(
7✔
331
                                EntityDictionary.THIS_NAME,
332
                                context.topType(),
3✔
333
                                context.topMethod());
1✔
334

335
                TInvocation invok = dico.addFamixInvocation(context.topMethod(), invoked, receiver, signature,
12✔
336
                                context.getLastInvocation(), node.resolveConstructorBinding());
3✔
337
                context.setLastInvocation(invok);
4✔
338

339
                if (options.withAnchors(VerveineJOptions.AnchorOptions.assoc) && (invok != null)) {
5!
340
                        dico.addSourceAnchor(invok, node);
×
341
                }
342

343
                return super.visit(node);
4✔
344
        }
345

346
        public boolean visit(SuperConstructorInvocation node) {
347
                //System.err.println("visit(SuperConstructorInvocation) ");
348
                // ConstructorInvocation (i.e. 'super(...)' ) happen in constructor, so the name
349
                // is that of the superclass
350
                // Class superC = superClass();
351
                Method invoked = null;
2✔
352

353
                // if (superC != null) {
354
                // invoked = this.dico.ensureFamixMethod(node.resolveConstructorBinding(),
355
                // superC.getName(), /*paramsType*/(Collection<String>) null, superC,
356
                // EntityDictionary.UNKNOWN_MODIFIERS, /*persistIt*/!classSummary);
357
                // }
358
                // else {
359
                invoked = this.dico.ensureFamixMethod(node.resolveConstructorBinding());
6✔
360
                // }
361

362
                if (invoked != null) {
2✔
363
                        String signature = node.toString();
3✔
364
                        if (signature.endsWith("\n")) {
4!
365
                                signature = signature.substring(0, signature.length() - 1);
8✔
366
                        }
367
                        if (signature.endsWith(";")) {
4!
368
                                signature = signature.substring(0, signature.length() - 1);
8✔
369
                        }
370
                        ImplicitVariable receiver = dico.ensureFamixImplicitVariable(
7✔
371
                                        EntityDictionary.SUPER_NAME,
372
                                        context.topType(),
3✔
373
                                        context.topMethod());
1✔
374
                        Invocation invok = dico.addFamixInvocation(context.topMethod(), invoked, receiver, signature,
12✔
375
                                        context.getLastInvocation(), node.resolveConstructorBinding());
3✔
376
                        context.setLastInvocation(invok);
4✔
377
                        if (options.withAnchors(VerveineJOptions.AnchorOptions.assoc)) {
5!
378
                                dico.addSourceAnchor(invok, node);
×
379
                        }
380
                }
381

382
                return super.visit(node);
4✔
383
        }
384

385
        // UTILITY METHODS
386

387
        /**
388
         * Handles an invocation of a method by creating the corresponding Famix Entity.
389
         *
390
         * @param calledBnd  -- a binding for the method invoked
391
         * @param calledName of the method invoked
392
         * @param receiver   of the call, i.e. the object to which the message is sent
393
         * @param methOwner  -- owner of the method invoked. Might be a subtype of the
394
         *                   receiver's type
395
         * @param l_args     -- list of the method's parameters
396
         *                   TODO Why are Invocations, Accesses and References not
397
         *                   created through a method in JavaDictionnary ?
398
         */
399
        private Invocation methodInvocation(IMethodBinding calledBnd, String calledName, TNamedEntity receiver,
400
                        TType methOwner, Collection<Expression> l_args) {
401
                //System.err.println("methodInvocation(): " + calledName);
402

403
                TMethod sender = this.context.topMethod();
4✔
404
                TMethod invoked;
405
                Invocation invok;
406

407
                // If the method is parametric, get the generic method.
408
                IMethodBinding actualCalledMethodBnd = (calledBnd == null ? null : calledBnd.getMethodDeclaration());
7✔
409

410
                if ((receiver != null) && (receiver.getName().equals("class")) && (actualCalledMethodBnd != null)
10!
411
                                && (actualCalledMethodBnd.getDeclaringClass() == null)) {
2!
412
                        /* bug with JDT apparently has to do with invoking a method of a meta-class */
413
                        // humm ... we do not create the FamixInvocation ? Seems like a bug ...
414
                        return null;
×
415
                } else if ((actualCalledMethodBnd != null) && (actualCalledMethodBnd.isAnnotationMember())) {
5✔
416
                        // if this is not an AnnotationType member, it is similar to creating a
417
                        // FamixAttribute access
418
                        return null;
2✔
419
                } else if (sender == null) {
2!
420
                        return null;
×
421
                }
422

423
                Collection<String> unkwnArgs = new ArrayList<String>();
4✔
424
                if (l_args != null) {
2✔
425
                        for (@SuppressWarnings("unused")
426
                        Expression a : l_args) {
10✔
427
                                unkwnArgs.add("?");
4✔
428
                        }
1✔
429
                }
430

431
                int modifiers = (actualCalledMethodBnd != null) ? actualCalledMethodBnd.getModifiers() : EntityDictionary.UNKNOWN_MODIFIERS;
7✔
432

433
                if ((receiver != null) && (receiver instanceof TStructuralEntity)) {
5✔
434
                        invoked = this.dico.ensureFamixMethod(actualCalledMethodBnd, calledName, unkwnArgs, /* retType */null,
12✔
435
                                        (TWithMethods) methOwner, modifiers);
436
                } else {
437
                        TType owner;
438

439
                        if (receiver != null)
2✔
440
                                owner = (TType) receiver;
4✔
441
                        else {
442
                                if (actualCalledMethodBnd != null && actualCalledMethodBnd.getDeclaringClass().isParameterizedType()) {
6!
443
                                        owner = this.dico.ensureFamixType(actualCalledMethodBnd.getDeclaringClass().getErasure());
×
444
                                } else {
445
                                        owner = methOwner;
2✔
446
                                }
447
                        }
448

449
                        // Implicit constructor
450
                        if (owner != null && calledName.equals(owner.getName())) {
7✔
451
                                List<String> parameterTypesNames = new ArrayList<>();
4✔
452
                                if (calledBnd != null) {
2✔
453
                                        parameterTypesNames = Arrays.stream(calledBnd.getParameterTypes()).map(ITypeBinding::getName).toList();
7✔
454
                                }
455
                                
456
                                invoked = dico.ensureImplicitConstructor(calledBnd, (TWithMethods) owner, calledName, parameterTypesNames );
9✔
457
                        } else {
1✔
458
                                // static method called on the class (or null receiver)
459
                                invoked = this.dico.ensureFamixMethod(actualCalledMethodBnd, calledName, unkwnArgs, /* retType */null,
11✔
460
                                                (TWithMethods) /* owner */owner, modifiers);
461
                        }
462
                }
463

464
                String signature = "";
2✔
465
                if (actualCalledMethodBnd != null && actualCalledMethodBnd.isParameterizedMethod()) {
5!
466
                        signature += "<";
×
467
                        int size = ((ParametricMethod) invoked).getTypeParameters().size();
×
468
                        int i = 0;
×
469
                        for (TTypeParameter param : ((ParametricMethod) invoked).getTypeParameters()) {
×
470
                                signature += ((TNamedEntity) param).getName() + (i < size - 1 ? "," : "");
×
471
                                i++;
×
472
                        }
×
473
                        signature += "> ";
×
474
                }
475
                signature += calledName + "(";
4✔
476

477
                if (l_args != null) {
2✔
478
                        boolean first = true;
2✔
479
                        for (Expression a : l_args) {
10✔
480
                                if (first) {
2✔
481
                                        signature += a.toString();
5✔
482
                                        first = false;
3✔
483
                                } else {
484
                                        signature += "," + a.toString();
5✔
485
                                }
486
                        }
1✔
487
                }
488
                signature += ")";
3✔
489
                
490
                invok = dico.addFamixInvocation(sender, invoked, (TInvocationsReceiver) receiver, signature,
11✔
491
                                context.getLastInvocation(), calledBnd);
2✔
492
                // TODO add FileAnchor to Invocation
493
                context.setLastInvocation(invok);
4✔
494

495
                return invok;
2✔
496

497
        }
498

499
        /**
500
         * Finds and/or create the Famix Entity receiving a message
501
         * Can be: ImplicitVariable (this, super), GlobalVariable, LocalVariable,
502
         * Attribute, UnknownVariable, Parameter
503
         * 
504
         * @param expr -- the Java expression describing the receiver
505
         * @return the Famix Entity or null if could not find it
506
         */
507
        @SuppressWarnings("static-access")
508
        private TNamedEntity getReceiver(Expression expr) {
509
                // msg(), same as ThisExpression
510
                if (expr == null) {
2✔
511
                        return this.dico.ensureFamixImplicitVariable(dico.THIS_NAME, this.context.topType(), context.topMethod());
14✔
512
                }
513

514
                // array[i].msg()
515
                if (NodeTypeChecker.isArrayAccess(expr)) {
3!
516
                        return getReceiver(((ArrayAccess) expr).getArray());
×
517
                }
518

519
                // new type[].msg()
520
                if (NodeTypeChecker.isArrayCreation(expr)) {
3!
521
                        return null;
×
522
                }
523

524
                // (variable = value).msg()
525
                if (NodeTypeChecker.isAssignment(expr)) {
3!
526
                        return getReceiver(((Assignment) expr).getLeftHandSide());
×
527
                }
528

529
                // ((type)expr).msg()
530
                if (NodeTypeChecker.isCastExpression(expr)) {
3!
531
                        return getReceiver(((CastExpression) expr).getExpression());
×
532
                }
533

534
                // new Class().msg()
535
                if (NodeTypeChecker.isClassInstanceCreation(expr)) {
3✔
536
                        return null;
2✔
537
                }
538

539
                // (cond-expr ? then-expr : else-expr).msg()
540
                if (NodeTypeChecker.isConditionalExpression(expr)) {
3!
541
                        // can be one or the other (then-expr/else-expr) so we choose one
542
                        TNamedEntity ret = getReceiver(((ConditionalExpression) expr).getThenExpression());
×
543
                        if (ret == null) {
×
544
                                // can as well try the other
545
                                ret = getReceiver(((ConditionalExpression) expr).getElseExpression());
×
546
                        }
547
                        return ret;
×
548
                }
549

550
                // field.msg()
551
                if (NodeTypeChecker.isFieldAccess(expr)) {
3✔
552
                        IVariableBinding bnd = ((FieldAccess) expr).resolveFieldBinding();
4✔
553
                        return dico.getEntityByKey(bnd);
5✔
554
                }
555

556
                // (left-expr oper right-expr).msg()
557
                if (NodeTypeChecker.isInfixExpression(expr)) {
3!
558
                        // anonymous receiver
559
                        return null;
×
560
                }
561

562
                // msg1().msg()
563
                if (NodeTypeChecker.isMethodInvocation(expr)) {
3✔
564
                        return null;
2✔
565
                }
566

567
                // name.msg()
568
                if (NodeTypeChecker.isName(expr)) {
3✔
569
                        // can be a class or a variable name
570
                        IBinding bnd = ((Name) expr).resolveBinding();
4✔
571
                        if (bnd == null) {
2✔
572
                                return null;
2✔
573
                        }
574
                        TNamedEntity ret = null;
2✔
575
                        if (bnd.getKind() == IBinding.TYPE) {
4✔
576
                                // msg() is a static method of Name so name should be a class, except if its an
577
                                // Enum
578
                                ret = dico.getEntityByKey(bnd);
5✔
579
                        }
580

581
                        if (bnd.getKind() == IBinding.VARIABLE) {
4✔
582
                                // a bit convoluted, but sometimes 'bnd' is not directly the binding of the
583
                                // variable's declaration from which the Famix entity was created
584
                                return dico.getEntityByKey(((IVariableBinding) bnd).getVariableDeclaration());
7✔
585
                        }
586

587
                        return ret;
2✔
588
                }
589

590
                // (expr).msg()
591
                if (NodeTypeChecker.isParenthesizedExpression(expr)) {
3!
592
                        return getReceiver(((ParenthesizedExpression) expr).getExpression());
×
593
                }
594

595
                // "string".msg()
596
                if (NodeTypeChecker.isStringLiteral(expr)) {
3✔
597
                        return null;
2✔
598
                }
599

600
                // <text block>.msg()
601
                // <text block> are created with triple quotes to allow defining multi-line string literals
602
                // they used to be reported as NullLiteral in JDT
603
                // In more modern version there is a Node type for them
604
                if (expr instanceof NullLiteral || ( expr.getNodeType() == ASTNode.TEXT_BLOCK) ) {
7!
605
                        return null;
2✔
606
                }
607

608
                // super.field.msg()
609
                if (NodeTypeChecker.isSuperFieldAccess(expr)) {
3!
610
                        return dico.getEntityByKey(((SuperFieldAccess) expr).resolveFieldBinding());
×
611
                }
612

613
                // super.msg1().msg()
614
                if (NodeTypeChecker.isSuperMethodInvocation(expr)) {
3!
615
                        return null;
×
616
                }
617

618
                // this.msg()
619
                if (NodeTypeChecker.isThisExpression(expr)) {
3✔
620
                        return this.dico.ensureFamixImplicitVariable(EntityDictionary.THIS_NAME, context.topType(),
10✔
621
                                        context.topMethod());
1✔
622
                }
623

624
                // type.class.msg()
625
                if (NodeTypeChecker.isTypeLiteral(expr)) {
3!
626
                        // similar to a field access
627
                        return dico.getFamixAttribute(null, "class", dico.ensureFamixMetaClass(null));
10✔
628
                }
629

630
                // ... OTHER POSSIBLE EXPRESSIONS ?
631
                System.err.println("WARNING: Unexpected receiver expression: " + expr.getClass().getName()
×
632
                                + " (method called is " + expr.getClass().getName() + ".aMethod(...))");
×
633
                return null;
×
634
        }
635

636
        /**
637
         * Tries its best to find the type of a receiver without using the bindings.
638
         * Most of the time, the type is that of the receiver, but not always (if there
639
         * is a cast or if receiver is null)
640
         *
641
         * @param expr     -- the Java expression describing the receiver
642
         * @param receiver -- the FAMIX Entity describing the receiver
643
         * @return the Famix Entity or null if could not find it
644
         */
645
        private TType getInvokedMethodOwner(Expression expr, TNamedEntity receiver) {
646
                // ((type)expr).msg()
647
                if (NodeTypeChecker.isCastExpression(expr)) {
3!
648
                        Type tcast = ((CastExpression) expr).getType();
×
NEW
649
                        return dico.referredType(tcast, (ContainerEntity) this.context.top(), true);
×
650
                }
651

652
                // new Class().msg()
653
                else if (NodeTypeChecker.isClassInstanceCreation(expr)) {
3!
654
                        return this.classInstanceCreated;
×
655
                }
656

657
                // msg1().msg()
658
                else if (NodeTypeChecker.isMethodInvocation(expr)) {
3✔
659
                        IMethodBinding callerBnd = ((MethodInvocation) expr).resolveMethodBinding();
4✔
660
                        if (callerBnd != null) {
2✔
661
                                return dico.referredType(callerBnd.getReturnType(), (ContainerEntity) this.context.top());
10✔
662
                        } else {
663
                                return null;
2✔
664
                        }
665
                }
666

667
                // (expr).msg()
668
                else if (NodeTypeChecker.isParenthesizedExpression(expr)) {
3!
669
                        return getInvokedMethodOwner(((ParenthesizedExpression) expr).getExpression(), receiver);
×
670
                }
671

672
                // "string".msg()
673
                else if (NodeTypeChecker.isStringLiteral(expr)) {
3!
674
                        return dico.ensureFamixType(/* binding */null, "String", dico.ensureFamixPackageJavaLang(null),
×
675
                                        /* context */null, EntityDictionary.UNKNOWN_MODIFIERS); // creating FamixClass java.lang.String
676
                }
677

678
                // this.msg() occurs for example with a MethodReference: 'this::msg'
679
                else if (NodeTypeChecker.isThisExpression(expr)) {
3✔
680
                        return context.topType();
4✔
681
                }
682

683
                // super.msg1().msg()
684
                else if (NodeTypeChecker.isSuperMethodInvocation(expr)) {
3!
685
                        IMethodBinding superBnd = ((SuperMethodInvocation) expr).resolveMethodBinding();
×
686
                        if (superBnd != null) {
×
NEW
687
                                return dico.referredType(superBnd.getReturnType(), (ContainerEntity) context.topType());
×
688
                        } else {
689
                                return null;
×
690
                        }
691
                }
692

693
                // everything else, see the receiver
694
                else {
695
                        if (receiver == null) {
2✔
696
                                return null;
2✔
697
                        }
698
                        else if (receiver instanceof TTypedEntity) {
3!
699
                                return ((TTypedEntity) receiver).getDeclaredType();
4✔
700
                        } else if (receiver instanceof org.moosetechnology.model.famix.famixjavaentities.Type) {
×
701
                                return (org.moosetechnology.model.famix.famixjavaentities.Type) receiver;
×
702
                        }
703
                        // ... what else ?
704
                        else {
705
                                return null;
×
706
                        }
707
                }
708
        }
709

710
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc