4949import javax .lang .model .type .ExecutableType ;
5050import javax .lang .model .type .TypeKind ;
5151import javax .lang .model .type .TypeMirror ;
52+ import javax .lang .model .type .TypeVariable ;
53+ import javax .lang .model .type .WildcardType ;
5254import javax .lang .model .util .ElementFilter ;
5355import org .netbeans .api .annotations .common .CheckForNull ;
5456import org .netbeans .api .java .source .CompilationInfo ;
@@ -88,11 +90,6 @@ public class NPECheck {
8890 })
8991 public static ErrorDescription assignment (HintContext ctx ) {
9092 Element e = ctx .getInfo ().getTrees ().getElement (ctx .getVariables ().get ("$var" ));
91-
92- if (!isVariableElement (ctx , e )) {
93- return null ;
94- }
95-
9693 TreePath expr = ctx .getVariables ().get ("$expr" );
9794 StateEnum r = computeExpressionsState (ctx ).getOrDefault (expr .getLeaf (), DEFAULT_STATE ).thisTypeState ;
9895
@@ -459,7 +456,7 @@ private static boolean hasNull(HintContext ctx, BinaryTree bt) {
459456 @ TriggerPattern ("return $expression;" )
460457 public static ErrorDescription returnNull (HintContext ctx ) {
461458 TreePath expression = ctx .getVariables ().get ("$expression" );
462- StateEnum returnState = computeExpressionsState (ctx ).getOrDefault (expression .getLeaf (), DEFAULT_STATE ). thisTypeState ;
459+ State returnState = computeExpressionsState (ctx ).getOrDefault (expression .getLeaf (), DEFAULT_STATE );
463460
464461 if (returnState == null ) return null ;
465462
@@ -483,16 +480,12 @@ public static ErrorDescription returnNull(HintContext ctx) {
483480 if (el == null || el .getKind () != ElementKind .METHOD ) return null ;
484481
485482 State expected = getStateFromAnnotations (info , el );
486- String key = null ;
487-
488- switch (returnState ) {
489- case NULL :
490- if (expected .isNotNull ()) key = "ERR_ReturningNullFromNonNull" ;
491- break ;
492- case POSSIBLE_NULL_REPORT :
493- if (expected .isNotNull ()) key = "ERR_ReturningPossibleNullFromNonNull" ;
494- break ;
495- }
483+ String key = switch (statesMatch (expected , returnState )) {
484+ case TOP_LEVEL_NULL_TO_NONNULL -> "ERR_ReturningNullFromNonNull" ;
485+ case TOP_LEVEL_POSSIBLE_NULL_TO_NONNULL -> "ERR_ReturningPossibleNullFromNonNull" ;
486+ case MISMATCH -> "ERR_TYPES_MISMATCH" ;
487+ default -> null ;
488+ };
496489
497490 if (key != null ) {
498491 String displayName = NbBundle .getMessage (NPECheck .class , key );
@@ -633,6 +626,7 @@ private static State getStateFromAnnotations(CompilationInfo info, Element e) {
633626 //XXX:
634627 //- should include with OVERRIDE_ANNOTATIONS?
635628 //- adjust default(!)
629+ //- should really ignore the defaults for local variables??? especially at validation time?
636630 result = getStateFromAnnotations (info , e .asType (), x -> null ,
637631 LOCAL_VARIABLES .contains (e .getKind ()) ? StateEnum .POSSIBLE_NULL : typeDefault , typeDefault );
638632 } else if (e .getKind () == ElementKind .METHOD ) {
@@ -660,13 +654,42 @@ private static State getStateFromAnnotations(CompilationInfo info, TypeMirror ty
660654 }
661655
662656 private static State getStateFromAnnotations (CompilationInfo info , TypeMirror type , Function <TypeMirror , State > type2StateMapper , StateEnum topLevelFallbackState , StateEnum fallbackState ) {
657+ return getStateFromAnnotations (info , type , type2StateMapper , topLevelFallbackState , fallbackState , true );
658+ }
659+
660+ private static State getStateFromAnnotations (CompilationInfo info , TypeMirror type , Function <TypeMirror , State > type2StateMapper , StateEnum topLevelFallbackState , StateEnum fallbackState , boolean recurseToGenericTypes ) {
663661 State state = type2StateMapper .apply (type );
664662
665663 if (state != null ) {
666664 //TODO: should presumably merge with other aspects?
667665 return state ;
668666 }
669667
668+ if (recurseToGenericTypes ) {
669+ if (type .getKind () == TypeKind .TYPEVAR ) {
670+ StateEnum thisTypeState = getStateFromAnnotations (type .getAnnotationMirrors (), StateEnum .POSSIBLE_NULL );
671+ State fromBound = getStateFromAnnotations (info , ((TypeVariable ) type ).getUpperBound (), type2StateMapper , topLevelFallbackState , fallbackState );
672+
673+ if (thisTypeState != StateEnum .POSSIBLE_NULL ) {
674+ return fromBound .setThisState (thisTypeState );
675+ } else {
676+ return fromBound ;
677+ }
678+ } else if (type .getKind () == TypeKind .WILDCARD ) {
679+ WildcardType wt = (WildcardType ) type ;
680+ TypeMirror base ;
681+ if (wt .getExtendsBound () != null ) {
682+ base = wt .getExtendsBound ();
683+ } else if (wt .getSuperBound () != null ) {
684+ base = wt .getSuperBound ();
685+ } else {
686+ return new State (StateEnum .POSSIBLE_NULL );
687+ }
688+
689+ return getStateFromAnnotations (info , base , type2StateMapper , topLevelFallbackState , fallbackState );
690+ }
691+ }
692+
670693 StateEnum thisTypeState = getStateFromAnnotations (type .getAnnotationMirrors (), topLevelFallbackState );
671694 List <State > typeParameters = null ;
672695 State arrayComponentState = null ;
@@ -676,12 +699,12 @@ private static State getStateFromAnnotations(CompilationInfo info, TypeMirror ty
676699
677700 typeParameters = dt .getTypeArguments ()
678701 .stream ()
679- .map (ta -> getStateFromAnnotations (info , ta , type2StateMapper , fallbackState ))
702+ .map (ta -> getStateFromAnnotations (info , ta , type2StateMapper , fallbackState , fallbackState , recurseToGenericTypes ))
680703 .toList ();
681704 } else if (type .getKind () == TypeKind .ARRAY ) {
682705 ArrayType at = (ArrayType ) type ;
683706
684- arrayComponentState = getStateFromAnnotations (info , at .getComponentType (), type2StateMapper , fallbackState );
707+ arrayComponentState = getStateFromAnnotations (info , at .getComponentType (), type2StateMapper , fallbackState , fallbackState , recurseToGenericTypes );
685708 }
686709
687710 return new State (thisTypeState , typeParameters , arrayComponentState );
@@ -1263,9 +1286,12 @@ public State visitMethodInvocation(MethodInvocationTree node, Void p) {
12631286 TypeMirror instantiatedReturnType = ((ExecutableType ) info .getTypes ().asMemberOf (receiver , e )).getReturnType ();
12641287 State instantiatedState = getStateFromAnnotations (info , instantiatedReturnType , marker2State ::get , StateEnum .POSSIBLE_NULL );
12651288 TypeMirror declaredReturnType = ((ExecutableElement ) e ).getReturnType ();
1266- State declaredState = getStateFromAnnotations (info , declaredReturnType , null );
1289+ State declaredState = getStateFromAnnotations (info , declaredReturnType , type -> null , null , null , false );
12671290
1291+ //testTypeAnnotations2: NOT_NULL List <PNR String> + PNR T -> PNR<PNR>
1292+ //testTypeVariables4: @NN String, T -> PNR
12681293 return State .weakMerge (instantiatedState , declaredState );
1294+ // return instantiatedState;
12691295 }
12701296 }
12711297 }
@@ -1996,9 +2022,9 @@ public static State weakMerge(State s1, State s2) {
19962022 }
19972023
19982024 private static List <State > mergeTypeParams (List <State > typeParams1 , List <State > typeParams2 , boolean strict ) {
1999- if (typeParams1 == null ) {
2025+ if (typeParams1 == null || typeParams1 . isEmpty () ) {
20002026 return typeParams2 ;
2001- } else if (typeParams2 == null ) {
2027+ } else if (typeParams2 == null || typeParams2 . isEmpty () ) {
20022028 return typeParams1 ;
20032029 } else if (typeParams1 .size () == typeParams2 .size ()) {
20042030 List <State > typeParams = new ArrayList <>();
0 commit comments