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

pmd / pmd / #3722

pending completion
#3722

push

github actions

adangel
Suppress MissingOverride for Chars::isEmpty (#4291)

67270 of 127658 relevant lines covered (52.7%)

0.53 hits per line

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

89.25
/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/ClassStub.java
1
/*
2
 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3
 */
4

5
package net.sourceforge.pmd.lang.java.symbols.internal.asm;
6

7
import java.io.IOException;
8
import java.io.InputStream;
9
import java.util.ArrayList;
10
import java.util.Collections;
11
import java.util.List;
12
import java.util.Objects;
13
import java.util.regex.Pattern;
14

15
import org.checkerframework.checker.nullness.qual.NonNull;
16
import org.checkerframework.checker.nullness.qual.Nullable;
17
import org.objectweb.asm.ClassReader;
18
import org.objectweb.asm.Opcodes;
19
import org.pcollections.HashTreePSet;
20
import org.pcollections.PSet;
21

22
import net.sourceforge.pmd.lang.java.symbols.JClassSymbol;
23
import net.sourceforge.pmd.lang.java.symbols.JConstructorSymbol;
24
import net.sourceforge.pmd.lang.java.symbols.JElementSymbol;
25
import net.sourceforge.pmd.lang.java.symbols.JExecutableSymbol;
26
import net.sourceforge.pmd.lang.java.symbols.JFieldSymbol;
27
import net.sourceforge.pmd.lang.java.symbols.JMethodSymbol;
28
import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol;
29
import net.sourceforge.pmd.lang.java.symbols.JTypeParameterOwnerSymbol;
30
import net.sourceforge.pmd.lang.java.symbols.SymbolicValue;
31
import net.sourceforge.pmd.lang.java.symbols.SymbolicValue.SymAnnot;
32
import net.sourceforge.pmd.lang.java.symbols.internal.SymbolEquality;
33
import net.sourceforge.pmd.lang.java.symbols.internal.asm.ExecutableStub.CtorStub;
34
import net.sourceforge.pmd.lang.java.symbols.internal.asm.ExecutableStub.MethodStub;
35
import net.sourceforge.pmd.lang.java.symbols.internal.asm.GenericSigBase.LazyClassSignature;
36
import net.sourceforge.pmd.lang.java.types.JClassType;
37
import net.sourceforge.pmd.lang.java.types.JTypeVar;
38
import net.sourceforge.pmd.lang.java.types.LexicalScope;
39
import net.sourceforge.pmd.lang.java.types.Substitution;
40
import net.sourceforge.pmd.lang.java.types.TypeSystem;
41
import net.sourceforge.pmd.util.CollectionUtil;
42

43

44
final class ClassStub implements JClassSymbol, AsmStub, AnnotationOwner {
1✔
45

46
    static final int UNKNOWN_ARITY = 0;
47

48
    private final AsmSymbolResolver resolver;
49

50
    private final Names names;
51

52
    // all the following are lazy and depend on the parse lock
53

54
    private int accessFlags;
55

56
    private EnclosingInfo enclosingInfo;
57
    private LazyClassSignature signature;
58
    private LexicalScope scope;
59

60
    private List<JFieldSymbol> fields = new ArrayList<>();
1✔
61
    private List<JClassSymbol> memberClasses = new ArrayList<>();
1✔
62
    private List<JMethodSymbol> methods = new ArrayList<>();
1✔
63
    private List<JConstructorSymbol> ctors = new ArrayList<>();
1✔
64
    private List<JFieldSymbol> enumConstants = null;
1✔
65

66
    private PSet<SymAnnot> annotations = HashTreePSet.empty();
1✔
67

68
    private PSet<String> annotAttributes;
69

70
    private final ParseLock parseLock;
71

72
    /** Note that '.' is forbidden because in internal names they're replaced by slashes '/'. */
73
    private static final Pattern INTERNAL_NAME_FORBIDDEN_CHARS = Pattern.compile("[;<>\\[.]");
1✔
74

75
    private static boolean isValidInternalName(String internalName) {
76
        return !internalName.isEmpty() && !INTERNAL_NAME_FORBIDDEN_CHARS.matcher(internalName).find();
1✔
77
    }
78

79
    ClassStub(AsmSymbolResolver resolver, String internalName, @NonNull Loader loader, int observedArity) {
1✔
80
        assert isValidInternalName(internalName) : internalName;
1✔
81

82
        this.resolver = resolver;
1✔
83
        this.names = new Names(internalName);
1✔
84

85
        this.parseLock = new ParseLock() {
1✔
86
            // note to devs: to debug the parsing logic you might have
87
            // to replace the implementation of toString temporarily,
88
            // otherwise an IDE could call toString just to show the item
89
            // in the debugger view (which could cause parsing of the class file).
90

91
            @Override
92
            protected boolean doParse() throws IOException {
93
                try (InputStream instream = loader.getInputStream()) {
1✔
94
                    if (instream != null) {
1✔
95
                        ClassReader classReader = new ClassReader(instream);
1✔
96
                        ClassStubBuilder builder = new ClassStubBuilder(ClassStub.this, resolver);
1✔
97
                        classReader.accept(builder, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
1✔
98
                        return true;
1✔
99
                    } else {
100
                        return false;
1✔
101
                    }
102
                }
1✔
103
            }
104

105
            @Override
106
            protected void finishParse(boolean failed) {
107
                if (enclosingInfo == null) {
1✔
108
                    // this may be normal
109
                    enclosingInfo = EnclosingInfo.NO_ENCLOSING;
1✔
110
                }
111
                if (signature == null) {
1✔
112
                    assert failed : "No signature, but the parse hasn't failed? investigate";
1✔
113
                    signature = LazyClassSignature.defaultWhenUnresolved(ClassStub.this, observedArity);
1✔
114
                }
115
                methods = Collections.unmodifiableList(methods);
1✔
116
                ctors = Collections.unmodifiableList(ctors);
1✔
117
                fields = Collections.unmodifiableList(fields);
1✔
118
                memberClasses = Collections.unmodifiableList(memberClasses);
1✔
119
                enumConstants = CollectionUtil.makeUnmodifiableAndNonNull(enumConstants);
1✔
120

121
                if (EnclosingInfo.NO_ENCLOSING.equals(enclosingInfo)) {
1✔
122
                    if (names.canonicalName == null || names.simpleName == null) {
1✔
123
                        // This happens if the simple name contains dollars,
124
                        // in which case we might have an enclosing class, and
125
                        // we can only tell now (no enclosingInfo) that that's
126
                        // not the case.
127
                        names.finishOuterClass();
1✔
128
                    }
129
                }
130
                annotAttributes = (accessFlags & Opcodes.ACC_ANNOTATION) != 0
1✔
131
                                  ? getDeclaredMethods().stream().filter(JMethodSymbol::isAnnotationAttribute)
1✔
132
                                                        .map(JElementSymbol::getSimpleName)
1✔
133
                                                        .collect(CollectionUtil.toPersistentSet())
1✔
134
                                  : HashTreePSet.empty();
1✔
135
            }
1✔
136

137
            @Override
138
            protected boolean postCondition() {
139
                return signature != null && enclosingInfo != null;
1✔
140
            }
141
        };
142
    }
1✔
143

144
    @Override
145
    public AsmSymbolResolver getResolver() {
146
        return resolver;
1✔
147
    }
148

149
    // <editor-fold  defaultstate="collapsed" desc="Setters used during loading">
150

151
    void setHeader(@Nullable String signature,
152
                   @Nullable String superName,
153
                   String[] interfaces) {
154
        this.signature = new LazyClassSignature(this, signature, superName, interfaces);
1✔
155
    }
1✔
156

157
    /**
158
     * Called if this is an inner class (their simple name cannot be
159
     * derived from splitting the internal/binary name on dollars, as
160
     * the simple name may itself contain dollars).
161
     */
162
    void setSimpleName(String simpleName) {
163
        this.names.simpleName = simpleName;
1✔
164
    }
1✔
165

166
    void setModifiers(int accessFlags, boolean fromClassInfo) {
167
        /*
168
            A different set of modifiers is contained in the ClassInfo
169
            structure and the InnerClasses structure. See
170
            https://docs.oracle.com/javase/specs/jvms/se13/html/jvms-4.html#jvms-4.1-200-E.1
171
            https://docs.oracle.com/javase/specs/jvms/se13/html/jvms-4.html#jvms-4.7.6-300-D.1-D.1
172

173
            Here is the diff (+ lines (resp. - lines) are only available
174
            in InnerClasses (resp. ClassInfo), the rest are available in both)
175

176
            ACC_PUBLIC      0x0001  Declared public; may be accessed from outside its package.
177
         +  ACC_PRIVATE     0x0002  Marked private in source.
178
         +  ACC_PROTECTED   0x0004  Marked protected in source.
179
         +  ACC_STATIC      0x0008  Marked or implicitly static in source.
180
            ACC_FINAL       0x0010  Declared final; no subclasses allowed.
181
         -  ACC_SUPER       0x0020  Treat superclass methods specially when invoked by the invokespecial instruction.
182
            ACC_INTERFACE   0x0200  Is an interface, not a class.
183
            ACC_ABSTRACT    0x0400  Declared abstract; must not be instantiated.
184
            ACC_SYNTHETIC   0x1000  Declared synthetic; not present in the source code.
185
            ACC_ANNOTATION  0x2000  Declared as an annotation type.
186
            ACC_ENUM        0x4000  Declared as an enum type.
187
         -  ACC_MODULE      0x8000  Is a module, not a class or interface.
188

189
            If this stub is a nested class, then we don't have all its
190
            modifiers just with the ClassInfo, the actual source-declared
191
            visibility (if not public) is only in the InnerClasses, as
192
            well as its ACC_STATIC.
193

194
            Also ACC_SUPER conflicts with ACC_SYNCHRONIZED, which
195
            Modifier.toString would reflect.
196

197
            Since the differences are disjoint we can just OR the two
198
            sets of flags.
199
         */
200

201
        int myAccess = this.accessFlags;
1✔
202
        if (fromClassInfo) {
1✔
203
            // we don't care about ACC_SUPER and it conflicts
204
            // with ACC_SYNCHRONIZED
205
            accessFlags = accessFlags & ~Opcodes.ACC_SUPER;
1✔
206
        } else if ((myAccess & Opcodes.ACC_PUBLIC) != 0
1✔
207
            && (accessFlags & Opcodes.ACC_PROTECTED) != 0) {
208
            // ClassInfo mentions ACC_PUBLIC even if the real
209
            // visibility is protected
210
            // We remove the public to avoid a "public protected" combination
211
            myAccess = myAccess & ~Opcodes.ACC_PUBLIC;
1✔
212
        }
213
        this.accessFlags = myAccess | accessFlags;
1✔
214

215
        if ((accessFlags & Opcodes.ACC_ENUM) != 0) {
1✔
216
            this.enumConstants = new ArrayList<>();
1✔
217
        }
218
    }
1✔
219

220
    void setOuterClass(ClassStub outer, @Nullable String methodName, @Nullable String methodDescriptor) {
221
        if (enclosingInfo == null) {
1✔
222
            if (outer == null) {
1✔
223
                assert methodName == null && methodDescriptor == null
×
224
                    : "Enclosing method requires enclosing class";
225
                this.enclosingInfo = EnclosingInfo.NO_ENCLOSING;
×
226
            } else {
227
                this.enclosingInfo = new EnclosingInfo(outer, methodName, methodDescriptor);
1✔
228
            }
229
        }
230
    }
1✔
231

232
    void addField(FieldStub fieldStub) {
233
        fields.add(fieldStub);
1✔
234

235
        if (fieldStub.isEnumConstant() && enumConstants != null) {
1✔
236
            enumConstants.add(fieldStub);
1✔
237
        }
238
    }
1✔
239

240
    void addMemberClass(ClassStub classStub) {
241
        classStub.setOuterClass(this, null, null);
1✔
242
        memberClasses.add(classStub);
1✔
243
    }
1✔
244

245
    void addMethod(MethodStub methodStub) {
246
        methods.add(methodStub);
1✔
247
    }
1✔
248

249
    void addCtor(CtorStub methodStub) {
250
        ctors.add(methodStub);
1✔
251
    }
1✔
252

253
    @Override
254
    public void addAnnotation(SymAnnot annot) {
255
        annotations = annotations.plus(annot);
1✔
256
    }
1✔
257

258

259
    // </editor-fold>
260

261

262
    @Override
263
    public @Nullable JClassSymbol getSuperclass() {
264
        parseLock.ensureParsed();
1✔
265
        return signature.getRawSuper();
1✔
266
    }
267

268
    @Override
269
    public List<JClassSymbol> getSuperInterfaces() {
270
        parseLock.ensureParsed();
1✔
271
        return signature.getRawItfs();
1✔
272
    }
273

274
    @Override
275
    public @Nullable JClassType getSuperclassType(Substitution substitution) {
276
        parseLock.ensureParsed();
1✔
277
        return signature.getSuperType(substitution);
1✔
278
    }
279

280
    @Override
281
    public List<JClassType> getSuperInterfaceTypes(Substitution substitution) {
282
        parseLock.ensureParsed();
1✔
283
        return signature.getSuperItfs(substitution);
1✔
284
    }
285

286
    @Override
287
    public List<JTypeVar> getTypeParameters() {
288
        parseLock.ensureParsed();
1✔
289
        return signature.getTypeParams();
1✔
290
    }
291

292
    @Override
293
    public boolean isGeneric() {
294
        parseLock.ensureParsed();
1✔
295
        return signature.isGeneric();
1✔
296
    }
297

298
    @Override
299
    public LexicalScope getLexicalScope() {
300
        if (scope == null) {
1✔
301
            scope = JClassSymbol.super.getLexicalScope();
1✔
302
        }
303
        return scope;
1✔
304
    }
305

306
    @Override
307
    public List<JFieldSymbol> getDeclaredFields() {
308
        parseLock.ensureParsed();
1✔
309
        return fields;
1✔
310
    }
311

312
    @Override
313
    public List<JMethodSymbol> getDeclaredMethods() {
314
        parseLock.ensureParsed();
1✔
315
        return methods;
1✔
316
    }
317

318
    @Override
319
    public List<JConstructorSymbol> getConstructors() {
320
        parseLock.ensureParsed();
1✔
321
        return ctors;
1✔
322
    }
323

324
    @Override
325
    public List<JClassSymbol> getDeclaredClasses() {
326
        parseLock.ensureParsed();
1✔
327
        return memberClasses;
1✔
328
    }
329

330
    @Override
331
    public PSet<SymAnnot> getDeclaredAnnotations() {
332
        parseLock.ensureParsed();
1✔
333
        return annotations;
1✔
334
    }
335

336
    @Override
337
    public PSet<String> getAnnotationAttributeNames() {
338
        parseLock.ensureParsed();
1✔
339
        return annotAttributes;
1✔
340
    }
341

342
    @Override
343
    public @Nullable SymbolicValue getDefaultAnnotationAttributeValue(String attrName) {
344
        parseLock.ensureParsed();
1✔
345
        if (!annotAttributes.contains(attrName)) {
1✔
346
            // this is a shortcut, because the default impl checks each method
347
            return null;
1✔
348
        }
349
        return JClassSymbol.super.getDefaultAnnotationAttributeValue(attrName);
1✔
350
    }
351

352
    @Override
353
    public @Nullable JClassSymbol getEnclosingClass() {
354
        parseLock.ensureParsed();
1✔
355
        return enclosingInfo.getEnclosingClass();
1✔
356
    }
357

358
    @Override
359
    public @Nullable JExecutableSymbol getEnclosingMethod() {
360
        parseLock.ensureParsed();
1✔
361
        return enclosingInfo.getEnclosingMethod();
1✔
362
    }
363

364
    @Override
365
    public @NonNull List<JFieldSymbol> getEnumConstants() {
366
        parseLock.ensureParsed();
1✔
367
        return enumConstants;
1✔
368
    }
369

370
    @Override
371
    public JTypeParameterOwnerSymbol getEnclosingTypeParameterOwner() {
372
        parseLock.ensureParsed();
1✔
373
        return enclosingInfo.getEnclosing();
1✔
374
    }
375

376
    @Override
377
    public String toString() {
378
        // do not use SymbolToString as it triggers the class parsing,
379
        // making tests undebuggable
380
        return getInternalName();
1✔
381
    }
382

383
    @Override
384
    public int hashCode() {
385
        return SymbolEquality.CLASS.hash(this);
1✔
386
    }
387

388
    @Override
389
    public boolean equals(Object obj) {
390
        return SymbolEquality.CLASS.equals(this, obj);
1✔
391
    }
392

393
    // <editor-fold  defaultstate="collapsed" desc="Names">
394

395
    public String getInternalName() {
396
        return getNames().internalName;
1✔
397
    }
398

399
    private Names getNames() {
400
        return names;
1✔
401
    }
402

403
    @Override
404
    public @NonNull String getBinaryName() {
405
        return getNames().binaryName;
1✔
406
    }
407

408
    /**
409
     * Simpler check than computing the canonical name.
410
     */
411
    boolean hasCanonicalName() {
412
        if (names.canonicalName != null) {
1✔
413
            return true;
1✔
414
        }
415
        parseLock.ensureParsed();
1✔
416
        if (isAnonymousClass() || isLocalClass()) {
1✔
417
            return false;
×
418
        }
419
        JClassSymbol enclosing = getEnclosingClass();
1✔
420
        return enclosing == null // top-level class
1✔
421
            || enclosing instanceof ClassStub
422
            && ((ClassStub) enclosing).hasCanonicalName();
1✔
423
    }
424

425
    @Override
426
    public String getCanonicalName() {
427
        String canoName = names.canonicalName;
1✔
428
        if (canoName == null) {
1✔
429
            canoName = computeCanonicalName();
1✔
430
            names.canonicalName = canoName;
1✔
431
        }
432
        return canoName;
1✔
433
    }
434

435
    private @Nullable String computeCanonicalName() {
436
        parseLock.ensureParsed();
1✔
437
        if (names.canonicalName != null) {
1✔
438
            return names.canonicalName;
×
439
        }
440
        JClassSymbol enclosing = getEnclosingClass();
1✔
441
        if (enclosing == null) {
1✔
442
            return names.packageName + '.' + getSimpleName();
×
443
        }
444
        String outerName = enclosing.getCanonicalName();
1✔
445
        if (outerName == null) {
1✔
446
            return null;
×
447
        }
448
        return outerName + '.' + getSimpleName();
1✔
449
    }
450

451
    @Override
452
    public @NonNull String getPackageName() {
453
        return getNames().packageName;
1✔
454
    }
455

456
    @Override
457
    public @NonNull String getSimpleName() {
458
        String mySimpleName = names.simpleName;
1✔
459
        if (mySimpleName == null) {
1✔
460
            parseLock.ensureParsed();
×
461
            return Objects.requireNonNull(names.simpleName, "Null simple name after parsing");
×
462
        }
463
        return mySimpleName;
1✔
464
    }
465

466
    @Override
467
    public TypeSystem getTypeSystem() {
468
        return getResolver().getTypeSystem();
1✔
469
    }
470

471
    // </editor-fold>
472

473
    // <editor-fold  defaultstate="collapsed" desc="Modifier info">
474

475

476
    @Override
477
    public boolean isUnresolved() {
478
        return parseLock.isFailed();
1✔
479
    }
480

481
    @Override
482
    public boolean isArray() {
483
        return false;
1✔
484
    }
485

486
    @Override
487
    public boolean isPrimitive() {
488
        return false;
×
489
    }
490

491
    @Override
492
    public @Nullable JTypeDeclSymbol getArrayComponent() {
493
        return null;
×
494
    }
495

496
    @Override
497
    public int getModifiers() {
498
        parseLock.ensureParsed();
1✔
499
        return accessFlags;
1✔
500
    }
501

502
    @Override
503
    public boolean isAbstract() {
504
        return (getModifiers() & Opcodes.ACC_ABSTRACT) != 0;
1✔
505
    }
506

507
    @Override
508
    public boolean isEnum() {
509
        return (getModifiers() & Opcodes.ACC_ENUM) != 0;
1✔
510
    }
511

512
    @Override
513
    public boolean isAnnotation() {
514
        return (getModifiers() & Opcodes.ACC_ANNOTATION) != 0;
1✔
515
    }
516

517
    @Override
518
    public boolean isInterface() {
519
        return (getModifiers() & Opcodes.ACC_INTERFACE) != 0;
1✔
520
    }
521

522
    @Override
523
    public boolean isClass() {
524
        return (getModifiers() & (Opcodes.ACC_INTERFACE | Opcodes.ACC_ANNOTATION)) == 0;
1✔
525
    }
526

527
    @Override
528
    public boolean isRecord() {
529
        JClassSymbol sup = getSuperclass();
×
530
        return sup != null && "java.lang.Record".equals(sup.getBinaryName());
×
531
    }
532

533
    @Override
534
    public boolean isLocalClass() {
535
        return enclosingInfo.isLocal();
1✔
536
    }
537

538
    @Override
539
    public boolean isAnonymousClass() {
540
        return getSimpleName().isEmpty();
1✔
541
    }
542

543

544
    // </editor-fold>
545

546

547
    static class Names {
1✔
548

549
        final String binaryName;
550
        final String internalName;
551
        final String packageName;
552
        /** If null, the class requires parsing to find out the actual canonical name. */
553
        @Nullable String canonicalName;
554
        /** If null, the class requires parsing to find out the actual simple name. */
555
        @Nullable String simpleName;
556

557
        Names(String internalName) {
1✔
558
            assert isValidInternalName(internalName) : internalName;
1✔
559
            int packageEnd = internalName.lastIndexOf('/');
1✔
560

561
            this.internalName = internalName;
1✔
562
            this.binaryName = internalName.replace('/', '.');
1✔
563
            if (packageEnd == -1) {
1✔
564
                this.packageName = "";
1✔
565
            } else {
566
                this.packageName = binaryName.substring(0, packageEnd);
1✔
567
            }
568

569
            if (binaryName.indexOf('$', packageEnd + 1) >= 0) {
1✔
570
                // Contains a dollar in class name (after package)
571
                // Requires parsing to find out the actual simple name,
572
                // this might be an inner class, or simply a class with
573
                // a dollar in its name.
574

575
                // ASSUMPTION: all JVM languages use the $ convention
576
                // to separate inner classes. Java compilers do so but
577
                // not necessarily true of all compilers/languages.
578
                this.canonicalName = null;
1✔
579
                this.simpleName = null;
1✔
580
            } else {
581
                // fast path
582
                this.canonicalName = binaryName;
1✔
583
                this.simpleName = binaryName.substring(packageEnd + 1);
1✔
584
            }
585
        }
1✔
586

587
        public void finishOuterClass() {
588
            int packageEnd = internalName.lastIndexOf('/');
1✔
589
            this.simpleName = binaryName.substring(packageEnd + 1); // if -1, start from 0
1✔
590
            this.canonicalName = binaryName;
1✔
591
        }
1✔
592
    }
593

594
    static class EnclosingInfo {
595

596
        static final EnclosingInfo NO_ENCLOSING = new EnclosingInfo(null, null, null);
1✔
597

598
        private final @Nullable JClassSymbol stub;
599
        private final @Nullable String methodName;
600
        private final @Nullable String methodDescriptor;
601

602
        EnclosingInfo(@Nullable JClassSymbol stub, @Nullable String methodName, @Nullable String methodDescriptor) {
1✔
603
            this.stub = stub;
1✔
604
            this.methodName = methodName;
1✔
605
            this.methodDescriptor = methodDescriptor;
1✔
606
        }
1✔
607

608
        boolean isLocal() {
609
            return methodName != null || methodDescriptor != null;
1✔
610
        }
611

612
        public @Nullable JClassSymbol getEnclosingClass() {
613
            return stub;
1✔
614
        }
615

616
        public @Nullable MethodStub getEnclosingMethod() {
617
            if (stub instanceof ClassStub && methodName != null) {
1✔
618
                ClassStub stub1 = (ClassStub) stub;
×
619
                stub1.parseLock.ensureParsed();
×
620
                for (JMethodSymbol m : stub1.methods) {
×
621
                    MethodStub ms = (MethodStub) m;
×
622
                    if (ms.matches(methodName, methodDescriptor)) {
×
623
                        return ms;
×
624
                    }
625
                }
×
626
            }
627
            return null;
1✔
628
        }
629

630

631
        JTypeParameterOwnerSymbol getEnclosing() {
632
            if (methodName != null) {
1✔
633
                return getEnclosingMethod();
×
634
            } else {
635
                return getEnclosingClass();
1✔
636
            }
637
        }
638

639
        @Override
640
        public boolean equals(Object o) {
641
            if (this == o) {
1✔
642
                return true;
1✔
643
            }
644
            if (o == null || getClass() != o.getClass()) {
1✔
645
                return false;
×
646
            }
647
            EnclosingInfo that = (EnclosingInfo) o;
1✔
648
            return Objects.equals(stub, that.stub)
1✔
649
                && Objects.equals(methodName, that.methodName)
×
650
                && Objects.equals(methodDescriptor, that.methodDescriptor);
1✔
651
        }
652

653
        @Override
654
        public int hashCode() {
655
            return Objects.hash(stub, methodName, methodDescriptor);
×
656
        }
657
    }
658
}
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