From 767c165c2e3e1f043d60e937ec06175916e7efaf Mon Sep 17 00:00:00 2001 From: Aosen Xiong Date: Tue, 27 Feb 2024 14:41:09 -0500 Subject: [PATCH] Use CF constructor from use impl --- .../InferenceAnnotatedTypeFactory.java | 122 +++++++++++++++++- 1 file changed, 118 insertions(+), 4 deletions(-) diff --git a/src/checkers/inference/InferenceAnnotatedTypeFactory.java b/src/checkers/inference/InferenceAnnotatedTypeFactory.java index fef2cea8..5f0f31d0 100644 --- a/src/checkers/inference/InferenceAnnotatedTypeFactory.java +++ b/src/checkers/inference/InferenceAnnotatedTypeFactory.java @@ -70,6 +70,8 @@ import com.sun.source.tree.MethodInvocationTree; import com.sun.source.tree.NewClassTree; import com.sun.source.tree.Tree; +import org.checkerframework.javacutil.TypesUtils; +import org.plumelib.util.CollectionsPlume; /** * InferenceAnnotatedTypeFactory is responsible for creating AnnotatedTypeMirrors that are annotated with @@ -389,11 +391,126 @@ public ParameterizedExecutableType constructorFromUse(final NewClassTree newClas "Current path:\n" + getVisitorTreePath(); final ExecutableElement constructorElem = TreeUtils.elementFromUse(newClassTree); + AnnotatedDeclaredType constructorReturnType = (AnnotatedDeclaredType) toAnnotatedType(TreeUtils.typeOf(newClassTree), false); addComputedTypeAnnotations(newClassTree, constructorReturnType); - final AnnotatedExecutableType constructorType = AnnotatedTypes.asMemberOf(types, this, constructorReturnType, constructorElem); + if (!TreeUtils.isDiamondTree(newClassTree)) { + if (newClassTree.getClassBody() == null) { + constructorReturnType.setTypeArguments(getExplicitNewClassClassTypeArgs(newClassTree)); + } + } else { + constructorReturnType = getAnnotatedType(TypesUtils.getTypeElement(constructorReturnType.underlyingType)); + // Add explicit annotations below. + constructorReturnType.clearAnnotations(); + } + + AnnotationMirrorSet explicitAnnos = getExplicitNewClassAnnos(newClassTree); + constructorReturnType.addAnnotations(explicitAnnos); + + // Get the enclosing type of the constructor, if one exists. + // this.new InnerClass() + AnnotatedDeclaredType enclosingType = (AnnotatedDeclaredType) getReceiverType(newClassTree); + constructorReturnType.setEnclosingType(enclosingType); + + // Add computed annotations to the type. + addComputedTypeAnnotations(newClassTree, constructorReturnType); + +// final AnnotatedExecutableType constructorType = AnnotatedTypes.asMemberOf(types, this, constructorReturnType, constructorElem); + ExecutableElement ctor = TreeUtils.elementFromUse(newClassTree); + AnnotatedExecutableType constructorType = getAnnotatedType(ctor); // get unsubstituted type + constructorFromUsePreSubstitution(newClassTree, constructorType); + + if (viewpointAdapter != null) { + viewpointAdapter.viewpointAdaptConstructor(constructorReturnType, constructorElem, constructorType); + } + + if (newClassTree.getClassBody() != null) { + // Because the anonymous constructor can't have explicit annotations on its parameters, + // they are copied from the super constructor invoked in the anonymous constructor. To + // do this: + // 1. get unsubstituted type of the super constructor. + // 2. adapt it to this call site. + // 3. compute and store the vararg type. + // 4. copy the parameters to the anonymous constructor, `con`. + // 5. copy annotations on the return type to `con`. + AnnotatedExecutableType superCon = + getAnnotatedType(TreeUtils.getSuperConstructor(newClassTree)); + constructorFromUsePreSubstitution(newClassTree, superCon); + // no viewpoint adaptation needed for super invocation + superCon = + AnnotatedTypes.asMemberOf(types, this, constructorReturnType, superCon.getElement(), superCon); + constructorType.computeVarargType(superCon); + if (superCon.getParameterTypes().size() == constructorType.getParameterTypes().size()) { + constructorType.setParameterTypes(superCon.getParameterTypes()); + } else { + // If the super class of the anonymous class has an enclosing type, then it is the + // first parameter of the anonymous constructor. For example, + // class Outer { class Inner {} } + // new Inner(){}; + // Then javac creates the following constructor: + // (.Outer x0) { + // x0.super(); + // } + // So the code below deals with this. + List p = + new ArrayList<>(superCon.getParameterTypes().size() + 1); + if (TreeUtils.hasSyntheticArgument(newClassTree)) { + p.add(constructorType.getParameterTypes().get(0)); + constructorType.setReceiverType(superCon.getReceiverType()); + } else if (constructorType.getReceiverType() != null) { + // Because the anonymous constructor doesn't have annotated receiver type, + // we copy the receiver type from the super constructor invoked in the anonymous + // constructor and add it to the parameterTypes as the first element. + constructorType.setReceiverType(superCon.getReceiverType()); + p.add(constructorType.getReceiverType()); + } else { + p.add(constructorType.getParameterTypes().get(0)); + } + p.addAll(1, superCon.getParameterTypes()); + constructorType.setParameterTypes(Collections.unmodifiableList(p)); + } + constructorType.getReturnType().replaceAnnotations(superCon.getReturnType().getAnnotations()); + } else { + // Store varargType before calling setParameterTypes, otherwise we may lose the + // varargType as it is the last element of the original parameterTypes. + // AnnotatedTypes.asMemberOf handles vararg type properly, so we do not need to compute + // vararg type again. + constructorType.computeVarargType(); + constructorType = AnnotatedTypes.asMemberOf(types, this, constructorReturnType, ctor, constructorType); + } + + Map typeParamToTypeArg = + AnnotatedTypes.findTypeArguments(processingEnv, this, newClassTree, ctor, constructorType); + List typeargs; + if (typeParamToTypeArg.isEmpty()) { + typeargs = Collections.emptyList(); + } else { + typeargs = + CollectionsPlume.mapList( + (AnnotatedTypeMirror.AnnotatedTypeVariable tv) -> + typeParamToTypeArg.get(tv.getUnderlyingType()), + constructorType.getTypeVariables()); + } + if (TreeUtils.isDiamondTree(newClassTree)) { + // TODO: This should be done at the same time as type argument inference. + List classTypeArgs = inferDiamondType(newClassTree); + int i = 0; + for (AnnotatedTypeMirror typeParam : constructorReturnType.getTypeArguments()) { + typeParamToTypeArg.put( + (TypeVariable) typeParam.getUnderlyingType(), classTypeArgs.get(i)); + i++; + } + } + constructorType = (AnnotatedExecutableType) typeVarSubstitutor.substitute(typeParamToTypeArg, constructorType); + + stubTypes.injectRecordComponentType(types, ctor, constructorType); + if (enclosingType != null) { + // Reset the enclosing type because it can be substituted incorrectly. + ((AnnotatedDeclaredType) constructorType.getReturnType()).setEnclosingType(enclosingType); + } + // Take adapt parameter logic from AnnotatedTypeFactory#constructorFromUse to // InferenceAnnotatedTypeFactory#constructorFromUse. // Store varargType before calling setParameterTypes, otherwise we may lose the @@ -407,9 +524,6 @@ public ParameterizedExecutableType constructorFromUse(final NewClassTree newClas List parameters = AnnotatedTypes.adaptParameters(this, constructorType, newClassTree.getArguments()); constructorType.setParameterTypes(parameters); - if (viewpointAdapter != null) { - viewpointAdapter.viewpointAdaptConstructor(constructorReturnType, constructorElem, constructorType); - } ParameterizedExecutableType substitutedPair = substituteTypeArgs(newClassTree, constructorElem, constructorType); inferencePoly.replacePolys(newClassTree, substitutedPair.executableType);