Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1756,28 +1756,35 @@ public Nullness getGenericMethodReturnTypeNullness(
private @Nullable Type getTypeForSymbol(Symbol symbol, VisitorState state) {
if (symbol.isAnonymous()) {
// For anonymous classes, symbol.type does not contain annotations on generic type parameters.
// So, we get a correct type from the enclosing NewClassTree representing the anonymous class.
// So, we get a correct type from the NewClassTree representing the anonymous class.
// The nearest enclosing NewClassTree on the current path may be some other constructor call,
// such as when `this` from an anonymous class is passed as an argument to a constructor.
TreePath path = state.getPath();
path =
castToNonNull(ASTHelpers.findPathFromEnclosingNodeToTopLevel(path, NewClassTree.class));
NewClassTree newClassTree = (NewClassTree) path.getLeaf();
if (newClassTree.getClassBody() == null) {
throw new RuntimeException(
"method should be directly inside an anonymous NewClassTree "
+ state.getSourceForNode(path.getLeaf()));
}
Type typeFromTree = getTreeType(newClassTree, state);
if (typeFromTree != null) {
verify(
state.getTypes().isAssignable(symbol.type, typeFromTree),
"%s is not assignable to %s",
symbol.type,
typeFromTree);
while (path != null) {
if (path.getLeaf() instanceof NewClassTree newClassTree
&& newClassTree.getClassBody() != null) {
Type newClassType = ASTHelpers.getType(newClassTree);
if (newClassType != null && newClassType.tsym.equals(symbol)) {
Type typeFromTree = getTreeType(newClassTree, state);
if (typeFromTree != null) {
verify(
state.getTypes().isAssignable(symbol.type, typeFromTree),
"%s is not assignable to %s",
symbol.type,
typeFromTree);
}
return typeFromTree;
}
}
path = path.getParentPath();
}
return typeFromTree;
} else {
return symbol.type;
throw new RuntimeException(
"could not find anonymous NewClassTree for symbol "
+ symbol
+ " from path "
+ state.getSourceForNode(state.getPath().getLeaf()));
}
return symbol.type;
}

public Nullness getGenericMethodReturnTypeNullness(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1653,6 +1653,32 @@ interface TestInterface<T extends @Nullable Object> {
.doTest();
}

@Test
public void constructorCallWithThisFromAnonymousClass() {
makeHelper()
.addSourceLines(
"Test.java",
"""
package com.uber;
import org.jspecify.annotations.NullMarked;
@NullMarked
class Test {
static class TakesRunnable {
TakesRunnable(Runnable runnable) {}
}
void repro() {
new Runnable() {
@Override
public void run() {
new TakesRunnable(this);
}
};
}
}
""")
.doTest();
}

@Test
public void explicitlyTypedAnonymousClassAsReceiver() {
makeHelper()
Expand Down
Loading