1 // Copyright (c) Facebook, Inc. and its affiliates.
3 // This source code is licensed under the MIT license found in the
4 // LICENSE file in the "hack" directory of this source tree.
6 use std::cmp::Ordering;
9 use crate::message::Message;
11 use crate::pos_or_decl::PosOrDecl;
12 use crate::quickfix::Quickfix;
13 use crate::user_error::UserError;
15 impl<PP, P> UserError<PP, P> {
19 reasons: Vec<Message<P>>,
20 quickfixes: Vec<Quickfix>,
30 pub fn pos(&self) -> &PP {
31 let Message(pos, _msg) = &self.claim;
35 pub fn code(&self) -> ErrorCode {
41 fn cmp_file(&self, other: &Self) -> Ordering;
44 impl FileOrd for Pos {
45 fn cmp_file(&self, other: &Self) -> Ordering {
46 self.filename().cmp(other.filename())
50 impl<PP: Ord + FileOrd, P: Ord + FileOrd> Ord for UserError<PP, P> {
51 // Intended to match the implementation of `compare` in `Errors.sort` in OCaml.
52 fn cmp(&self, other: &Self) -> Ordering {
56 reasons: self_reasons,
62 reasons: other_reasons,
65 let Message(self_pos, self_msg) = self_claim;
66 let Message(other_pos, other_msg) = other_claim;
67 // The primary sort order is by file of the claim (main message).
70 // If the files are the same, sort by phase.
71 .then(((*self_code / 1000) as isize).cmp(&((*other_code / 1000) as isize)))
72 // If the phases are the same, sort by position.
73 .then(self_pos.cmp(other_pos))
74 // If the positions are the same, sort by claim message text.
75 .then(self_msg.cmp(other_msg))
76 // If the claim message text is the same, compare the reason
77 // messages (which contain further explanation for the error
78 // reported in the claim message).
79 .then(self_reasons.iter().cmp(other_reasons.iter()))
83 impl<PP: Ord + FileOrd, P: Ord + FileOrd> PartialOrd for UserError<PP, P> {
84 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
89 impl UserError<Pos, PosOrDecl> {
90 /// Return a struct with a `std::fmt::Display` implementation that displays
91 /// the error in the "raw" format expected by our typecheck test cases.
92 pub fn display_raw(&self) -> DisplayRaw<'_> {
97 pub struct DisplayRaw<'a>(&'a UserError<Pos, PosOrDecl>);
99 impl<'a> std::fmt::Display for DisplayRaw<'a> {
100 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107 let Message(pos, msg) = claim;
108 let code = DisplayErrorCode(*code);
109 write!(f, "{}\n{} ({})", pos.string(), msg, code)?;
110 for Message(pos, msg) in reasons.iter() {
111 write!(f, "\n {}\n {}", pos.string(), msg)?;
117 fn error_kind(error_code: ErrorCode) -> &'static str {
118 match error_code / 1000 {
129 struct DisplayErrorCode(ErrorCode);
131 impl std::fmt::Display for DisplayErrorCode {
132 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
133 write!(f, "{}[{:04}]", error_kind(self.0), self.0)
138 pub fn empty() -> Self {
139 Errors(FilesT::new(), FilesT::new())
142 pub fn is_empty(&self) -> bool {
143 let Errors(errors, _fixmes) = self;
147 pub fn into_vec(self) -> Vec<Error> {
148 let Errors(errors, _fixmes) = self;
151 .flat_map(|(_filename, errs_by_phase)| {
154 .flat_map(|(_phase, errs)| errs.into_iter())
159 pub fn into_sorted_vec(self) -> Vec<Error> {
160 let mut errors = self.into_vec();
161 errors.sort_unstable();
168 pub fn fd_name_already_bound(p: Pos) -> Error {
170 Self::FdNameAlreadyBound as isize,
171 Message(p, "Field name already bound".into()),
177 pub fn method_needs_visibility(p: Pos) -> Error {
179 Self::MethodNeedsVisibility as isize,
182 "Methods need to be marked public, private, or protected.".into(),
189 pub fn unsupported_trait_use_as(p: Pos) -> Error {
191 Self::UnsupportedTraitUseAs as isize,
194 "Trait use as is a PHP feature that is unsupported in Hack".into(),
201 pub fn unsupported_instead_of(p: Pos) -> Error {
203 Self::UnsupportedInsteadOf as isize,
206 "insteadof is a PHP feature that is unsupported in Hack".into(),
213 pub fn invalid_trait_use_as_visibility(p: Pos) -> Error {
215 Self::InvalidTraitUseAsVisibility as isize,
218 "Cannot redeclare trait method's visibility in this manner".into(),
227 pub fn not_abstract_without_typeconst(p: Pos) -> Error {
229 Self::NotAbstractWithoutTypeconst as isize,
232 "This type constant is not declared as abstract, it must have an assigned type"
240 pub fn multiple_xhp_category(p: Pos) -> Error {
242 Self::MultipleXhpCategory as isize,
245 "XHP classes can only contain one category declaration".into(),
252 pub fn partially_abstract_typeconst_definition(p: Pos) -> Error {
254 Self::PartiallyAbstractTypeconstDefinition as isize,
257 "`as` constraints are only legal on abstract type constants".into(),