apply clang-tidy modernize-use-override
[hiphop-php.git] / hphp / compiler / expression / parameter_expression.cpp
blob6616c7d76d7f7b761456d271f56215c67b187ecf
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
16 #include "hphp/compiler/expression/parameter_expression.h"
17 #include "hphp/compiler/type_annotation.h"
18 #include "hphp/compiler/analysis/function_scope.h"
19 #include "hphp/compiler/analysis/file_scope.h"
20 #include "hphp/compiler/analysis/variable_table.h"
21 #include "hphp/compiler/analysis/class_scope.h"
22 #include "hphp/compiler/analysis/code_error.h"
23 #include "hphp/util/text-util.h"
24 #include "hphp/compiler/option.h"
25 #include "hphp/compiler/expression/constant_expression.h"
26 #include "hphp/runtime/vm/runtime.h"
27 #include "hphp/runtime/vm/type-constraint.h"
29 using namespace HPHP;
31 ///////////////////////////////////////////////////////////////////////////////
32 // constructors/destructors
34 ParameterExpression::ParameterExpression(
35 EXPRESSION_CONSTRUCTOR_PARAMETERS,
36 TypeAnnotationPtr type,
37 bool hhType,
38 const std::string &name,
39 bool ref,
40 TokenID modifier,
41 ExpressionPtr defaultValue,
42 ExpressionPtr attributeList,
43 bool variadic)
44 : Expression(EXPRESSION_CONSTRUCTOR_PARAMETER_VALUES(ParameterExpression))
45 , m_originalType(type)
46 , m_name(name)
47 , m_hhType(hhType)
48 , m_ref(ref)
49 , m_modifier(modifier)
50 , m_defaultValue(defaultValue)
51 , m_attributeList(attributeList)
52 , m_variadic(variadic)
54 m_type = toLower(type ? type->vanillaName() : "");
55 if (m_defaultValue) {
56 m_defaultValue->setContext(InParameterExpression);
60 ExpressionPtr ParameterExpression::clone() {
61 ParameterExpressionPtr exp(new ParameterExpression(*this));
62 Expression::deepCopy(exp);
63 exp->m_defaultValue = Clone(m_defaultValue);
64 exp->m_attributeList = Clone(m_attributeList);
65 return exp;
68 const std::string ParameterExpression::getOriginalTypeHint() const {
69 assert(hasTypeHint());
70 return m_originalType->vanillaName();
73 const std::string ParameterExpression::getUserTypeHint() const {
74 assert(hasUserType());
75 return m_originalType->fullName();
78 const std::string ParameterExpression::getTypeHintDisplayName() const {
79 auto name = m_originalType->vanillaName();
80 const char* str = name.c_str();
81 auto len = name.size();
82 if (len > 3 && tolower(str[0]) == 'h' && tolower(str[1]) == 'h' &&
83 str[2] == '\\') {
84 bool strip = false;
85 const char* stripped = str + 3;
86 switch (len - 3) {
87 case 3:
88 strip = (!strcasecmp(stripped, "int") ||
89 !strcasecmp(stripped, "num"));
90 break;
91 case 4: strip = !strcasecmp(stripped, "bool"); break;
92 case 5: strip = !strcasecmp(stripped, "float"); break;
93 case 6: strip = !strcasecmp(stripped, "string"); break;
94 case 8: strip = (!strcasecmp(stripped, "resource") ||
95 !strcasecmp(stripped, "arraykey"));
96 break;
97 default:
98 break;
100 if (strip) {
101 return stripped;
104 return name;
107 ///////////////////////////////////////////////////////////////////////////////
108 // parser functions
110 void ParameterExpression::parseHandler(FileScopeRawPtr file,
111 ClassScopePtr cls) {
112 // Trait has not been 'inlined' into using class so context is not available
113 if (!m_type.empty() && !cls->isTrait()) {
114 fixupSelfAndParentTypehints(cls);
116 if (m_defaultValue) {
117 compatibleDefault(file);
122 void ParameterExpression::fixupSelfAndParentTypehints(ClassScopePtr cls) {
123 if (m_type == "self") {
124 m_type = toLower(cls->getOriginalName());
125 } else if (m_type == "parent") {
126 if (!cls->getOriginalParent().empty()) {
127 m_type = toLower(cls->getOriginalParent());
132 ///////////////////////////////////////////////////////////////////////////////
133 // static analysis functions
135 void ParameterExpression::analyzeProgram(AnalysisResultPtr ar) {
136 if (m_defaultValue) m_defaultValue->analyzeProgram(ar);
139 ConstructPtr ParameterExpression::getNthKid(int n) const {
140 switch (n) {
141 case 0:
142 return m_defaultValue;
143 default:
144 assert(false);
145 break;
147 return ConstructPtr();
150 int ParameterExpression::getKidCount() const {
151 return 1;
154 void ParameterExpression::setNthKid(int n, ConstructPtr cp) {
155 switch (n) {
156 case 0:
157 m_defaultValue = dynamic_pointer_cast<Expression>(cp);
158 break;
159 default:
160 break;
164 static bool useHackTypeHintErrorMessage(const char* hint) {
165 String typeName(hint);
166 MaybeDataType dt = nameToMaybeDataType(typeName.get());
167 if (!dt.hasValue()) {
168 return false;
170 switch (*dt) {
171 case KindOfBoolean:
172 case KindOfInt64:
173 case KindOfDouble:
174 case KindOfString:
175 case KindOfResource:
176 return true;
177 default:
178 return false;
182 void ParameterExpression::compatibleDefault(FileScopeRawPtr file) {
183 bool compat = true;
184 if (!m_defaultValue || !hasTypeHint()) return;
186 DataType defaultType = KindOfUninit;
187 if (m_defaultValue->isArray()) {
188 defaultType = KindOfArray;
189 } else if (m_defaultValue->isLiteralNull()) {
190 defaultType = KindOfNull;
191 } else {
192 Variant defaultValue;
193 if (m_defaultValue->getScalarValue(defaultValue)) {
194 defaultType = defaultValue.getType();
198 const char* hint = getTypeHint().c_str();
199 // type declarations mean that we must accept arbitrary types for
200 // arbitrary strings in HipHopSyntax mode. But we can still enforce
201 // the known types.
202 auto const acceptAny = m_hhType && (strcasecmp(hint, "HH\\bool") &&
203 strcasecmp(hint, "HH\\int") &&
204 strcasecmp(hint, "HH\\num") &&
205 strcasecmp(hint, "HH\\arraykey") &&
206 strcasecmp(hint, "HH\\float") &&
207 strcasecmp(hint, "HH\\num") &&
208 strcasecmp(hint, "HH\\string") &&
209 strcasecmp(hint, "HH\\vec") &&
210 strcasecmp(hint, "HH\\dict") &&
211 strcasecmp(hint, "HH\\keyset") &&
212 strcasecmp(hint, "HH\\varray") &&
213 strcasecmp(hint, "HH\\darray") &&
214 strcasecmp(hint, "HH\\varray_or_darray") &&
215 strcasecmp(hint, "array"));
217 // Normally a named type like 'int' is compatible with Int but not integer
218 // Since the default value's type is inferred from the value itself it is
219 // ok to compare against the lower case version of the type hint in hint
220 [&] {
221 switch (defaultType) {
222 case KindOfUninit:
223 // PHP 7 allows user defined constants as parameter default values
224 compat = m_hhType || RuntimeOption::PHP7_ScalarTypes;
225 return;
226 case KindOfNull:
227 compat = true;
228 return;
230 case KindOfBoolean:
231 compat = acceptAny || !strcasecmp(hint, "HH\\bool");
232 return;
234 case KindOfInt64:
235 compat = (acceptAny ||
236 !strcasecmp(hint, "HH\\int") ||
237 !strcasecmp(hint, "HH\\num") ||
238 !strcasecmp(hint, "HH\\arraykey") ||
239 // PHP 7 allows widening conversions
240 (RuntimeOption::PHP7_ScalarTypes &&
241 !strcasecmp(hint, "HH\\float")));
242 return;
244 case KindOfDouble:
245 compat = (acceptAny ||
246 !strcasecmp(hint, "HH\\float") ||
247 !strcasecmp(hint, "HH\\num"));
248 return;
250 case KindOfPersistentString:
251 case KindOfString:
252 compat = (acceptAny ||
253 !strcasecmp(hint, "HH\\string") ||
254 !strcasecmp(hint, "HH\\arraykey"));
255 return;
257 case KindOfPersistentVec:
258 case KindOfVec:
259 compat = (acceptAny || !strcasecmp(hint, "HH\\vec"));
260 return;
262 case KindOfPersistentDict:
263 case KindOfDict:
264 compat = (acceptAny || !strcasecmp(hint, "HH\\dict"));
265 return;
267 case KindOfPersistentKeyset:
268 case KindOfKeyset:
269 compat = (acceptAny || !strcasecmp(hint, "HH\\keyset"));
270 return;
272 case KindOfPersistentArray:
273 case KindOfArray:
274 compat = (acceptAny ||
275 !strcasecmp(hint, "array") ||
276 !strcasecmp(hint, "HH\\varray") ||
277 !strcasecmp(hint, "HH\\darray") ||
278 !strcasecmp(hint, "HH\\varray_or_darray")
280 return;
282 case KindOfObject:
283 case KindOfResource:
284 case KindOfRef:
285 if (!m_hhType) {
286 compat = false;
287 return;
289 break;
291 always_assert(false /* likely parser bug */);
292 }();
294 if (!compat) {
295 const char* msg = "Default value for parameter %s with type %s "
296 "needs to have the same type as the type hint %s";
297 if (!m_hhType) {
298 if (!strcasecmp(hint, "array")) {
299 msg = "Default value for parameter %s with array type hint "
300 "can only be an array or NULL";
301 } else if (!useHackTypeHintErrorMessage(hint)) {
302 msg = "Default value for parameter %s with a class type hint "
303 "can only be NULL";
307 auto const& name = getName();
308 auto const tdefault = HPHP::tname(defaultType);
309 parseTimeFatal(file,
310 Compiler::BadDefaultValueType, msg,
311 name.c_str(), tdefault.c_str(),
312 getTypeHintDisplayName().c_str());
316 ///////////////////////////////////////////////////////////////////////////////
317 // code generation functions
319 void ParameterExpression::outputPHP(CodeGenerator &cg, AnalysisResultPtr ar) {
320 if (!m_type.empty()) cg_printf("%s ", m_originalType->vanillaName().c_str());
321 if (m_ref) cg_printf("&");
322 cg_printf("$%s", m_name.c_str());
323 if (m_defaultValue) {
324 cg_printf(" = ");
325 m_defaultValue->outputPHP(cg, ar);