@@ -49,14 +49,6 @@ public static void handleConstructorState(CtConstructor<?> c, RefinedFunction f,
4949 }
5050 }
5151
52- /**
53- * An unannotated constructor whose body starts with {@code super(...)} leaves the object in the state that the
54- * called superclass constructor's specification gives it (e.g. {@code MyException(String m, Throwable c) { super(m,
55- * c); }} with the {@code Throwable(String, Throwable)} spec: {@code withThrowable(this)}). The super constructor's
56- * parameters are renamed to the arguments passed, which must be parameters of this constructor or literals.
57- *
58- * @return whether a state was inherited (otherwise the caller falls back to the default state)
59- */
6052 /**
6153 * JDK classes whose constructors all pass their arguments unchanged to the superclass constructor with the same
6254 * parameter types (documented in their Javadoc), so a spec on that superclass constructor describes them too. Only
@@ -66,6 +58,43 @@ public static void handleConstructorState(CtConstructor<?> c, RefinedFunction f,
6658 private static final Set <String > DELEGATING_JDK_CLASSES = Set .of ("java.lang.Exception" ,
6759 "java.lang.RuntimeException" , "java.lang.Error" );
6860
61+ /**
62+ * An unannotated constructor whose body starts with {@code super(...)} leaves the object in the state that the
63+ * called superclass constructor's specification gives it (e.g. {@code MyException(String m, Throwable c) { super(m,
64+ * c); }} with the {@code Throwable(String, Throwable)} spec: {@code withThrowable(this)}). The super constructor's
65+ * parameters are renamed to the arguments passed, which must be parameters of this constructor or literals.
66+ *
67+ * @return whether a state was inherited (otherwise the caller falls back to the default state)
68+ */
69+ private static boolean inheritSuperConstructorState (CtConstructor <?> c , RefinedFunction f , TypeChecker tc ) {
70+ CtInvocation <?> call = superConstructorCall (c );
71+ if (call == null )
72+ return false ;
73+ var exe = call .getExecutable ();
74+ RefinedFunction superF = specifiedConstructor (tc , exe .getSimpleName (), exe .getDeclaringType (),
75+ exe .getParameters ());
76+ if (superF == null || superF .getToStates ().isEmpty ()
77+ || superF .getArguments ().size () != call .getArguments ().size ())
78+ return false ;
79+ List <ObjectState > states = renameStates (superF .getToStates (), superParamsToArguments (superF , call ));
80+ if (states == null )
81+ return false ;
82+ f .setAllStates (states );
83+ return true ;
84+ }
85+
86+ /** The {@code super(...)} call that starts the body of {@code c}, or null if it does not start with one. */
87+ private static CtInvocation <?> superConstructorCall (CtConstructor <?> c ) {
88+ if (c .getBody () == null || c .getBody ().getStatements ().isEmpty ()
89+ || !(c .getBody ().getStatement (0 )instanceof CtInvocation <?> call ))
90+ return null ;
91+ var exe = call .getExecutable ();
92+ if (exe == null || !exe .isConstructor () || exe .getDeclaringType () == null || c .getDeclaringType () == null
93+ || exe .getDeclaringType ().equals (c .getDeclaringType ().getReference ()))
94+ return null ; // not a super(...) call (this(...) delegates within the class)
95+ return call ;
96+ }
97+
6998 /** The specified constructor that {@code name(params)} of {@code type} ends up running, if one is known. */
7099 private static RefinedFunction specifiedConstructor (TypeChecker tc , String name , CtTypeReference <?> type ,
71100 List <CtTypeReference <?>> params ) {
@@ -82,19 +111,11 @@ private static RefinedFunction specifiedConstructor(TypeChecker tc, String name,
82111 return null ;
83112 }
84113
85- private static boolean inheritSuperConstructorState (CtConstructor <?> c , RefinedFunction f , TypeChecker tc ) {
86- if (c .getBody () == null || c .getBody ().getStatements ().isEmpty ()
87- || !(c .getBody ().getStatement (0 )instanceof CtInvocation <?> call ))
88- return false ;
89- var exe = call .getExecutable ();
90- if (exe == null || !exe .isConstructor () || exe .getDeclaringType () == null || c .getDeclaringType () == null
91- || exe .getDeclaringType ().equals (c .getDeclaringType ().getReference ()))
92- return false ; // not a super(...) call (this(...) delegates within the class)
93- RefinedFunction superF = specifiedConstructor (tc , exe .getSimpleName (), exe .getDeclaringType (),
94- exe .getParameters ());
95- if (superF == null || superF .getToStates ().isEmpty ()
96- || superF .getArguments ().size () != call .getArguments ().size ())
97- return false ;
114+ /**
115+ * Maps each parameter of the super constructor to the name of the argument {@code call} passes for it: a parameter
116+ * of the calling constructor or a non-string literal, or null for any other expression.
117+ */
118+ private static Map <String , String > superParamsToArguments (RefinedFunction superF , CtInvocation <?> call ) {
98119 Map <String , String > rename = new HashMap <>();
99120 for (int i = 0 ; i < call .getArguments ().size (); i ++) {
100121 CtExpression <?> arg = call .getArguments ().get (i );
@@ -107,12 +128,20 @@ else if (arg instanceof CtLiteral<?> lit && lit.getValue() != null && !(lit.getV
107128 else
108129 rename .put (param , null ); // an argument we cannot name: usable only if the state ignores it
109130 }
131+ return rename ;
132+ }
133+
134+ /**
135+ * The given states with the super constructor's parameters renamed, or null if one of them mentions a parameter
136+ * whose argument has no name.
137+ */
138+ private static List <ObjectState > renameStates (List <Predicate > toStates , Map <String , String > rename ) {
110139 List <ObjectState > states = new ArrayList <>();
111- for (Predicate to : superF . getToStates () ) {
140+ for (Predicate to : toStates ) {
112141 for (Map .Entry <String , String > r : rename .entrySet ()) {
113142 if (r .getValue () == null ) {
114143 if (to .getVariableNames ().contains (r .getKey ()))
115- return false ;
144+ return null ;
116145 } else {
117146 to = to .substituteVariable (r .getKey (), r .getValue ());
118147 }
@@ -121,8 +150,7 @@ else if (arg instanceof CtLiteral<?> lit && lit.getValue() != null && !(lit.getV
121150 os .setTo (to );
122151 states .add (os );
123152 }
124- f .setAllStates (states );
125- return true ;
153+ return states ;
126154 }
127155
128156 /**
0 commit comments