⭐️ A friendly language for building type-safe, scalable systems!

Move syntax error printing

Now it's consistent with the way we print other error messages and
allows us greater control, like adding additional extra labels and such.

authored by giacomocavalieri.me and committed by Louis Pilfold 6fde375c 66223aad

+1015 -18
compiler-core/src/error.rs
··· 2 2 use crate::build::{Origin, Outcome, Runtime, Target}; 3 3 use crate::dependency::{PackageFetcher, ResolutionError}; 4 4 use crate::diagnostic::{Diagnostic, ExtraLabel, Label, Location}; 5 + use crate::parse::Token; 5 6 use crate::strings::{to_snake_case, to_upper_camel_case}; 6 7 use crate::type_::collapse_links; 7 8 use crate::type_::error::{ ··· 3757 3758 3758 3759 3759 3760 Error::Parse { path, src, error } => { 3760 - let (label, extra) = error.details(); 3761 - let text = extra.join("\n"); 3762 - 3763 - let adjusted_location = if error.error == ParseErrorType::UnexpectedEof { 3761 + let location = if error.error == ParseErrorType::UnexpectedEof { 3764 3762 crate::ast::SrcSpan { 3765 3763 start: (src.len() - 1) as u32, 3766 3764 end: (src.len() - 1) as u32, ··· 3769 3767 error.location 3770 3768 }; 3771 3769 3772 - vec![Diagnostic { 3773 - title: "Syntax error".into(), 3774 - text, 3775 - hint: None, 3776 - level: Level::Error, 3777 - location: Some(Location { 3778 - label: Label { 3779 - text: Some(label.to_string()), 3780 - span: adjusted_location, 3770 + let title = String::from("Syntax error"); 3771 + let diagnostic = 3772 + match &error.error { 3773 + ParseErrorType::ExpectedEqual => 3774 + Diagnostic { 3775 + title, 3776 + text: "".into(), 3777 + hint: None, 3778 + level: Level::Error, 3779 + location: Some(Location { 3780 + label: Label { 3781 + text: Some("I was expecting a '=' after this".into()), 3782 + span: location 3783 + }, 3784 + path: path.clone(), 3785 + src: src.clone(), 3786 + extra_labels: vec![], 3787 + }), 3788 + }, 3789 + 3790 + ParseErrorType::ExpectedExpr => 3791 + Diagnostic { 3792 + title, 3793 + text: "".into(), 3794 + hint: None, 3795 + level: Level::Error, 3796 + location: Some(Location { 3797 + label: Label { 3798 + text: Some("I was expecting an expression after this".into()), 3799 + span: location 3800 + }, 3801 + path: path.clone(), 3802 + src: src.clone(), 3803 + extra_labels: vec![], 3804 + }), 3805 + }, 3806 + 3807 + ParseErrorType::ExpectedName => 3808 + Diagnostic { 3809 + title, 3810 + text: "".into(), 3811 + hint: None, 3812 + level: Level::Error, 3813 + location: Some(Location { 3814 + label: Label { 3815 + text: Some("I was expecting a name here".into()), 3816 + span: location 3817 + }, 3818 + path: path.clone(), 3819 + src: src.clone(), 3820 + extra_labels: vec![], 3821 + }), 3822 + }, 3823 + 3824 + ParseErrorType::ExpectedPattern => 3825 + Diagnostic { 3826 + title, 3827 + text: "".into(), 3828 + hint: None, 3829 + level: Level::Error, 3830 + location: Some(Location { 3831 + label: Label { 3832 + text: Some("I was expecting a pattern after this".into()), 3833 + span: location 3834 + }, 3835 + path: path.clone(), 3836 + src: src.clone(), 3837 + extra_labels: vec![], 3838 + }), 3839 + }, 3840 + 3841 + ParseErrorType::ExpectedType => 3842 + Diagnostic { 3843 + title, 3844 + text: "See: https://tour.gleam.run/basics/assignments/".into(), 3845 + hint: None, 3846 + level: Level::Error, 3847 + location: Some(Location { 3848 + label: Label { 3849 + text: Some("I was expecting a type after this".into()), 3850 + span: location 3851 + }, 3852 + path: path.clone(), 3853 + src: src.clone(), 3854 + extra_labels: vec![], 3855 + }), 3856 + }, 3857 + 3858 + ParseErrorType::ExpectedUpName => 3859 + Diagnostic { 3860 + title, 3861 + text: "".into(), 3862 + hint: None, 3863 + level: Level::Error, 3864 + location: Some(Location { 3865 + label: Label { 3866 + text: Some("I was expecting a type name here".into()), 3867 + span: location 3868 + }, 3869 + path: path.clone(), 3870 + src: src.clone(), 3871 + extra_labels: vec![], 3872 + }), 3873 + }, 3874 + 3875 + ParseErrorType::ExpectedValue => 3876 + Diagnostic { 3877 + title, 3878 + text: "".into(), 3879 + hint: None, 3880 + level: Level::Error, 3881 + location: Some(Location { 3882 + label: Label { 3883 + text: Some("I was expecting a value after this".into()), 3884 + span: location 3885 + }, 3886 + path: path.clone(), 3887 + src: src.clone(), 3888 + extra_labels: vec![], 3889 + }), 3890 + }, 3891 + 3892 + ParseErrorType::ExpectedDefinition => 3893 + Diagnostic { 3894 + title, 3895 + text: "".into(), 3896 + hint: None, 3897 + level: Level::Error, 3898 + location: Some(Location { 3899 + label: Label { 3900 + text: Some("I was expecting a definition after this".into()), 3901 + span: location 3902 + }, 3903 + path: path.clone(), 3904 + src: src.clone(), 3905 + extra_labels: vec![], 3906 + }), 3907 + }, 3908 + 3909 + ParseErrorType::ExpectedDeprecationMessage => 3910 + Diagnostic { 3911 + title, 3912 + text: "".into(), 3913 + hint: None, 3914 + level: Level::Error, 3915 + location: Some(Location { 3916 + label: Label { 3917 + text: Some("A deprecation attribute must have a string message.".into()), 3918 + span: location 3919 + }, 3920 + path: path.clone(), 3921 + src: src.clone(), 3922 + extra_labels: vec![], 3923 + }), 3924 + }, 3925 + 3926 + ParseErrorType::ExpectedFunctionDefinition => 3927 + Diagnostic { 3928 + title, 3929 + text: "".into(), 3930 + hint: None, 3931 + level: Level::Error, 3932 + location: Some(Location { 3933 + label: Label { 3934 + text: Some("I was expecting a function definition after this".into()), 3935 + span: location 3936 + }, 3937 + path: path.clone(), 3938 + src: src.clone(), 3939 + extra_labels: vec![], 3940 + }), 3941 + }, 3942 + 3943 + ParseErrorType::ExpectedTargetName => 3944 + Diagnostic { 3945 + title, 3946 + text: "Try `erlang`, `javascript`.".into(), 3947 + hint: None, 3948 + level: Level::Error, 3949 + location: Some(Location { 3950 + label: Label { 3951 + text: Some("I was expecting a target name after this".into()), 3952 + span: location 3953 + }, 3954 + path: path.clone(), 3955 + src: src.clone(), 3956 + extra_labels: vec![], 3957 + }), 3958 + }, 3959 + 3960 + ParseErrorType::ExtraSeparator => 3961 + Diagnostic { 3962 + title, 3963 + text: "".into(), 3964 + hint: Some("Try removing it?".into()), 3965 + level: Level::Error, 3966 + location: Some(Location { 3967 + label: Label { 3968 + text: Some("This is an extra delimiter".into()), 3969 + span: location 3970 + }, 3971 + path: path.clone(), 3972 + src: src.clone(), 3973 + extra_labels: vec![], 3974 + }), 3975 + }, 3976 + 3977 + ParseErrorType::ExprLparStart => 3978 + Diagnostic { 3979 + title, 3980 + text: "".into(), 3981 + hint: Some("To group expressions in Gleam, use \"{\" and \"}\"; tuples are created with `#(` and `)`.".into()), 3982 + level: Level::Error, 3983 + location: Some(Location { 3984 + label: Label { 3985 + text: Some("This parenthesis cannot be understood here".into()), 3986 + span: location 3987 + }, 3988 + path: path.clone(), 3989 + src: src.clone(), 3990 + extra_labels: vec![], 3991 + }), 3992 + }, 3993 + 3994 + ParseErrorType::IncorrectName => 3995 + Diagnostic { 3996 + title, 3997 + text: "".into(), 3998 + hint: Some(wrap("Variable and module names start with a lowercase letter, \ 3999 + and can contain a-z, 0-9, or _.")), 4000 + level: Level::Error, 4001 + location: Some(Location { 4002 + label: Label { 4003 + text: Some("I'm expecting a lowercase name here".into()), 4004 + span: location 4005 + }, 4006 + path: path.clone(), 4007 + src: src.clone(), 4008 + extra_labels: vec![], 4009 + }), 4010 + }, 4011 + 4012 + ParseErrorType::IncorrectUpName => 4013 + Diagnostic { 4014 + title, 4015 + text: "".into(), 4016 + hint: Some(wrap("Type names start with a uppercase letter, and can \ 4017 + contain a-z, A-Z, or 0-9.")), 4018 + level: Level::Error, 4019 + location: Some(Location { 4020 + label: Label { 4021 + text: Some("I'm expecting a type name here".into()), 4022 + span: location 4023 + }, 4024 + path: path.clone(), 4025 + src: src.clone(), 4026 + extra_labels: vec![], 4027 + }), 4028 + }, 4029 + 4030 + ParseErrorType::InvalidBitArraySegment => 4031 + Diagnostic { 4032 + title, 4033 + text: "See: https://tour.gleam.run/data-types/bit-arrays/".into(), 4034 + hint: Some(format!("Valid BitArray segment options are:\n{}", wrap( 4035 + "bits, bytes, int, float, utf8, utf16, utf32, utf8_codepoint, \ 4036 + utf16_codepoint, utf32_codepoint, signed, unsigned, big, little, native, size, unit.", 4037 + ))), 4038 + level: Level::Error, 4039 + location: Some(Location { 4040 + label: Label { 4041 + text: Some("This is not a valid BitArray segment option".into()), 4042 + span: location 4043 + }, 4044 + path: path.clone(), 4045 + src: src.clone(), 4046 + extra_labels: vec![], 4047 + }), 4048 + }, 4049 + 4050 + ParseErrorType::InvalidBitArrayUnit => 4051 + Diagnostic { 4052 + title, 4053 + text: "See: https://tour.gleam.run/data-types/bit-arrays/".into(), 4054 + hint: Some("Unit must be an integer literal >= 1 and <= 256.".into()), 4055 + level: Level::Error, 4056 + location: Some(Location { 4057 + label: Label { 4058 + text: Some("This is not a valid BitArray unit value".into()), 4059 + span: location 4060 + }, 4061 + path: path.clone(), 4062 + src: src.clone(), 4063 + extra_labels: vec![], 4064 + }), 4065 + }, 4066 + 4067 + ParseErrorType::InvalidTailPattern => 4068 + Diagnostic { 4069 + title, 4070 + text: "".into(), 4071 + hint: None, 4072 + level: Level::Error, 4073 + location: Some(Location { 4074 + label: Label { 4075 + text: Some("This part of a list pattern can only be a name or a discard".into()), 4076 + span: location 4077 + }, 4078 + path: path.clone(), 4079 + src: src.clone(), 4080 + extra_labels: vec![], 4081 + }), 4082 + }, 4083 + 4084 + ParseErrorType::InvalidTupleAccess => 4085 + Diagnostic { 4086 + title, 4087 + text: "".into(), 4088 + hint: Some("Only non negative integer literals like 0, or 1_000 can be used.".into()), 4089 + level: Level::Error, 4090 + location: Some(Location { 4091 + label: Label { 4092 + text: Some("This integer is not valid for tuple access".into()), 4093 + span: location 4094 + }, 4095 + path: path.clone(), 4096 + src: src.clone(), 4097 + extra_labels: vec![], 4098 + }), 4099 + }, 4100 + 4101 + ParseErrorType::LexError { error: lex_err } => { 4102 + let (label_text, text_lines) = lex_err.to_parse_error_info(); 4103 + let text = text_lines.join("\n"); 4104 + Diagnostic { 4105 + title, 4106 + text, 4107 + hint: None, 4108 + level: Level::Error, 4109 + location: Some(Location { 4110 + label: Label { 4111 + text: Some(label_text.into()), 4112 + span: location 4113 + }, 4114 + path: path.clone(), 4115 + src: src.clone(), 4116 + extra_labels: vec![], 4117 + }), 4118 + } 3781 4119 }, 3782 - path: path.clone(), 3783 - src: src.clone(), 3784 - extra_labels: vec![], 3785 - }), 3786 - }] 4120 + 4121 + ParseErrorType::NestedBitArrayPattern => 4122 + Diagnostic { 4123 + title, 4124 + text: "".into(), 4125 + hint: None, 4126 + level: Level::Error, 4127 + location: Some(Location { 4128 + label: Label { 4129 + text: Some("BitArray patterns cannot be nested".into()), 4130 + span: location 4131 + }, 4132 + path: path.clone(), 4133 + src: src.clone(), 4134 + extra_labels: vec![], 4135 + }), 4136 + }, 4137 + 4138 + ParseErrorType::NotConstType => 4139 + Diagnostic { 4140 + title, 4141 + text: "See: https://tour.gleam.run/basics/constants/".into(), 4142 + hint: None, 4143 + level: Level::Error, 4144 + location: Some(Location { 4145 + label: Label { 4146 + text: Some("This type is not allowed in module constants".into()), 4147 + span: location 4148 + }, 4149 + path: path.clone(), 4150 + src: src.clone(), 4151 + extra_labels: vec![], 4152 + }), 4153 + }, 4154 + 4155 + ParseErrorType::NoLetBinding => 4156 + Diagnostic { 4157 + title, 4158 + text: "See: https://tour.gleam.run/basics/assignments/".into(), 4159 + hint: Some("Use let for binding.".into()), 4160 + level: Level::Error, 4161 + location: Some(Location { 4162 + label: Label { 4163 + text: Some("There must be a 'let' to bind variable to value".into()), 4164 + span: location 4165 + }, 4166 + path: path.clone(), 4167 + src: src.clone(), 4168 + extra_labels: vec![], 4169 + }), 4170 + }, 4171 + 4172 + ParseErrorType::NoValueAfterEqual => 4173 + Diagnostic { 4174 + title, 4175 + text: "".into(), 4176 + hint: None, 4177 + level: Level::Error, 4178 + location: Some(Location { 4179 + label: Label { 4180 + text: Some("I was expecting to see a value after this equals sign".into()), 4181 + span: location 4182 + }, 4183 + path: path.clone(), 4184 + src: src.clone(), 4185 + extra_labels: vec![], 4186 + }), 4187 + }, 4188 + 4189 + ParseErrorType::OpaqueTypeAlias => 4190 + Diagnostic { 4191 + title, 4192 + text: "See: https://tour.gleam.run/basics/type-aliases/".into(), 4193 + hint: None, 4194 + level: Level::Error, 4195 + location: Some(Location { 4196 + label: Label { 4197 + text: Some("Type Aliases cannot be opaque".into()), 4198 + span: location 4199 + }, 4200 + path: path.clone(), 4201 + src: src.clone(), 4202 + extra_labels: vec![], 4203 + }), 4204 + }, 4205 + 4206 + ParseErrorType::OpNakedRight => 4207 + Diagnostic { 4208 + title, 4209 + text: "".into(), 4210 + hint: Some("Remove it or put a value after it.".into()), 4211 + level: Level::Error, 4212 + location: Some(Location { 4213 + label: Label { 4214 + text: Some("This operator has no value on its right side".into()), 4215 + span: location 4216 + }, 4217 + path: path.clone(), 4218 + src: src.clone(), 4219 + extra_labels: vec![], 4220 + }), 4221 + }, 4222 + 4223 + ParseErrorType::TooManyArgHoles => 4224 + Diagnostic { 4225 + title, 4226 + text: "See: https://tour.gleam.run/functions/functions/".into(), 4227 + hint: Some("Function calls can have at most one argument hole.".into()), 4228 + level: Level::Error, 4229 + location: Some(Location { 4230 + label: Label { 4231 + text: Some("There is more than 1 argument hole in this function call".into()), 4232 + span: location 4233 + }, 4234 + path: path.clone(), 4235 + src: src.clone(), 4236 + extra_labels: vec![], 4237 + }), 4238 + }, 4239 + 4240 + ParseErrorType::UnexpectedEof => 4241 + Diagnostic { 4242 + title, 4243 + text: "".into(), 4244 + hint: None, 4245 + level: Level::Error, 4246 + location: Some(Location { 4247 + label: Label { 4248 + text: Some("The module ended unexpectedly".into()), 4249 + span: location 4250 + }, 4251 + path: path.clone(), 4252 + src: src.clone(), 4253 + extra_labels: vec![], 4254 + }), 4255 + }, 4256 + 4257 + ParseErrorType::ListSpreadWithoutElements => 4258 + Diagnostic { 4259 + title, 4260 + text: "See: https://tour.gleam.run/basics/lists/".into(), 4261 + hint: Some("Try prepending some elements [1, 2, ..list].".into()), 4262 + level: Level::Error, 4263 + location: Some(Location { 4264 + label: Label { 4265 + text: Some("This spread does nothing".into()), 4266 + span: location 4267 + }, 4268 + path: path.clone(), 4269 + src: src.clone(), 4270 + extra_labels: vec![], 4271 + }), 4272 + }, 4273 + 4274 + ParseErrorType::ListSpreadWithAnotherSpread { first_spread_location, second_spread_location } => 4275 + Diagnostic { 4276 + title, 4277 + text: vec![ 4278 + "Lists are immutable and singly-linked, so to join two or more lists", 4279 + "all the elements of the lists would need to be copied into a new list.", 4280 + "This would be slow, so there is no built-in syntax for it.", 4281 + ].join("\n"), 4282 + hint: None, 4283 + level: Level::Error, 4284 + location: Some(Location { 4285 + label: Label { 4286 + text: Some("I wasn't expecting a second spread here".into()), 4287 + span: *second_spread_location, 4288 + }, 4289 + path: path.clone(), 4290 + src: src.clone(), 4291 + extra_labels: vec![ 4292 + ExtraLabel { 4293 + src_info: None, 4294 + label: Label { 4295 + text: Some("You're using a spread here".into()), 4296 + span: *first_spread_location, 4297 + } 4298 + } 4299 + ], 4300 + }), 4301 + }, 4302 + 4303 + ParseErrorType::ListSpreadFollowedByElements => 4304 + Diagnostic { 4305 + title, 4306 + text: vec![ 4307 + "Lists are immutable and singly-linked, so to append items to them", 4308 + "all the elements of a list would need to be copied into a new list.", 4309 + "This would be slow, so there is no built-in syntax for it.", 4310 + "", 4311 + ].join("\n"), 4312 + hint: Some("Prepend items to the list and then reverse it once you are done.".into()), 4313 + level: Level::Error, 4314 + location: Some(Location { 4315 + label: Label { 4316 + text: Some("I wasn't expecting elements after this".into()), 4317 + span: location, 4318 + }, 4319 + path: path.clone(), 4320 + src: src.clone(), 4321 + extra_labels: vec![], 4322 + }), 4323 + }, 4324 + 4325 + 4326 + ParseErrorType::ListPatternSpreadFollowedByElements => 4327 + Diagnostic { 4328 + title, 4329 + text: vec![ 4330 + "Lists are immutable and singly-linked, so to match on the end", 4331 + "of a list would require the whole list to be traversed. This", 4332 + "would be slow, so there is no built-in syntax for it. Pattern", 4333 + "match on the start of the list instead.", 4334 + ].join("\n"), 4335 + hint: None, 4336 + level: Level::Error, 4337 + location: Some(Location { 4338 + label: Label { 4339 + text: Some("I wasn't expecting elements after this".into()), 4340 + span: location, 4341 + }, 4342 + path: path.clone(), 4343 + src: src.clone(), 4344 + extra_labels: vec![], 4345 + }), 4346 + }, 4347 + 4348 + ParseErrorType::UnexpectedReservedWord => 4349 + Diagnostic { 4350 + title, 4351 + text: "".into(), 4352 + hint: Some("I was expecting to see a name here.".into()), 4353 + level: Level::Error, 4354 + location: Some(Location { 4355 + label: Label { 4356 + text: Some("This is a reserved word".into()), 4357 + span: location, 4358 + }, 4359 + path: path.clone(), 4360 + src: src.clone(), 4361 + extra_labels: vec![], 4362 + }), 4363 + }, 4364 + 4365 + ParseErrorType::LowcaseBooleanPattern => 4366 + Diagnostic { 4367 + title, 4368 + text: "See: https://tour.gleam.run/basics/bools/".into(), 4369 + hint: Some("In Gleam boolean literals are `True` and `False`.".into()), 4370 + level: Level::Error, 4371 + location: Some(Location { 4372 + label: Label { 4373 + text: Some("Did you want a Bool instead of a variable?".into()), 4374 + span: location, 4375 + }, 4376 + path: path.clone(), 4377 + src: src.clone(), 4378 + extra_labels: vec![], 4379 + }), 4380 + }, 4381 + 4382 + ParseErrorType::UnexpectedLabel => 4383 + Diagnostic { 4384 + title, 4385 + text: "Please remove the argument label.".into(), 4386 + hint: None, 4387 + level: Level::Error, 4388 + location: Some(Location { 4389 + label: Label { 4390 + text: Some("Argument labels are not allowed for anonymous functions".into()), 4391 + span: location, 4392 + }, 4393 + path: path.clone(), 4394 + src: src.clone(), 4395 + extra_labels: vec![], 4396 + }), 4397 + }, 4398 + 4399 + ParseErrorType::UnexpectedToken { 4400 + token, 4401 + expected, 4402 + hint, 4403 + } => { 4404 + let found = match token { 4405 + Token::Int { .. } => "an Int".to_string(), 4406 + Token::Float { .. } => "a Float".to_string(), 4407 + Token::String { .. } => "a String".to_string(), 4408 + Token::CommentDoc { .. } => "a comment".to_string(), 4409 + Token::DiscardName { .. } => "a discard name".to_string(), 4410 + Token::Name { .. } | Token::UpName { .. } => "a name".to_string(), 4411 + _ if token.is_reserved_word() => format!("the keyword {token}"), 4412 + _ => token.to_string(), 4413 + }; 4414 + 4415 + let messages = std::iter::once(format!("Found {found}, expected one of: ")) 4416 + .chain(expected.iter().map(|s| format!("- {s}"))); 4417 + 4418 + let messages = match hint { 4419 + Some(hint_text) => messages 4420 + .chain(std::iter::once(format!("Hint: {hint_text}"))) 4421 + .collect_vec(), 4422 + _ => messages.collect(), 4423 + }; 4424 + 4425 + Diagnostic { 4426 + title, 4427 + text: messages.join("\n"), 4428 + hint: None, 4429 + level: Level::Error, 4430 + location: Some(Location { 4431 + label: Label { 4432 + text: Some("I was not expecting this".into()), 4433 + span: location, 4434 + }, 4435 + path: path.clone(), 4436 + src: src.clone(), 4437 + extra_labels: vec![], 4438 + }), 4439 + } 4440 + } 4441 + 4442 + ParseErrorType::ConcatPatternVariableLeftHandSide => 4443 + Diagnostic { 4444 + title, 4445 + text: vec![ 4446 + "We can't tell what size this prefix should be so we don't know", 4447 + "how to handle this pattern.", 4448 + "", 4449 + "If you want to match one character consider using `pop_grapheme`", 4450 + "from the stdlib's `gleam/string` module.", 4451 + ].join("\n"), 4452 + hint: None, 4453 + level: Level::Error, 4454 + location: Some(Location { 4455 + label: Label { 4456 + text: Some("This must be a string literal".into()), 4457 + span: location, 4458 + }, 4459 + path: path.clone(), 4460 + src: src.clone(), 4461 + extra_labels: vec![], 4462 + }), 4463 + }, 4464 + 4465 + ParseErrorType::UnexpectedFunction => 4466 + Diagnostic { 4467 + title, 4468 + text: "".into(), 4469 + hint: None, 4470 + level: Level::Error, 4471 + location: Some(Location { 4472 + label: Label { 4473 + text: Some("Functions can only be called within other functions".into()), 4474 + span: location, 4475 + }, 4476 + path: path.clone(), 4477 + src: src.clone(), 4478 + extra_labels: vec![], 4479 + }), 4480 + }, 4481 + 4482 + ParseErrorType::ListSpreadWithoutTail => 4483 + Diagnostic { 4484 + title, 4485 + text: "If a list expression has a spread then a tail must also be given.".into(), 4486 + hint: None, 4487 + level: Level::Error, 4488 + location: Some(Location { 4489 + label: Label { 4490 + text: Some("I was expecting a value after this spread".into()), 4491 + span: location, 4492 + }, 4493 + path: path.clone(), 4494 + src: src.clone(), 4495 + extra_labels: vec![], 4496 + }), 4497 + }, 4498 + 4499 + ParseErrorType::UnknownAttribute => 4500 + Diagnostic { 4501 + title, 4502 + text: "".into(), 4503 + hint: Some("Try `deprecated`, `external` or `target` instead.".into()), 4504 + level: Level::Error, 4505 + location: Some(Location { 4506 + label: Label { 4507 + text: Some("I don't recognise this attribute".into()), 4508 + span: location, 4509 + }, 4510 + path: path.clone(), 4511 + src: src.clone(), 4512 + extra_labels: vec![], 4513 + }), 4514 + }, 4515 + 4516 + ParseErrorType::DuplicateAttribute => 4517 + Diagnostic { 4518 + title, 4519 + text: "This attribute has already been given.".into(), 4520 + hint: None, 4521 + level: Level::Error, 4522 + location: Some(Location { 4523 + label: Label { 4524 + text: Some("Duplicate attribute".into()), 4525 + span: location, 4526 + }, 4527 + path: path.clone(), 4528 + src: src.clone(), 4529 + extra_labels: vec![], 4530 + }), 4531 + }, 4532 + 4533 + ParseErrorType::UnknownTarget => 4534 + Diagnostic { 4535 + title, 4536 + text: "Try `erlang`, `javascript`.".into(), 4537 + hint: None, 4538 + level: Level::Error, 4539 + location: Some(Location { 4540 + label: Label { 4541 + text: Some("I don't recognise this target".into()), 4542 + span: location, 4543 + }, 4544 + path: path.clone(), 4545 + src: src.clone(), 4546 + extra_labels: vec![], 4547 + }), 4548 + }, 4549 + 4550 + ParseErrorType::ExpectedFunctionBody => 4551 + Diagnostic { 4552 + title, 4553 + text: "".into(), 4554 + hint: None, 4555 + level: Level::Error, 4556 + location: Some(Location { 4557 + label: Label { 4558 + text: Some("This function does not have a body".into()), 4559 + span: location, 4560 + }, 4561 + path: path.clone(), 4562 + src: src.clone(), 4563 + extra_labels: vec![], 4564 + }), 4565 + }, 4566 + 4567 + ParseErrorType::RedundantInternalAttribute => 4568 + Diagnostic { 4569 + title, 4570 + text: "Only a public definition can be annotated as internal.".into(), 4571 + hint: Some("Remove the `@internal` annotation.".into()), 4572 + level: Level::Error, 4573 + location: Some(Location { 4574 + label: Label { 4575 + text: Some("Redundant internal attribute".into()), 4576 + span: location, 4577 + }, 4578 + path: path.clone(), 4579 + src: src.clone(), 4580 + extra_labels: vec![], 4581 + }), 4582 + }, 4583 + 4584 + ParseErrorType::InvalidModuleTypePattern => 4585 + Diagnostic { 4586 + title, 4587 + text: vec![ 4588 + "I'm expecting a pattern here", 4589 + "Hint: A pattern can be a constructor name, a literal value", 4590 + "or a variable to bind a value to, etc.", 4591 + "See: https://tour.gleam.run/flow-control/case-expressions/", 4592 + ].join("\n"), 4593 + hint: None, 4594 + level: Level::Error, 4595 + location: Some(Location { 4596 + label: Label { 4597 + text: Some("Invalid pattern".into()), 4598 + span: location, 4599 + }, 4600 + path: path.clone(), 4601 + src: src.clone(), 4602 + extra_labels: vec![], 4603 + }), 4604 + }, 4605 + 4606 + ParseErrorType::ExpectedRecordConstructor { 4607 + name, 4608 + public, 4609 + opaque, 4610 + field, 4611 + field_type, 4612 + } => { 4613 + let (accessor, opaque) = match *public { 4614 + true if *opaque => ("pub ", "opaque "), 4615 + true => ("pub ", ""), 4616 + false => ("", ""), 4617 + }; 4618 + 4619 + let mut annotation = EcoString::new(); 4620 + match field_type { 4621 + Some(t) => t.print(&mut annotation), 4622 + None => annotation.push_str("Type"), 4623 + }; 4624 + 4625 + Diagnostic { 4626 + title, 4627 + text: vec![ 4628 + "Each custom type variant must have a constructor:\n".into(), 4629 + format!("{accessor}{opaque}type {name} {{"), 4630 + format!(" {name}("), 4631 + format!(" {field}: {annotation},"), 4632 + " )".into(), 4633 + "}".into(), 4634 + ].join("\n"), 4635 + hint: None, 4636 + level: Level::Error, 4637 + location: Some(Location { 4638 + label: Label { 4639 + text: Some("I was not expecting this".into()), 4640 + span: location, 4641 + }, 4642 + path: path.clone(), 4643 + src: src.clone(), 4644 + extra_labels: vec![], 4645 + }), 4646 + } 4647 + } 4648 + 4649 + ParseErrorType::CallInClauseGuard => 4650 + Diagnostic { 4651 + title, 4652 + text: "Functions cannot be called in clause guards.".into(), 4653 + hint: None, 4654 + level: Level::Error, 4655 + location: Some(Location { 4656 + label: Label { 4657 + text: Some("Unsupported expression".into()), 4658 + span: location, 4659 + }, 4660 + path: path.clone(), 4661 + src: src.clone(), 4662 + extra_labels: vec![], 4663 + }), 4664 + }, 4665 + 4666 + ParseErrorType::IfExpression => 4667 + Diagnostic { 4668 + title, 4669 + text: vec![ 4670 + "If you want to write a conditional expression you can use a `case`:", 4671 + "", 4672 + " case condition {", 4673 + " True -> todo", 4674 + " False -> todo", 4675 + " }", 4676 + "", 4677 + "See: https://tour.gleam.run/flow-control/case-expressions/", 4678 + ].join("\n"), 4679 + hint: None, 4680 + level: Level::Error, 4681 + location: Some(Location { 4682 + label: Label { 4683 + text: Some("Gleam doesn't have if expressions".into()), 4684 + span: location, 4685 + }, 4686 + path: path.clone(), 4687 + src: src.clone(), 4688 + extra_labels: vec![], 4689 + }), 4690 + }, 4691 + 4692 + ParseErrorType::ConstantRecordConstructorNoArguments => 4693 + Diagnostic { 4694 + title, 4695 + text: "A record must be passed arguments when constructed.".into(), 4696 + hint: None, 4697 + level: Level::Error, 4698 + location: Some(Location { 4699 + label: Label { 4700 + text: Some("I was expecting arguments here".into()), 4701 + span: location, 4702 + }, 4703 + path: path.clone(), 4704 + src: src.clone(), 4705 + extra_labels: vec![], 4706 + }), 4707 + }, 4708 + 4709 + ParseErrorType::TypeConstructorNoArguments => 4710 + Diagnostic { 4711 + title, 4712 + text: "A type constructor must be passed arguments.".into(), 4713 + hint: None, 4714 + level: Level::Error, 4715 + location: Some(Location { 4716 + label: Label { 4717 + text: Some("I was expecting arguments here".into()), 4718 + span: location, 4719 + }, 4720 + path: path.clone(), 4721 + src: src.clone(), 4722 + extra_labels: vec![], 4723 + }), 4724 + }, 4725 + 4726 + ParseErrorType::TypeDefinitionNoArguments => 4727 + Diagnostic { 4728 + title, 4729 + text: "A generic type must have at least a generic parameter.".into(), 4730 + hint: Some("If a type is not generic you should omit the `()`.".into()), 4731 + level: Level::Error, 4732 + location: Some(Location { 4733 + label: Label { 4734 + text: Some("I was expecting generic parameters here".into()), 4735 + span: location, 4736 + }, 4737 + path: path.clone(), 4738 + src: src.clone(), 4739 + extra_labels: vec![], 4740 + }), 4741 + }, 4742 + 4743 + ParseErrorType::UnknownAttributeRecordVariant => 4744 + Diagnostic { 4745 + title, 4746 + text: "".into(), 4747 + hint: Some("Did you mean `@deprecated`?".into()), 4748 + level: Level::Error, 4749 + location: Some(Location { 4750 + label: Label { 4751 + text: Some("This attribute cannot be used on a variant.".into()), 4752 + span: location, 4753 + }, 4754 + path: path.clone(), 4755 + src: src.clone(), 4756 + extra_labels: vec![], 4757 + }), 4758 + }, 4759 + 4760 + ParseErrorType::IncorrectImportModuleSeparator { module, item } => 4761 + Diagnostic { 4762 + title, 4763 + text: vec![ 4764 + "Perhaps you meant one of:".into(), 4765 + "".into(), 4766 + format!(" import {module}/{item}"), 4767 + format!(" import {module}.{{item}}"), 4768 + ].join("\n"), 4769 + hint: None, 4770 + level: Level::Error, 4771 + location: Some(Location { 4772 + label: Label { 4773 + text: Some("I was expecting either `/` or `.{` here.".into()), 4774 + span: location, 4775 + }, 4776 + path: path.clone(), 4777 + src: src.clone(), 4778 + extra_labels: vec![], 4779 + }), 4780 + }, 4781 + }; 4782 + 4783 + vec![diagnostic] 3787 4784 } 3788 4785 3789 4786 Error::ImportCycle { modules } => {
+4 -319
compiler-core/src/parse/error.rs
··· 1 1 use crate::ast::{SrcSpan, TypeAst}; 2 - use crate::error::wrap; 3 2 use crate::parse::Token; 4 3 use ecow::EcoString; 5 4 ··· 36 35 pub location: SrcSpan, 37 36 } 38 37 39 - impl ParseError { 40 - pub fn details(&self) -> (&'static str, Vec<String>) { 41 - match &self.error { 42 - ParseErrorType::ExpectedEqual => ("I was expecting a '=' after this", vec![]), 43 - ParseErrorType::ExpectedExpr => ("I was expecting an expression after this", vec![]), 44 - ParseErrorType::ExpectedName => ("I was expecting a name here", vec![]), 45 - ParseErrorType::ExpectedPattern => ("I was expecting a pattern after this", vec![]), 46 - ParseErrorType::ExpectedType => ( 47 - "I was expecting a type after this", 48 - vec!["See: https://tour.gleam.run/basics/assignments/".into()], 49 - ), 50 - ParseErrorType::ExpectedUpName => ("I was expecting a type name here", vec![]), 51 - ParseErrorType::ExpectedValue => ("I was expecting a value after this", vec![]), 52 - ParseErrorType::ExpectedDefinition => { 53 - ("I was expecting a definition after this", vec![]) 54 - } 55 - ParseErrorType::ExpectedDeprecationMessage => ( 56 - "A deprecation attribute must have a string message.", 57 - vec![], 58 - ), 59 - ParseErrorType::ExpectedFunctionDefinition => { 60 - ("I was expecting a function definition after this", vec![]) 61 - } 62 - ParseErrorType::ExpectedTargetName => ( 63 - "I was expecting a target name after this", 64 - vec!["Try `erlang`, `javascript`.".into()], 65 - ), 66 - ParseErrorType::ExtraSeparator => ( 67 - "This is an extra delimiter", 68 - vec!["Hint: Try removing it?".into()], 69 - ), 70 - ParseErrorType::ExprLparStart => ( 71 - "This parenthesis cannot be understood here", 72 - vec![ 73 - "Hint: To group expressions in Gleam, use \"{\" and \"}\"; tuples are created with `#(` and `)`.".into(), 74 - ] 75 - ), 76 - ParseErrorType::IncorrectName => ( 77 - "I'm expecting a lowercase name here", 78 - vec![wrap( 79 - "Hint: Variable and module names start with a lowercase letter, \ 80 - and can contain a-z, 0-9, or _.", 81 - )], 82 - ), 83 - ParseErrorType::IncorrectUpName => ( 84 - "I'm expecting a type name here", 85 - vec![wrap( 86 - "Hint: Type names start with a uppercase letter, and can \ 87 - contain a-z, A-Z, or 0-9.", 88 - )], 89 - ), 90 - ParseErrorType::InvalidBitArraySegment => ( 91 - "This is not a valid BitArray segment option", 92 - vec![ 93 - "Hint: Valid BitArray segment options are:".into(), 94 - wrap( 95 - "bits, bytes, int, float, utf8, utf16, utf32, utf8_codepoint, \ 96 - utf16_codepoint, utf32_codepoint, signed, unsigned, big, little, native, size, unit.", 97 - ), 98 - "See: https://tour.gleam.run/data-types/bit-arrays/".into(), 99 - ], 100 - ), 101 - ParseErrorType::InvalidBitArrayUnit => ( 102 - "This is not a valid BitArray unit value", 103 - vec![ 104 - "Hint: unit must be an integer literal >= 1 and <= 256.".into(), 105 - "See: https://tour.gleam.run/data-types/bit-arrays/".into(), 106 - ], 107 - ), 108 - ParseErrorType::InvalidTailPattern => ( 109 - "This part of a list pattern can only be a name or a discard", 110 - vec![], 111 - ), 112 - ParseErrorType::InvalidTupleAccess => ( 113 - "This integer is not valid for tuple access", 114 - vec![ 115 - "Hint: Only non negative integer literals like 0, or 1_000 can be used." 116 - .to_string(), 117 - ], 118 - ), 119 - ParseErrorType::LexError { error: lex_err } => lex_err.to_parse_error_info(), 120 - ParseErrorType::NestedBitArrayPattern => ("BitArray patterns cannot be nested", vec![]), 121 - ParseErrorType::NotConstType => ( 122 - "This type is not allowed in module constants", 123 - vec!["See: https://tour.gleam.run/basics/constants/".into()], 124 - ), 125 - ParseErrorType::NoLetBinding => ( 126 - "There must be a 'let' to bind variable to value", 127 - vec![ 128 - "Hint: Use let for binding.".into(), 129 - "See: https://tour.gleam.run/basics/assignments/".into(), 130 - ], 131 - ), 132 - ParseErrorType::NoValueAfterEqual => ( 133 - "I was expecting to see a value after this equals sign", 134 - vec![], 135 - ), 136 - ParseErrorType::OpaqueTypeAlias => ( 137 - "Type Aliases cannot be opaque", 138 - vec!["See: https://tour.gleam.run/basics/type-aliases/".into()], 139 - ), 140 - ParseErrorType::OpNakedRight => ( 141 - "This operator has no value on its right side", 142 - vec!["Hint: Remove it or put a value after it.".into()], 143 - ), 144 - ParseErrorType::TooManyArgHoles => ( 145 - "There is more than 1 argument hole in this function call", 146 - vec![ 147 - "Hint: Function calls can have at most one argument hole.".into(), 148 - "See: https://tour.gleam.run/functions/functions/".into(), 149 - ], 150 - ), 151 - ParseErrorType::UnexpectedEof => ("The module ended unexpectedly", vec![]), 152 - ParseErrorType::ListSpreadWithoutElements => ( 153 - "This spread does nothing", 154 - vec![ 155 - "Hint: Try prepending some elements [1, 2, ..list].".into(), 156 - "See: https://tour.gleam.run/basics/lists/".into(), 157 - ], 158 - ), 159 - ParseErrorType::ListSpreadWithAnotherSpread => ( 160 - "I wasn't expecting a spread here", 161 - vec![ 162 - "Lists are immutable and singly-linked, so to join two or more lists".into(), 163 - "all the elements of the lists would need to be copied into a new list.".into(), 164 - "This would be slow, so there is no built-in syntax for it.".into(), 165 - ], 166 - ), 167 - ParseErrorType::ListSpreadFollowedByElements => ( 168 - "I wasn't expecting elements after this", 169 - vec![ 170 - "Lists are immutable and singly-linked, so to append items to them".into(), 171 - "all the elements of a list would need to be copied into a new list.".into(), 172 - "This would be slow, so there is no built-in syntax for it.".into(), 173 - "".into(), 174 - "Hint: prepend items to the list and then reverse it once you are done.".into(), 175 - ], 176 - ), 177 - ParseErrorType::ListPatternSpreadFollowedByElements => ( 178 - "I wasn't expecting elements after this", 179 - vec![ 180 - "Lists are immutable and singly-linked, so to match on the end".into(), 181 - "of a list would require the whole list to be traversed. This".into(), 182 - "would be slow, so there is no built-in syntax for it. Pattern".into(), 183 - "match on the start of the list instead.".into(), 184 - ], 185 - ), 186 - ParseErrorType::UnexpectedReservedWord => ( 187 - "This is a reserved word", 188 - vec!["Hint: I was expecting to see a name here.".into()], 189 - ), 190 - ParseErrorType::LowcaseBooleanPattern => ( 191 - "Did you want a Bool instead of a variable?", 192 - vec![ 193 - "Hint: In Gleam boolean literals are `True` and `False`.".into(), 194 - "See: https://tour.gleam.run/basics/bools/".into(), 195 - ], 196 - ), 197 - ParseErrorType::UnexpectedLabel => ( 198 - "Argument labels are not allowed for anonymous functions", 199 - vec!["Please remove the argument label.".into()], 200 - ), 201 - ParseErrorType::UnexpectedToken { 202 - token, 203 - expected, 204 - hint, 205 - } => { 206 - let found = match token { 207 - Token::Int { .. } => "an Int".to_string(), 208 - Token::Float { .. } => "a Float".to_string(), 209 - Token::String { .. } => "a String".to_string(), 210 - Token::CommentDoc { .. } => "a comment".to_string(), 211 - Token::DiscardName { .. } => "a discard name".to_string(), 212 - Token::Name { .. } | Token::UpName { .. } => "a name".to_string(), 213 - _ if token.is_reserved_word() => format!("the keyword {token}"), 214 - _ => token.to_string(), 215 - }; 216 - 217 - let messages = std::iter::once(format!("Found {found}, expected one of: ")) 218 - .chain(expected.iter().map(|s| format!("- {s}"))); 219 - 220 - let messages = match hint { 221 - Some(hint_text) => messages 222 - .chain(std::iter::once(format!("Hint: {hint_text}"))) 223 - .collect(), 224 - _ => messages.collect(), 225 - }; 226 - 227 - ("I was not expecting this", messages) 228 - } 229 - ParseErrorType::ConcatPatternVariableLeftHandSide => ( 230 - "This must be a string literal", 231 - vec![ 232 - "We can't tell what size this prefix should be so we don't know".into(), 233 - "how to handle this pattern.".into(), 234 - "".into(), 235 - "If you want to match one character consider using `pop_grapheme`".into(), 236 - "from the stdlib's `gleam/string` module.".into(), 237 - ], 238 - ), 239 - ParseErrorType::UnexpectedFunction => ( 240 - "Functions can only be called within other functions", 241 - vec![], 242 - ), 243 - ParseErrorType::ListSpreadWithoutTail => ( 244 - "I was expecting a value after this spread", 245 - vec!["If a list expression has a spread then a tail must also be given.".into()], 246 - ), 247 - ParseErrorType::UnknownAttribute => ( 248 - "I don't recognise this attribute", 249 - vec!["Try `deprecated`, `external` or `target` instead.".into()], 250 - ), 251 - ParseErrorType::DuplicateAttribute => ( 252 - "Duplicate attribute", 253 - vec!["This attribute has already been given.".into()], 254 - ), 255 - ParseErrorType::UnknownTarget => ( 256 - "I don't recognise this target", 257 - vec!["Try `erlang`, `javascript`.".into()], 258 - ), 259 - ParseErrorType::ExpectedFunctionBody => ("This function does not have a body", vec![]), 260 - ParseErrorType::RedundantInternalAttribute => ( 261 - "Redundant internal attribute", 262 - vec![ 263 - format!("Only a public definition can be annotated as internal."), 264 - "Hint: remove the `@internal` annotation.".into(), 265 - ], 266 - ), 267 - ParseErrorType::InvalidModuleTypePattern => ( 268 - "Invalid pattern", 269 - vec![ 270 - "I'm expecting a pattern here".into(), 271 - "Hint: A pattern can be a constructor name, a literal value".into(), 272 - "or a variable to bind a value to, etc.".into(), 273 - "See: https://tour.gleam.run/flow-control/case-expressions/".into(), 274 - ], 275 - ), 276 - ParseErrorType::ExpectedRecordConstructor { 277 - name, 278 - public, 279 - opaque, 280 - field, 281 - field_type, 282 - } => { 283 - let (accessor, opaque) = match *public { 284 - true if *opaque => ("pub ", "opaque "), 285 - true => ("pub ", ""), 286 - false => ("", ""), 287 - }; 288 - 289 - let mut annotation = EcoString::new(); 290 - match field_type { 291 - Some(t) => t.print(&mut annotation), 292 - None => annotation.push_str("Type"), 293 - }; 294 - 295 - ( 296 - "I was not expecting this", 297 - vec![ 298 - "Each custom type variant must have a constructor:\n".into(), 299 - format!("{accessor}{opaque}type {name} {{"), 300 - format!(" {name}("), 301 - format!(" {field}: {annotation},"), 302 - " )".into(), 303 - "}".into(), 304 - ], 305 - ) 306 - } 307 - ParseErrorType::CallInClauseGuard => ( 308 - "Unsupported expression", 309 - vec!["Functions cannot be called in clause guards.".into()], 310 - ), 311 - ParseErrorType::IfExpression => ( 312 - "Gleam doesn't have if expressions", 313 - vec![ 314 - "If you want to write a conditional expression you can use a `case`:".into(), 315 - "".into(), 316 - " case condition {".into(), 317 - " True -> todo".into(), 318 - " False -> todo".into(), 319 - " }".into(), 320 - "".into(), 321 - "See: https://tour.gleam.run/flow-control/case-expressions/".into(), 322 - ], 323 - ), 324 - ParseErrorType::ConstantRecordConstructorNoArguments => ( 325 - "I was expecting arguments here", 326 - vec!["A record must be passed arguments when constructed.".into()], 327 - ), 328 - ParseErrorType::TypeConstructorNoArguments => ( 329 - "I was expecting arguments here", 330 - vec!["A type constructor must be passed arguments.".into()], 331 - ), 332 - ParseErrorType::TypeDefinitionNoArguments => ( 333 - "I was expecting generic parameters here", 334 - vec![ 335 - "A generic type must have at least a generic parameter.".into(), 336 - "Hint: If a type is not generic you should omit the `()`.".into(), 337 - ], 338 - ), 339 - ParseErrorType::UnknownAttributeRecordVariant => ( 340 - "This attribute cannot be used on a variant.", 341 - vec!["Hint: Did you mean `@deprecated`?".into()], 342 - ), 343 - ParseErrorType::IncorrectImportModuleSeparator { module, item } => ( 344 - "I was expecting either `/` or `.{` here.", 345 - vec![ 346 - "Perhaps you meant one of:".into(), 347 - "".into(), 348 - format!(" import {module}/{item}"), 349 - format!(" import {module}.{{item}}"), 350 - ] 351 - ) 352 - } 353 - } 354 - } 355 - 356 38 #[derive(Debug, Clone, PartialEq, Eq)] 357 39 pub enum ParseErrorType { 358 40 ExpectedEqual, // expect "=" ··· 389 71 UnknownTarget, // an unknown target was used 390 72 ListSpreadWithoutElements, // Pointless spread: `[..xs]` 391 73 ListSpreadFollowedByElements, // trying to append something after the spread: `[..xs, x]` 392 - ListSpreadWithAnotherSpread, // trying to use multiple spreads: `[..xs, ..ys]` 74 + ListSpreadWithAnotherSpread { 75 + first_spread_location: SrcSpan, 76 + second_spread_location: SrcSpan, 77 + }, // trying to use multiple spreads: `[..xs, ..ys]` 393 78 LowcaseBooleanPattern, // most likely user meant True or False in patterns 394 79 UnexpectedLabel, // argument labels were provided, but are not supported in this context 395 80 UnexpectedEof,
+2 -2
compiler-core/src/parse/snapshots/gleam_core__parse__tests__error_message_on_variable_starting_with_underscore.snap
··· 15 15 3 │ let val = _func_starting_with_underscore(1) 16 16 │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ I'm expecting a lowercase name here 17 17 18 - Hint: Variable and module names start with a lowercase letter, and can 19 - contain a-z, 0-9, or _. 18 + Hint: Variable and module names start with a lowercase letter, and can contain 19 + a-z, 0-9, or _.
+2 -2
compiler-core/src/parse/snapshots/gleam_core__parse__tests__error_message_on_variable_starting_with_underscore2.snap
··· 17 17 4 │ 1 -> _with_underscore(1) 18 18 │ ^^^^^^^^^^^^^^^^ I'm expecting a lowercase name here 19 19 20 - Hint: Variable and module names start with a lowercase letter, and can 21 - contain a-z, 0-9, or _. 20 + Hint: Variable and module names start with a lowercase letter, and can contain 21 + a-z, 0-9, or _.
+1 -1
compiler-core/src/parse/snapshots/gleam_core__parse__tests__list_spread_as_first_item_followed_by_other_items.snap
··· 21 21 all the elements of a list would need to be copied into a new list. 22 22 This would be slow, so there is no built-in syntax for it. 23 23 24 - Hint: prepend items to the list and then reverse it once you are done. 24 + Hint: Prepend items to the list and then reverse it once you are done.
+1 -1
compiler-core/src/parse/snapshots/gleam_core__parse__tests__list_spread_followed_by_extra_items.snap
··· 21 21 all the elements of a list would need to be copied into a new list. 22 22 This would be slow, so there is no built-in syntax for it. 23 23 24 - Hint: prepend items to the list and then reverse it once you are done. 24 + Hint: Prepend items to the list and then reverse it once you are done.
+1 -1
compiler-core/src/parse/snapshots/gleam_core__parse__tests__no_let_binding_snapshot_1.snap
··· 12 12 1 │ wibble = 4 13 13 │ ^ There must be a 'let' to bind variable to value 14 14 15 - Hint: Use let for binding. 16 15 See: https://tour.gleam.run/basics/assignments/ 16 + Hint: Use let for binding.
+1 -1
compiler-core/src/parse/snapshots/gleam_core__parse__tests__no_let_binding_snapshot_2.snap
··· 12 12 1 │ wibble:Int = 4 13 13 │ ^ There must be a 'let' to bind variable to value 14 14 15 - Hint: Use let for binding. 16 15 See: https://tour.gleam.run/basics/assignments/ 16 + Hint: Use let for binding.
+1 -1
compiler-core/src/parse/snapshots/gleam_core__parse__tests__no_let_binding_snapshot_3.snap
··· 13 13 2 │ wobble = 42 14 14 │ ^ There must be a 'let' to bind variable to value 15 15 16 - Hint: Use let for binding. 17 16 See: https://tour.gleam.run/basics/assignments/ 17 + Hint: Use let for binding.
+1 -1
compiler-core/src/parse/snapshots/gleam_core__parse__tests__private_internal_const.snap
··· 16 16 │ ^^^^^^^^^ Redundant internal attribute 17 17 18 18 Only a public definition can be annotated as internal. 19 - Hint: remove the `@internal` annotation. 19 + Hint: Remove the `@internal` annotation.
+1 -1
compiler-core/src/parse/snapshots/gleam_core__parse__tests__private_internal_function.snap
··· 16 16 │ ^^^^^^^^^ Redundant internal attribute 17 17 18 18 Only a public definition can be annotated as internal. 19 - Hint: remove the `@internal` annotation. 19 + Hint: Remove the `@internal` annotation.
+1 -1
compiler-core/src/parse/snapshots/gleam_core__parse__tests__private_internal_type.snap
··· 18 18 │ ^^^^^^^^^ Redundant internal attribute 19 19 20 20 Only a public definition can be annotated as internal. 21 - Hint: remove the `@internal` annotation. 21 + Hint: Remove the `@internal` annotation.
+1 -1
compiler-core/src/parse/snapshots/gleam_core__parse__tests__private_internal_type_alias.snap
··· 16 16 │ ^^^^^^^^^ Redundant internal attribute 17 17 18 18 Only a public definition can be annotated as internal. 19 - Hint: remove the `@internal` annotation. 19 + Hint: Remove the `@internal` annotation.
+1 -1
compiler-core/src/parse/snapshots/gleam_core__parse__tests__unknown_attribute.snap
··· 13 13 1 │ @go_faster() 14 14 │ ^^^^^^^^^^ I don't recognise this attribute 15 15 16 - Try `deprecated`, `external` or `target` instead. 16 + Hint: Try `deprecated`, `external` or `target` instead.