Skip to content

Commit 83a0b84

Browse files
Francesco Zappa Nardellimeta-codesync[bot]
authored andcommitted
Sealed methods
Summary: This diff adds a method-level `__Sealed` attribute which controls which classes can override the method. The core logic is implemented in `Typing_extends.bad_sealed_override_error`. Reviewed By: enetsee Differential Revision: D57863499 fbshipit-source-id: c1037d6c042f8f808cde1c3d0dd462346a15086b
1 parent 4af5600 commit 83a0b84

63 files changed

Lines changed: 703 additions & 12 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

hphp/hack/src/decl/decl_class.ml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ let element_to_class_elt
9090
elt_visibility = ce_visibility;
9191
elt_deprecated = ce_deprecated;
9292
elt_sort_text = ce_sort_text;
93+
elt_sealed_allowlist = ce_sealed_allowlist;
9394
elt_overlapping_tparams = ce_overlapping_tparams;
9495
} :
9596
Decl_defs.element) : Typing_defs.class_elt =
@@ -103,6 +104,7 @@ let element_to_class_elt
103104
ce_deprecated;
104105
ce_pos;
105106
ce_flags;
107+
ce_sealed_allowlist;
106108
ce_sort_text;
107109
ce_overlapping_tparams;
108110
}

hphp/hack/src/decl/decl_defs.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ type element = {
103103
elt_origin: string;
104104
elt_visibility: ce_visibility;
105105
elt_deprecated: string option;
106+
elt_sealed_allowlist: SSet.t option;
106107
elt_sort_text: string option;
107108
(* Derived from <<__Overlapping(_)>> attribute *)
108109
elt_overlapping_tparams: SSet.t option;

hphp/hack/src/decl/decl_folded_class.ml

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -281,8 +281,8 @@ let synthesize_typeconst_defaults
281281
(typeconsts, consts)
282282
| _ -> (typeconsts, consts)
283283

284-
let get_sealed_whitelist (c : Shallow_decl_defs.shallow_class) : SSet.t option =
285-
match Attributes.find SN.UserAttributes.uaSealed c.sc_user_attributes with
284+
let extract_sealed_classnames attrs : SSet.t option =
285+
match attrs with
286286
| None -> None
287287
| Some { ua_params; _ } ->
288288
let cn_params =
@@ -294,6 +294,16 @@ let get_sealed_whitelist (c : Shallow_decl_defs.shallow_class) : SSet.t option =
294294
in
295295
Some (SSet.of_list cn_params)
296296

297+
let get_class_sealed_allowlist (c : Shallow_decl_defs.shallow_class) :
298+
SSet.t option =
299+
extract_sealed_classnames
300+
(Attributes.find SN.UserAttributes.uaSealed c.sc_user_attributes)
301+
302+
let get_method_sealed_allowlist (m : Shallow_decl_defs.shallow_method) :
303+
SSet.t option =
304+
extract_sealed_classnames
305+
(Attributes.find SN.UserAttributes.uaSealed m.sm_attributes)
306+
297307
let get_overlapping_tparams (m : Shallow_decl_defs.shallow_method) :
298308
SSet.t option =
299309
match Attributes.find SN.UserAttributes.uaOverlapping m.sm_attributes with
@@ -387,6 +397,7 @@ let build_constructor
387397
elt_visibility = vis;
388398
elt_origin = class_name;
389399
elt_deprecated = method_.sm_deprecated;
400+
elt_sealed_allowlist = None;
390401
elt_sort_text = method_.sm_sort_text;
391402
elt_overlapping_tparams = get_overlapping_tparams method_;
392403
}
@@ -521,6 +532,7 @@ let prop_decl_eager
521532
elt_visibility = vis;
522533
elt_origin;
523534
elt_deprecated = None;
535+
elt_sealed_allowlist = None;
524536
elt_sort_text = None;
525537
elt_overlapping_tparams = None;
526538
}
@@ -560,6 +572,7 @@ let static_prop_decl_eager
560572
elt_visibility = vis;
561573
elt_origin = snd c.sc_name;
562574
elt_deprecated = None;
575+
elt_sealed_allowlist = None;
563576
elt_sort_text = None;
564577
elt_overlapping_tparams = None;
565578
}
@@ -707,6 +720,7 @@ let method_decl_eager
707720
| _ -> visibility (snd c.sc_name) c.sc_module m.sm_visibility
708721
in
709722
let support_dynamic_type = sm_support_dynamic_type m in
723+
let sealed_allowlist = get_method_sealed_allowlist m in
710724
let parent_sort_text =
711725
match SMap.find_opt id acc with
712726
| Some ({ elt_sort_text = _ as parent_text; _ }, _) -> parent_text
@@ -745,6 +759,7 @@ let method_decl_eager
745759
elt_visibility = vis;
746760
elt_origin = snd c.sc_name;
747761
elt_deprecated = m.sm_deprecated;
762+
elt_sealed_allowlist = sealed_allowlist;
748763
elt_sort_text = sort_text;
749764
elt_overlapping_tparams = get_overlapping_tparams m;
750765
}
@@ -1000,7 +1015,7 @@ and class_decl
10001015
p
10011016
c.sc_tparams
10021017
in
1003-
let sealed_whitelist = get_sealed_whitelist c in
1018+
let sealed_whitelist = get_class_sealed_allowlist c in
10041019
let ua_sort_text =
10051020
match
10061021
Attributes.find

