-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
FunctionTypeBuilder.java
184 lines (164 loc) · 6.06 KB
/
FunctionTypeBuilder.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
/*
* Copyright 2013 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp.newtypes;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* A builder for {@link FunctionType} and {@link DeclaredFunctionType}.
*
* The builder is called during both JSDoc parsing and type inference, and
* these parts use different warning systems, so expect the context to handle
* the exception appropriately.
*
* @author blickly@google.com (Ben Lickly)
* @author dimvar@google.com (Dimitris Vardoulakis)
*/
public final class FunctionTypeBuilder {
static class WrongParameterOrderException extends RuntimeException {
WrongParameterOrderException(String message) {
super(message);
}
}
private final List<JSType> requiredFormals = new ArrayList<>();
private final List<JSType> optionalFormals = new ArrayList<>();
private final Map<String, JSType> outerVars = new LinkedHashMap<>();
private JSType restFormals = null;
private JSType returnType = null;
private boolean loose = false;
private boolean isAbstract = false;
private JSType nominalType;
// Only used to build DeclaredFunctionType for prototype methods
private JSType receiverType;
// Non-empty iff this function has an @template annotation
private ImmutableList<String> typeParameters = ImmutableList.of();
private final JSTypes commonTypes;
public FunctionTypeBuilder(JSTypes commonTypes) {
this.commonTypes = Preconditions.checkNotNull(commonTypes);
}
/**
* Used when the order of required/optional/rest formals in a function jsdoc is wrong.
*/
public FunctionTypeBuilder addPlaceholderFormal() {
if (restFormals != null) {
// Nothing to do here, since there is no way to add a placeholder.
} else if (!optionalFormals.isEmpty()) {
optionalFormals.add(this.commonTypes.UNKNOWN);
} else {
requiredFormals.add(this.commonTypes.UNKNOWN);
}
return this;
}
public FunctionTypeBuilder addReqFormal(JSType t) {
if (!optionalFormals.isEmpty() || restFormals != null) {
throw new WrongParameterOrderException(
"Cannot add required formal after optional or rest args");
}
requiredFormals.add(t);
return this;
}
public FunctionTypeBuilder addOptFormal(JSType t) {
if (restFormals != null) {
throw new WrongParameterOrderException(
"Cannot add optional formal after rest args");
}
if (t == null) {
optionalFormals.add(null);
} else {
Preconditions.checkArgument(!t.isBottom());
optionalFormals.add(JSType.join(t, this.commonTypes.UNDEFINED));
}
return this;
}
public FunctionTypeBuilder addOuterVarPrecondition(String name, JSType t) {
outerVars.put(name, t);
return this;
}
public FunctionTypeBuilder addRestFormals(JSType t) {
Preconditions.checkState(restFormals == null);
restFormals = t;
return this;
}
public FunctionTypeBuilder addRetType(JSType t) {
Preconditions.checkState(returnType == null);
returnType = t;
return this;
}
public FunctionTypeBuilder addLoose() {
loose = true;
return this;
}
public FunctionTypeBuilder addAbstract(boolean isAbstract) {
this.isAbstract = isAbstract;
return this;
}
public FunctionTypeBuilder addNominalType(JSType t) {
Preconditions.checkState(this.nominalType == null);
this.nominalType = t;
return this;
}
public FunctionTypeBuilder addTypeParameters(ImmutableList<String> typeParameters) {
Preconditions.checkNotNull(typeParameters);
Preconditions.checkState(this.typeParameters.isEmpty());
this.typeParameters = typeParameters;
return this;
}
public FunctionTypeBuilder appendTypeParameters(ImmutableList<String> typeParameters) {
Preconditions.checkNotNull(typeParameters);
ImmutableList.Builder<String> newTypeParams = ImmutableList.builder();
newTypeParams.addAll(this.typeParameters);
newTypeParams.addAll(typeParameters);
this.typeParameters = newTypeParams.build();
return this;
}
public FunctionTypeBuilder addReceiverType(JSType t) {
// this.receiverType is not always null here, because of prototype methods
// with an explicit @this annotation
this.receiverType = t;
return this;
}
public DeclaredFunctionType buildDeclaration() {
Preconditions.checkState(!loose);
Preconditions.checkState(outerVars.isEmpty());
return DeclaredFunctionType.make(
this.commonTypes,
requiredFormals, optionalFormals, restFormals, returnType,
nominalType, receiverType, typeParameters, isAbstract);
}
public FunctionType buildFunction() {
// qmarkFunctionBuilder().buildDeclaration creates a non-loose function,
// we change that here to have a unique representation in FunctionType.
if (this.requiredFormals.isEmpty()
&& this.optionalFormals.isEmpty()
&& this.restFormals != null && this.restFormals.isUnknown()
&& this.returnType != null && this.returnType.isUnknown()
&& this.nominalType == null
&& this.receiverType == null
&& this.typeParameters.isEmpty()
&& this.outerVars.isEmpty()) {
return this.commonTypes.QMARK_FUNCTION;
}
FunctionType result = FunctionType.normalized(
this.commonTypes,
requiredFormals, optionalFormals, restFormals, returnType,
nominalType, receiverType, outerVars, typeParameters, loose, isAbstract);
result.checkValid();
return result;
}
}