2 * Copyright (c) Facebook, Inc. and its affiliates.
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the "hack" directory of this source tree.
10 module Codes
= Lints_codes.Codes
12 module Lints
= Lints_core
19 ("Objects created with `clone` will have references to shared "
20 ^
"deep objects. Prefer to implement your own explicit copy "
21 ^
"method to ensure the semantics you want. See "
22 ^
"http://php.net/manual/en/language.oop5.cloning.php")
24 let deprecated p msg
= Lints.add
Codes.deprecated Lint_warning p msg
26 let include_use p msg
= Lints.add
Codes.include_use Lint_error p msg
28 let if_literal p msg
= Lints.add
Codes.if_literal Lint_warning p msg
35 ("Do not use `await` in a loop. It almost always incurs "
36 ^
"non-obvious serial fetching that is easy to miss. "
37 ^
"See https://fburl.com/awaitinloop for more information.")
39 let loop_variable_shadows_local_variable p1 id p2
=
41 Codes.loop_variable_shadows_local_variable
45 "Loop variable %s shadows a local variable defined or last assigned here:\n%s"
46 (Local_id.get_name id
|> Markdown_lite.md_codify
)
47 (Pos.string (Pos.to_relative_string p2
)))
49 let bad_virtualized_method p
=
51 Codes.bool_method_return_hint
54 "`__bool` methods should return `bool`. They show that an expression tree type can be used in a boolean expression."
56 let non_equatable_comparison p ret ty1 ty2
=
57 Lints.add
Codes.non_equatable_comparison Lint_warning p
59 "Invalid comparison: This expression will always return %s.\nA value of type %s can never be equal to a value of type %s"
60 (string_of_bool ret
|> Markdown_lite.md_codify
)
61 (Markdown_lite.md_codify ty1
)
62 (Markdown_lite.md_codify ty2
)
64 let invalid_contains_check p trv_val_ty val_ty
=
65 Lints.add
Codes.invalid_contains_check Lint_warning p
67 "Invalid `C\\contains` check: This call will always return `false`.\nA `Traversable<%s>` cannot contain a value of type %s"
69 (Markdown_lite.md_codify val_ty
)
71 let invalid_contains_key_check p trv_key_ty key_ty
=
72 Lints.add
Codes.invalid_contains_check Lint_warning p
74 "Invalid `C\\contains_key` check: This call will always return `false`.\nA `KeyedTraversable<%s, ...>` cannot contain a key of type %s"
76 (Markdown_lite.md_codify key_ty
)
78 let non_equatable_due_to_opaque_types p ty1 ty2 enums
=
79 Lints.add
Codes.non_equatable_comparison Lint_warning p
81 "Invalid comparison:\nA value of type %s should not be compared to a value of type %s%s"
82 (Markdown_lite.md_codify ty1
)
83 (Markdown_lite.md_codify ty2
)
89 " because the enum type%s %s %s opaque.\nUse functions like `%s::coerce` and `%s::assert` to convert values to the appropriate type before comparing."
90 (if List.length enums
= 1 then
98 (Markdown_lite.md_codify a
)
99 (Markdown_lite.md_codify b
)
100 | _
-> String.concat ~sep
:", " enums
)
101 (if List.length enums
= 1 then
109 let is_always_true p lhs_class rhs_class
=
110 let lhs_class = Markdown_lite.md_codify
lhs_class in
111 let rhs_class = Markdown_lite.md_codify
rhs_class in
117 "This `is` check is always `true`. The expression on the left is an instance of %s. It is always an instance of %s because %s derives from %s."
123 let is_always_false p
lhs_class rhs_class =
124 let lhs_class = Markdown_lite.md_codify
lhs_class in
125 let rhs_class = Markdown_lite.md_codify
rhs_class in
127 Codes.is_always_false
131 "This `is` check is always `false`. The expression on the left is an instance of %s. It can never be an instance of %s because there is no class that is both %s and %s."
137 let as_always_succeeds p
lhs_class rhs_class =
138 let lhs_class = Markdown_lite.md_codify
lhs_class in
139 let rhs_class = Markdown_lite.md_codify
rhs_class in
141 Codes.as_always_succeeds
145 "This `as` assertion will always succeed and hence is redundant. The expression on the left is an instance of %s. It is always an instance of %s because %s derives from %s."
151 let as_always_fails p
lhs_class rhs_class =
152 let lhs_class = Markdown_lite.md_codify
lhs_class in
153 let rhs_class = Markdown_lite.md_codify
rhs_class in
155 Codes.as_always_fails
159 "This `as` assertion will always fail. The expression on the left is an instance of %s. It can never be an instance of %s because there is no class that is both %s and %s."
165 let as_invalid_type pos var hint
=
167 Codes.as_invalid_type
171 "A value of type %s will always throw an exception when refining to %s"
172 (Markdown_lite.md_codify var
)
173 (Markdown_lite.md_codify hint
))
175 let class_overrides_all_trait_methods pos class_name trait_name
=
177 Codes.class_overrides_all_trait_methods
181 "Unused trait: %s is overriding all the methods in %s"
182 (Utils.strip_ns class_name
|> Markdown_lite.md_codify
)
183 (Utils.strip_ns trait_name
|> Markdown_lite.md_codify
))
185 let invalid_null_check p ret ty
=
186 Lints.add
Codes.invalid_null_check Lint_warning p
188 "Invalid null check: This expression will always return %s.\nA value of type %s can never be null."
189 (string_of_bool ret
|> Markdown_lite.md_codify
)
190 (Markdown_lite.md_codify ty
)
192 let redundant_nonnull_assertion p ty
=
193 Lints.add
Codes.redundant_nonnull_assertion Lint_warning p
195 "This `as` assertion will always succeed and hence is redundant. A value of type %s can never be null."
196 (Markdown_lite.md_codify ty
)
198 let invalid_disjointness_check p name ty1 ty2
=
199 Lints.add
Codes.invalid_disjointness_check Lint_warning p
201 "This call to '%s' will always return the same value, because type %s is disjoint from type %s."
206 let invalid_switch_case_value_type
207 (case_value_p
: Ast_defs.pos
) case_value_ty scrutinee_ty
=
208 Lints.add
Codes.invalid_switch_case_value_type Lint_warning case_value_p
210 "Switch statements use `===` equality. Comparing values of type %s with %s may not give the desired result."
211 (Markdown_lite.md_codify
@@ Lazy.force case_value_ty
)
212 (Markdown_lite.md_codify
@@ Lazy.force scrutinee_ty
)
214 let missing_override_attribute p ~class_name ~method_name
=
217 "Method %s is also defined on %s, but this method is missing `<<__Override>>`."
218 (Markdown_lite.md_codify method_name
)
219 (Utils.strip_ns class_name
|> Markdown_lite.md_codify
)
221 Lints.add
Codes.missing_override_attribute Lint_error p
@@ msg
223 let sketchy_null_check pos name kind
=
224 let name = Option.value name ~default
:"$x" in
225 Lints.add
Codes.sketchy_null_check Lint_warning pos
226 @@ "This is a sketchy null check.\nIt detects nulls, but it will also detect many other falsy values, including `false`, `0`, `0.0`, `\"\"`, `\"0\"`, empty Containers, and more.\nIf you want to test for them, please consider doing so explicitly.\nIf you only meant to test for `null`, "
230 Printf.sprintf
"use `%s ?? $default` instead of `%s ?: $default`" name name
231 | `Eq
-> Printf.sprintf
"use `%s is null` instead" name
232 | `Neq
-> Printf.sprintf
"use `%s is nonnull` instead" name
234 let invalid_truthiness_test pos ty
=
235 Lints.add
Codes.invalid_truthiness_test Lint_warning pos
237 "Invalid condition: a value of type %s will always be truthy"
238 (Markdown_lite.md_codify ty
)
240 let invalid_truthiness_test_falsy pos ty
=
241 Lints.add
Codes.invalid_truthiness_test Lint_warning pos
243 "Invalid condition: a value of type %s will always be falsy"
244 (Markdown_lite.md_codify ty
)
246 let sketchy_truthiness_test pos ty truthiness
=
247 Lints.add
Codes.sketchy_truthiness_test Lint_warning pos
249 match truthiness
with
252 "Sketchy condition: testing the truthiness of %s may not behave as expected.\nThe values `\"\"` and `\"0\"` are both considered falsy. To check for emptiness, use `Str\\is_empty`."
256 "Sketchy condition: testing the truthiness of %s may not behave as expected.\nThe values `0`, `\"\"`, and `\"0\"` are all considered falsy. Test for them explicitly."
260 "Sketchy condition: testing the truthiness of a %s may not behave as expected.\nThe values `\"\"` and `\"0\"` are both considered falsy, but objects will be truthy even if their `__toString` returns `\"\"` or `\"0\"`.\nTo check for emptiness, convert to a string and use `Str\\is_empty`."
264 "Sketchy condition: testing the truthiness of an %s may not behave as expected.\nThe values `\"\"` and `\"0\"` are both considered falsy, but objects (including XHP elements) will be truthy even if their `__toString` returns `\"\"` or `\"0\"`."
267 (* We have a truthiness test on a value with an interface type which is a
268 subtype of Traversable, but not a subtype of Container.
269 Since the runtime value may be a falsy-when-empty Container or an
270 always-truthy Iterable/Generator, we forbid the test. *)
272 "Sketchy condition: a value of type %s may be truthy even when empty.\nHack collections and arrays are falsy when empty, but user-defined Traversables will always be truthy, even when empty.\nIf you would like to only allow containers which are falsy when empty, use the `Container` or `KeyedContainer` interfaces."
275 let redundant_covariant pos
name msg suggest
=
276 Lints.add
Codes.redundant_generic Lint_warning pos
277 @@ "The generic parameter "
278 ^
Markdown_lite.md_codify
name
279 ^
" is redundant because it only appears in a covariant (output) position"
281 ^
". Consider replacing uses of generic parameter with "
282 ^
Markdown_lite.md_codify suggest
283 ^
" or specifying `<<__Explicit>>` on the generic parameter"
285 let redundant_contravariant pos
name msg suggest
=
286 Lints.add
Codes.redundant_generic Lint_warning pos
287 @@ "The generic parameter "
288 ^
Markdown_lite.md_codify
name
289 ^
" is redundant because it only appears in a contravariant (input) position"
291 ^
". Consider replacing uses of generic parameter with "
292 ^
Markdown_lite.md_codify suggest
293 ^
" or specifying `<<__Explicit>>` on the generic parameter"
295 let redundant_generic pos
name =
296 Lints.add
Codes.redundant_generic Lint_warning pos
298 "The generic parameter %s is unused."
299 (Markdown_lite.md_codify
name)
301 let inferred_variance pos
name description syntax
=
302 Lints.add
Codes.inferred_variance Lint_advice pos
303 @@ "The generic parameter "
304 ^
Markdown_lite.md_codify
name
305 ^
" could be marked "
307 ^
". Consider prefixing it with "
310 let nullsafe_not_needed pos
=
311 Lints.add
Codes.nullsafe_not_needed Lint_advice pos
312 @@ "You are using the "
313 ^
Markdown_lite.md_codify
"?->"
314 ^
" operator but this object cannot be null."
315 ^
" You can use the "
316 ^
Markdown_lite.md_codify
"->"
317 ^
" operator instead."
319 let invalid_attribute_value
320 pos
(attr_name
: string) (valid_values
: string list
) =
321 let valid_values = List.map
valid_values ~f
:Markdown_lite.md_codify
in
323 Codes.bad_xhp_enum_attribute_value
327 "Invalid value for %s, expected one of %s."
328 (Markdown_lite.md_codify attr_name
)
329 (String.concat ~sep
:", " valid_values))
331 let parse_error code pos
msg = Lints.add code Lint_error pos
msg
333 let rec prettify_class_list names
=
337 | [c1
; c2
] -> c1 ^
" and " ^ c2
338 | h
:: t
-> h ^
", " ^
prettify_class_list t
340 let duplicate_property_class_constant_init
341 pos ~class_name ~prop_name ~class_names
=
343 Codes.duplicate_property_enum_init
347 ^
(Utils.strip_ns prop_name
|> Markdown_lite.md_codify
)
349 ^
prettify_class_list (List.map ~f
:Utils.strip_ns class_names
)
350 ^
", is inherited multiple times by class "
351 ^
(Utils.strip_ns class_name
|> Markdown_lite.md_codify
)
352 ^
" and one of its instances is initialised with a class or enum constant")
354 let duplicate_property pos ~class_name ~prop_name ~class_names
=
356 Codes.duplicate_property
359 ("Duplicated property "
360 ^
(Utils.strip_ns prop_name
|> Markdown_lite.md_codify
)
362 ^
(Utils.strip_ns class_name
|> Markdown_lite.md_codify
)
364 ^
prettify_class_list
366 ~f
:(fun n
-> Utils.strip_ns n
|> Markdown_lite.md_codify
)
368 ^
"): all instances will be aliased at runtime")
370 let loose_unsafe_cast_lower_bound p ty_str_opt
=
372 "HH\\FIXME\\UNSAFE_CAST input type annotation is too loose, please use a more specific type."
375 match ty_str_opt
with
378 ^
" The typechecker infers "
379 ^
Markdown_lite.md_codify ty_str
380 ^
" as the most specific type."
383 Lints.add
Codes.loose_unsafe_cast_lower_bound Lint_error p
msg
385 let loose_unsafe_cast_upper_bound p
=
387 Codes.loose_unsafe_cast_upper_bound
390 "HH\\FIXME\\UNSAFE_CAST output type annotation is too loose, please use a more specific type."
392 let switch_nonexhaustive p
=
394 Codes.switch_nonexhaustive
397 ("This switch statement is not exhaustive."
398 ^
" The expression it scrutinises has a type with infinitely many values and the statement does not have a default case."
399 ^
" If none of the cases match, an exception will be thrown."
400 ^
" Consider adding a default case.")