hphp/hack/src/decl/direct_decl_smart_constructors.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,7 @@ impl<'o, 't> DirectDeclSmartConstructors<'o, 't> {
332332
|| name == "__NoAutoDynamic"
333333
|| name == "__NoAutoLikes"
334334
|| name == "__Overlapping"
335+
|| name == "__Sealed"
335336
}
336337

337338
fn fold_string_concat(&self, expr: &nast::Expr, acc: &mut BString) -> bool {
@@ -3478,7 +3479,8 @@ impl<'o, 't> FlattenSmartConstructors for DirectDeclSmartConstructors<'o, 't> {
34783479
}
34793480

34803481
// Parse the user attributes
3481-
// in facts-mode all attributes are saved, otherwise only __NoAutoDynamic/__NoAutoLikes is
3482+
// in facts-mode all attributes are saved, otherwise only
3483+
// __NoAutoDynamic/__NoAutoLikes/__Overlapping and Sealed are
34823484
let user_attributes = attributes
34833485
.into_iter()
34843486
.rev()

hphp/hack/src/errors/error_codes.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -788,6 +788,7 @@ module Typing = struct
788788
| UnexpectedNamedArgs [@value 4500]
789789
| DuplicateNamedArgs [@value 4501]
790790
| LambdaNamedParamMismatch [@value 4502]
791+
| OverrideSealed [@value 4503]
791792
(* Add new Typing codes here! Comment out when deprecating. *)
792793
[@@deriving enum, show { with_path = false }]
793794

hphp/hack/src/hackrs/folded_decl_provider/fold.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@ impl<'a, R: Reason> DeclFolder<'a, R> {
314314
visibility: vis,
315315
deprecated: None,
316316
flags: ClassEltFlags::new(flag_args),
317+
sealed_allowlist: None,
317318
sort_text: None,
318319
overlapping_tparams: None,
319320
};
@@ -354,6 +355,7 @@ impl<'a, R: Reason> DeclFolder<'a, R> {
354355
visibility: vis,
355356
deprecated: None,
356357
flags: ClassEltFlags::new(flag_args),
358+
sealed_allowlist: None,
357359
sort_text: None,
358360
overlapping_tparams: None,
359361
};
@@ -389,6 +391,8 @@ impl<'a, R: Reason> DeclFolder<'a, R> {
389391
(_, v) => self.visibility(cls, self.child.module.as_ref().map(Positioned::id), v),
390392
};
391393

394+
let sealed_allowlist = self.get_method_sealed_allowlist(sm);
395+
392396
let sort_text = match sm.sort_text.to_owned() {
393397
Some(text) => Some(text),
394398
_ => match methods.get(&meth) {
@@ -429,6 +433,7 @@ impl<'a, R: Reason> DeclFolder<'a, R> {
429433
visibility: vis,
430434
deprecated: sm.deprecated,
431435
flags: ClassEltFlags::new(flag_args),
436+
sealed_allowlist,
432437
sort_text,
433438
overlapping_tparams: self.get_overlapping_tparams(sm),
434439
};
@@ -477,6 +482,7 @@ impl<'a, R: Reason> DeclFolder<'a, R> {
477482
visibility: vis,
478483
deprecated: sm.deprecated,
479484
flags: ClassEltFlags::new(flag_args),
485+
sealed_allowlist: None,
480486
sort_text: sm.sort_text.to_owned(),
481487
overlapping_tparams: None,
482488
}
@@ -823,12 +829,20 @@ impl<'a, R: Reason> DeclFolder<'a, R> {
823829
}
824830
}
825831

826-
fn get_sealed_whitelist(&self) -> Option<IndexSet<TypeName>> {
832+
fn get_class_sealed_whitelist(&self) -> Option<IndexSet<TypeName>> {
827833
(self.child.user_attributes.iter())
828834
.find(|ua| ua.name.id() == *sn::user_attributes::uaSealed)
829835
.map(|ua| ua.classname_params().iter().copied().collect())
830836
}
831837

838+
fn get_method_sealed_allowlist(&self, method: &ShallowMethod<R>) -> Option<IndexSet<TypeName>> {
839+
method
840+
.attributes
841+
.iter()
842+
.find(|ua| ua.name.id() == *sn::user_attributes::uaSealed)
843+
.map(|ua| ua.classname_params().iter().copied().collect())
844+
}
845+
832846
fn get_overlapping_tparams(&self, method: &ShallowMethod<R>) -> Option<IndexSet<Symbol>> {
833847
method
834848
.attributes
@@ -974,7 +988,7 @@ impl<'a, R: Reason> DeclFolder<'a, R> {
974988
});
975989
self.rewrite_class_consts_for_enum(enum_inner_ty, &ancestors, &mut consts);
976990

977-
let sealed_whitelist = self.get_sealed_whitelist();
991+
let sealed_whitelist = self.get_class_sealed_whitelist();
978992

979993
let deferred_init_members = self.get_deferred_init_members(&constructor.elt);
980994

hphp/hack/src/hackrs/ty/decl/folded.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ pub struct FoldedElement {
5252

5353
/// If the element is deprecated, this holds the deprecation message.
5454
pub deprecated: Option<Bytes>,
55+
pub sealed_allowlist: Option<IndexSet<TypeName>>,
5556
pub sort_text: Option<String>,
5657
pub overlapping_tparams: Option<IndexSet<Symbol>>,
5758
}

hphp/hack/src/hackrs/ty/decl/from_oxidized.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,7 @@ impl From<o::decl_defs::Element> for folded::FoldedElement {
608608
origin: x.origin.into(),
609609
visibility: x.visibility.into(),
610610
deprecated: x.deprecated.map(Into::into),
611+
sealed_allowlist: x.sealed_allowlist.map(map_k),
611612
sort_text: x.sort_text,
612613
overlapping_tparams: x.overlapping_tparams.map(map_k),
613614
}

hphp/hack/src/hackrs/ty/decl/to_oxidized.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,7 @@ impl ToOxidized for folded::FoldedElement {
558558
.deprecated
559559
.map(|x| String::from_utf8_lossy(x.as_bytes()).to_string()),
560560
flags: self.flags,
561+
sealed_allowlist: self.sealed_allowlist.to_oxidized(),
561562
sort_text: self.sort_text,
562563
overlapping_tparams: self.overlapping_tparams.to_oxidized(),
563564
}

hphp/hack/src/oxidized/gen/decl_defs.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// This source code is licensed under the MIT license found in the
44
// LICENSE file in the "hack" directory of this source tree.
55
//
6-
// @generated SignedSource<<b6ee7f07f15dfff40b614f3a29222450>>
6+
// @generated SignedSource<<36880d974d036f18d61136467e3900e6>>
77
//
88
// To regenerate this file, run:
99
// buck run @fbcode//mode/dev-nosan-lg fbcode//hphp/hack/src:oxidized_regen
@@ -169,6 +169,7 @@ pub struct Element {
169169
pub origin: String,
170170
pub visibility: CeVisibility,
171171
pub deprecated: Option<String>,
172+
pub sealed_allowlist: Option<s_set::SSet>,
172173
pub sort_text: Option<String>,
173174
pub overlapping_tparams: Option<s_set::SSet>,
174175
}

0 commit comments

Comments
 (0)