|
49 | 49 | import com.google.javascript.rhino.jstype.TemplateType;
|
50 | 50 | import java.util.ArrayList;
|
51 | 51 | import java.util.Collections;
|
52 |
| -import java.util.HashSet; |
53 | 52 | import java.util.Iterator;
|
54 | 53 | import java.util.LinkedHashMap;
|
| 54 | +import java.util.LinkedHashSet; |
55 | 55 | import java.util.List;
|
56 | 56 | import java.util.Map;
|
57 | 57 | import java.util.Set;
|
58 | 58 | import org.jspecify.nullness.Nullable;
|
59 | 59 |
|
60 | 60 | /**
|
61 |
| - * A builder for FunctionTypes, because FunctionTypes are so |
62 |
| - * ridiculously complex. All methods return {@code this} for ease of use. |
| 61 | + * A builder for FunctionTypes, because FunctionTypes are so ridiculously complex. All methods |
| 62 | + * return {@code this} for ease of use. |
63 | 63 | *
|
64 |
| - * Right now, this mostly uses JSDocInfo to infer type information about |
65 |
| - * functions. In the long term, developers should extend it to use other |
66 |
| - * signals by overloading the various "inferXXX" methods. For example, we |
67 |
| - * might want to use {@code goog.inherits} calls as a signal for inheritance, or |
68 |
| - * {@code return} statements as a signal for return type. |
| 64 | + * <p>Right now, this mostly uses JSDocInfo to infer type information about functions. In the long |
| 65 | + * term, developers should extend it to use other signals by overloading the various "inferXXX" |
| 66 | + * methods. For example, we might want to use {@code goog.inherits} calls as a signal for |
| 67 | + * inheritance, or {@code return} statements as a signal for return type. |
69 | 68 | *
|
70 |
| - * NOTE(nicksantos): Organizationally, this feels like it should be in Rhino. |
71 |
| - * But it depends on some coding convention stuff that's really part |
72 |
| - * of JSCompiler. |
| 69 | + * <p>NOTE(nicksantos): Organizationally, this feels like it should be in Rhino. But it depends on |
| 70 | + * some coding convention stuff that's really part of JSCompiler. |
73 | 71 | */
|
74 | 72 | final class FunctionTypeBuilder {
|
75 | 73 |
|
@@ -104,39 +102,34 @@ final class FunctionTypeBuilder {
|
104 | 102 | private @Nullable TypedScope declarationScope = null;
|
105 | 103 | private StaticTypedScope templateScope;
|
106 | 104 |
|
107 |
| - static final DiagnosticType EXTENDS_WITHOUT_TYPEDEF = DiagnosticType.warning( |
108 |
| - "JSC_EXTENDS_WITHOUT_TYPEDEF", |
109 |
| - "@extends used without @constructor or @interface for {0}"); |
| 105 | + static final DiagnosticType EXTENDS_WITHOUT_TYPEDEF = |
| 106 | + DiagnosticType.warning( |
| 107 | + "JSC_EXTENDS_WITHOUT_TYPEDEF", |
| 108 | + "@extends used without @constructor or @interface for {0}"); |
110 | 109 |
|
111 |
| - static final DiagnosticType EXTENDS_NON_OBJECT = DiagnosticType.warning( |
112 |
| - "JSC_EXTENDS_NON_OBJECT", |
113 |
| - "{0} @extends non-object type {1}"); |
| 110 | + static final DiagnosticType EXTENDS_NON_OBJECT = |
| 111 | + DiagnosticType.warning("JSC_EXTENDS_NON_OBJECT", "{0} @extends non-object type {1}"); |
114 | 112 |
|
115 |
| - static final DiagnosticType RESOLVED_TAG_EMPTY = DiagnosticType.warning( |
116 |
| - "JSC_RESOLVED_TAG_EMPTY", |
117 |
| - "Could not resolve type in {0} tag of {1}"); |
| 113 | + static final DiagnosticType RESOLVED_TAG_EMPTY = |
| 114 | + DiagnosticType.warning("JSC_RESOLVED_TAG_EMPTY", "Could not resolve type in {0} tag of {1}"); |
118 | 115 |
|
119 | 116 | static final DiagnosticType CONSTRUCTOR_REQUIRED =
|
120 |
| - DiagnosticType.warning("JSC_CONSTRUCTOR_REQUIRED", |
121 |
| - "{0} used without @constructor for {1}"); |
| 117 | + DiagnosticType.warning("JSC_CONSTRUCTOR_REQUIRED", "{0} used without @constructor for {1}"); |
122 | 118 |
|
123 |
| - static final DiagnosticType VAR_ARGS_MUST_BE_LAST = DiagnosticType.warning( |
124 |
| - "JSC_VAR_ARGS_MUST_BE_LAST", |
125 |
| - "variable length argument must be last"); |
| 119 | + static final DiagnosticType VAR_ARGS_MUST_BE_LAST = |
| 120 | + DiagnosticType.warning("JSC_VAR_ARGS_MUST_BE_LAST", "variable length argument must be last"); |
126 | 121 |
|
127 |
| - static final DiagnosticType OPTIONAL_ARG_AT_END = DiagnosticType.warning( |
128 |
| - "JSC_OPTIONAL_ARG_AT_END", |
129 |
| - "optional arguments must be at the end"); |
| 122 | + static final DiagnosticType OPTIONAL_ARG_AT_END = |
| 123 | + DiagnosticType.warning("JSC_OPTIONAL_ARG_AT_END", "optional arguments must be at the end"); |
130 | 124 |
|
131 |
| - static final DiagnosticType INEXISTENT_PARAM = DiagnosticType.warning( |
132 |
| - "JSC_INEXISTENT_PARAM", |
133 |
| - "parameter {0} does not appear in {1}''s parameter list"); |
| 125 | + static final DiagnosticType INEXISTENT_PARAM = |
| 126 | + DiagnosticType.warning( |
| 127 | + "JSC_INEXISTENT_PARAM", "parameter {0} does not appear in {1}''s parameter list"); |
134 | 128 |
|
135 |
| - static final DiagnosticType TYPE_REDEFINITION = DiagnosticType.warning( |
136 |
| - "JSC_TYPE_REDEFINITION", |
137 |
| - "attempted re-definition of type {0}\n" |
138 |
| - + "found : {1}\n" |
139 |
| - + "expected: {2}"); |
| 129 | + static final DiagnosticType TYPE_REDEFINITION = |
| 130 | + DiagnosticType.warning( |
| 131 | + "JSC_TYPE_REDEFINITION", |
| 132 | + "attempted re-definition of type {0}\n" + "found : {1}\n" + "expected: {2}"); |
140 | 133 |
|
141 | 134 | static final DiagnosticType TEMPLATE_TRANSFORMATION_ON_CLASS =
|
142 | 135 | DiagnosticType.warning(
|
@@ -326,8 +319,7 @@ FunctionTypeBuilder inferFromOverriddenFunction(
|
326 | 319 | } else {
|
327 | 320 | // We're overriding with a function literal. Apply type information
|
328 | 321 | // to each parameter of the literal.
|
329 |
| - FunctionParamBuilder paramBuilder = |
330 |
| - new FunctionParamBuilder(typeRegistry); |
| 322 | + FunctionParamBuilder paramBuilder = new FunctionParamBuilder(typeRegistry); |
331 | 323 | Iterator<Parameter> oldParams = oldType.getParameters().iterator();
|
332 | 324 | boolean warnedAboutArgList = false;
|
333 | 325 | boolean oldParamsListHitOptArgs = false;
|
@@ -388,8 +380,7 @@ FunctionTypeBuilder inferFromOverriddenFunction(
|
388 | 380 | @CanIgnoreReturnValue
|
389 | 381 | FunctionTypeBuilder inferReturnType(@Nullable JSDocInfo info, boolean fromInlineDoc) {
|
390 | 382 | if (info != null) {
|
391 |
| - JSTypeExpression returnTypeExpr = |
392 |
| - fromInlineDoc ? info.getType() : info.getReturnType(); |
| 383 | + JSTypeExpression returnTypeExpr = fromInlineDoc ? info.getType() : info.getReturnType(); |
393 | 384 | if (returnTypeExpr != null) {
|
394 | 385 | returnType = returnTypeExpr.evaluate(templateScope, typeRegistry);
|
395 | 386 | returnTypeInferred = false;
|
@@ -612,9 +603,7 @@ FunctionTypeBuilder inferThisType(JSDocInfo info) {
|
612 | 603 | return this;
|
613 | 604 | }
|
614 | 605 |
|
615 |
| - /** |
616 |
| - * Infer the parameter types from the doc info alone. |
617 |
| - */ |
| 606 | + /** Infer the parameter types from the doc info alone. */ |
618 | 607 | FunctionTypeBuilder inferParameterTypes(JSDocInfo info) {
|
619 | 608 | // Create a fake args parent.
|
620 | 609 | Node lp = IR.paramList();
|
@@ -648,7 +637,7 @@ FunctionTypeBuilder inferParameterTypes(@Nullable Node paramsParent, @Nullable J
|
648 | 637 | FunctionParamBuilder builder = new FunctionParamBuilder(typeRegistry);
|
649 | 638 | boolean warnedAboutArgList = false;
|
650 | 639 | Set<String> allJsDocParams =
|
651 |
| - (info == null) ? new HashSet<>() : new HashSet<>(info.getParameterNames()); |
| 640 | + (info == null) ? new LinkedHashSet<>() : new LinkedHashSet<>(info.getParameterNames()); |
652 | 641 | boolean isVarArgs = false;
|
653 | 642 | int paramIndex = 0;
|
654 | 643 | for (Node param = paramsParent.getFirstChild(); param != null; param = param.getNext()) {
|
@@ -694,19 +683,16 @@ FunctionTypeBuilder inferParameterTypes(@Nullable Node paramsParent, @Nullable J
|
694 | 683 | parameterType = parameterTypeExpression.evaluate(templateScope, typeRegistry);
|
695 | 684 | isOptionalParam = parameterTypeExpression.isOptionalArg();
|
696 | 685 | isVarArgs = parameterTypeExpression.isVarArgs();
|
697 |
| - } else if (oldParameterType != null && |
698 |
| - oldParameterType.getJSType() != null) { |
| 686 | + } else if (oldParameterType != null && oldParameterType.getJSType() != null) { |
699 | 687 | parameterType = oldParameterType.getJSType();
|
700 | 688 | isOptionalParam = oldParameterType.isOptional();
|
701 | 689 | isVarArgs = oldParameterType.isVariadic();
|
702 | 690 | } else {
|
703 | 691 | parameterType = typeRegistry.getNativeType(UNKNOWN_TYPE);
|
704 | 692 | }
|
705 | 693 |
|
706 |
| - warnedAboutArgList |= addParameter( |
707 |
| - builder, parameterType, warnedAboutArgList, |
708 |
| - isOptionalParam, |
709 |
| - isVarArgs); |
| 694 | + warnedAboutArgList |= |
| 695 | + addParameter(builder, parameterType, warnedAboutArgList, isOptionalParam, isVarArgs); |
710 | 696 |
|
711 | 697 | oldParameterType = oldParameters.hasNext() ? oldParameters.next() : null;
|
712 | 698 | paramIndex++;
|
@@ -781,7 +767,9 @@ private void setConstructorTemplateTypeNames(List<TemplateType> templates, @Null
|
781 | 767 | }
|
782 | 768 | }
|
783 | 769 |
|
784 |
| - /** @return Whether the given param is an optional param. */ |
| 770 | + /** |
| 771 | + * @return Whether the given param is an optional param. |
| 772 | + */ |
785 | 773 | private boolean isOptionalParameterByConvention(Node param) {
|
786 | 774 | if (param.isDestructuringPattern()) {
|
787 | 775 | return false;
|
@@ -897,17 +885,21 @@ FunctionTypeBuilder inferTemplateTypeName(@Nullable JSDocInfo info, @Nullable JS
|
897 | 885 |
|
898 | 886 | /**
|
899 | 887 | * Add a parameter to the param list.
|
| 888 | + * |
900 | 889 | * @param builder A builder.
|
901 | 890 | * @param paramType The parameter type.
|
902 |
| - * @param warnedAboutArgList Whether we've already warned about arg ordering |
903 |
| - * issues (like if optional args appeared before required ones). |
| 891 | + * @param warnedAboutArgList Whether we've already warned about arg ordering issues (like if |
| 892 | + * optional args appeared before required ones). |
904 | 893 | * @param isOptional Is this an optional parameter?
|
905 | 894 | * @param isVarArgs Is this a var args parameter?
|
906 | 895 | * @return Whether a warning was emitted.
|
907 | 896 | */
|
908 |
| - private boolean addParameter(FunctionParamBuilder builder, |
909 |
| - JSType paramType, boolean warnedAboutArgList, |
910 |
| - boolean isOptional, boolean isVarArgs) { |
| 897 | + private boolean addParameter( |
| 898 | + FunctionParamBuilder builder, |
| 899 | + JSType paramType, |
| 900 | + boolean warnedAboutArgList, |
| 901 | + boolean isOptional, |
| 902 | + boolean isVarArgs) { |
911 | 903 | boolean emittedWarning = false;
|
912 | 904 | if (isOptional) {
|
913 | 905 | // Remembering that an optional parameter has been encountered
|
@@ -985,18 +977,15 @@ private void provideDefaultReturnType() {
|
985 | 977 | }
|
986 | 978 | }
|
987 | 979 |
|
988 |
| - /** |
989 |
| - * Builds the function type, and puts it in the registry. |
990 |
| - */ |
| 980 | + /** Builds the function type, and puts it in the registry. */ |
991 | 981 | FunctionType buildAndRegister() {
|
992 | 982 | if (returnType == null) {
|
993 | 983 | provideDefaultReturnType();
|
994 | 984 | checkNotNull(returnType);
|
995 | 985 | }
|
996 | 986 |
|
997 | 987 | if (parameters == null) {
|
998 |
| - throw new IllegalStateException( |
999 |
| - "All Function types must have params and a return type"); |
| 988 | + throw new IllegalStateException("All Function types must have params and a return type"); |
1000 | 989 | }
|
1001 | 990 |
|
1002 | 991 | final FunctionType fnType;
|
@@ -1050,17 +1039,14 @@ private FunctionType.Builder createDefaultBuilder() {
|
1050 | 1039 | }
|
1051 | 1040 |
|
1052 | 1041 | /**
|
1053 |
| - * Returns a constructor function either by returning it from the |
1054 |
| - * registry if it exists or creating and registering a new type. If |
1055 |
| - * there is already a type, then warn if the existing type is |
1056 |
| - * different than the one we are creating, though still return the |
1057 |
| - * existing function if possible. The primary purpose of this is |
1058 |
| - * that registering a constructor will fail for all built-in types |
1059 |
| - * that are initialized in {@link JSTypeRegistry}. We a) want to |
1060 |
| - * make sure that the type information specified in the externs file |
1061 |
| - * matches what is in the registry and b) annotate the externs with |
1062 |
| - * the {@link JSType} from the registry so that there are not two |
1063 |
| - * separate JSType objects for one type. |
| 1042 | + * Returns a constructor function either by returning it from the registry if it exists or |
| 1043 | + * creating and registering a new type. If there is already a type, then warn if the existing type |
| 1044 | + * is different than the one we are creating, though still return the existing function if |
| 1045 | + * possible. The primary purpose of this is that registering a constructor will fail for all |
| 1046 | + * built-in types that are initialized in {@link JSTypeRegistry}. We a) want to make sure that the |
| 1047 | + * type information specified in the externs file matches what is in the registry and b) annotate |
| 1048 | + * the externs with the {@link JSType} from the registry so that there are not two separate JSType |
| 1049 | + * objects for one type. |
1064 | 1050 | */
|
1065 | 1051 | private FunctionType getOrCreateConstructor() {
|
1066 | 1052 | FunctionType fnType =
|
@@ -1090,17 +1076,17 @@ private FunctionType getOrCreateConstructor() {
|
1090 | 1076 | boolean isInstanceObject = existingType.isInstanceType();
|
1091 | 1077 | if (isInstanceObject || fnName.equals("Function")) {
|
1092 | 1078 | FunctionType existingFn =
|
1093 |
| - isInstanceObject ? |
1094 |
| - existingType.toObjectType().getConstructor() : |
1095 |
| - typeRegistry.getNativeFunctionType(FUNCTION_FUNCTION_TYPE); |
| 1079 | + isInstanceObject |
| 1080 | + ? existingType.toObjectType().getConstructor() |
| 1081 | + : typeRegistry.getNativeFunctionType(FUNCTION_FUNCTION_TYPE); |
1096 | 1082 |
|
1097 | 1083 | if (existingFn.getSource() == null) {
|
1098 | 1084 | existingFn.setSource(contents.getSourceNode());
|
1099 | 1085 | }
|
1100 | 1086 |
|
1101 | 1087 | if (!existingFn.hasEqualCallType(fnType)) {
|
1102 |
| - reportWarning(TYPE_REDEFINITION, formatFnName(), |
1103 |
| - fnType.toString(), existingFn.toString()); |
| 1088 | + reportWarning( |
| 1089 | + TYPE_REDEFINITION, formatFnName(), fnType.toString(), existingFn.toString()); |
1104 | 1090 | }
|
1105 | 1091 |
|
1106 | 1092 | // If the existing function is a built-in type, set its base type in case it @extends
|
@@ -1156,17 +1142,15 @@ private FunctionType getOrCreateInterface() {
|
1156 | 1142 | return fnType;
|
1157 | 1143 | }
|
1158 | 1144 |
|
1159 |
| - private void reportWarning(DiagnosticType warning, String ... args) { |
| 1145 | + private void reportWarning(DiagnosticType warning, String... args) { |
1160 | 1146 | compiler.report(JSError.make(errorRoot, warning, args));
|
1161 | 1147 | }
|
1162 | 1148 |
|
1163 |
| - private void reportError(DiagnosticType error, String ... args) { |
| 1149 | + private void reportError(DiagnosticType error, String... args) { |
1164 | 1150 | compiler.report(JSError.make(errorRoot, error, args));
|
1165 | 1151 | }
|
1166 | 1152 |
|
1167 |
| - /** |
1168 |
| - * Determines whether the given JsDoc info declares a function type. |
1169 |
| - */ |
| 1153 | + /** Determines whether the given JsDoc info declares a function type. */ |
1170 | 1154 | static boolean isFunctionTypeDeclaration(JSDocInfo info) {
|
1171 | 1155 | return info.getParameterCount() > 0
|
1172 | 1156 | || info.hasReturnType()
|
@@ -1276,6 +1260,4 @@ public boolean mayHaveSingleThrow() {
|
1276 | 1260 | return block.hasOneChild() && block.getFirstChild().isThrow();
|
1277 | 1261 | }
|
1278 | 1262 | }
|
1279 |
| - |
1280 |
| - |
1281 | 1263 | }
|
0 commit comments