Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,10 @@
segments when compiling a function with a JavaScript external.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))

- Fixed a bug where the compiler would produce a confusing error message when
writing a constructor with a lowercase name.
([Giacomo Cavalieri](https://github.com/giacomocavalieri))

- A `gleam@@compile.erl` is no longer left in the build output of
`gleam compile-package`.
([Louis Pilfold](https://github.com/lpil))
Expand Down
547 changes: 291 additions & 256 deletions compiler-core/src/parse.rs

Large diffs are not rendered by default.

59 changes: 38 additions & 21 deletions compiler-core/src/parse/error.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: 2020 The Gleam contributors

use crate::ast::{SrcSpan, TypeAst};
use crate::ast::{RecordConstructorArg, SrcSpan, TypeAst};
use crate::diagnostic::{ExtraLabel, Label};
use crate::error::{wrap, wrap_format};
use crate::parse::Token;
Expand Down Expand Up @@ -145,12 +145,23 @@ pub enum ParseErrorType {
RedundantInternalAttribute, // for a private definition marked as internal
InvalidModuleTypePattern, // for patterns that have a dot like: `name.thing`
ListPatternSpreadFollowedByElements, // When there is a pattern after a spread [..rest, pattern]

/// This happens when someone forgets to write the type constructor around
/// its fields in a custom type definition. For example:
///
/// ```gleam
/// pub type Wibble {
/// String,
/// field: Int,
/// field_2: a,
/// }
/// ```
ExpectedRecordConstructor {
name: EcoString,
type_name: EcoString,
public: bool,
opaque: bool,
field: EcoString,
field_type: Option<Box<TypeAst>>,
/// Those are the fields that have been written.
fields: Vec<RecordConstructorArg<()>>,
},
CallInClauseGuard, // case x { _ if f() -> 1 }
IfExpression,
Expand Down Expand Up @@ -684,34 +695,40 @@ See: https://tour.gleam.run/flow-control/case-expressions/"
},

ParseErrorType::ExpectedRecordConstructor {
name,
type_name,
public,
opaque,
field,
field_type,
fields,
} => {
let (accessor, opaque) = match *public {
true if *opaque => ("pub ", "opaque "),
true => ("pub ", ""),
false => ("", ""),
};

let mut annotation = EcoString::new();
match field_type {
Some(t) => t.print(&mut annotation),
None => annotation.push_str("Type"),
};
let fields = fields
.iter()
.map(|field| {
let mut type_ = EcoString::new();
field.ast.print(&mut type_);

match field.label.as_ref() {
Some((_, label)) => format!(" {label}: {type_},"),
None => format!(" {type_},"),
}
})
.join("\n");

ParseErrorDetails {
text: [
"Each custom type variant must have a constructor:\n".into(),
format!("{accessor}{opaque}type {name} {{"),
format!(" {name}("),
format!(" {field}: {annotation},"),
" )".into(),
"}".into(),
]
.join("\n"),
text: format!(
"Each custom type variant must have a constructor:

{accessor}{opaque}type {type_name} {{
{type_name}(
{fields}
)
}}"
),
hint: None,
label_text: "I was not expecting this".into(),
extra_labels: vec![],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
source: compiler-core/src/parse/tests.rs
expression: "\npub type Wibble {\n wibble(Int, String)\n}\n"
---
----- SOURCE CODE

pub type Wibble {
wibble(Int, String)
}


----- ERROR
error: Syntax error
┌─ /src/parse/error.gleam:3:3
3 │ wibble(Int, String)
│ ^^^^^^ I'm expecting a type name here

Hint: Type names start with a uppercase letter, and can contain a-z, A-Z,
or 0-9.
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
source: compiler-core/src/parse/tests.rs
expression: "\npub type Wibble(a) {\n field: Int,\n other: a\n}\n"
---
----- SOURCE CODE

pub type Wibble(a) {
field: Int,
other: a
}


----- ERROR
error: Syntax error
┌─ /src/parse/error.gleam:3:3
3 │ ╭ field: Int,
4 │ │ other: a
│ ╰──────────^ I was not expecting this

Each custom type variant must have a constructor:

pub type Wibble {
Wibble(
field: Int,
other: a,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ error: Syntax error
┌─ /src/parse/error.gleam:3:5
3 │ name: String,
│ ^^^^ I was not expecting this
│ ^^^^^^^^^^^^ I was not expecting this

Each custom type variant must have a constructor:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,7 @@ error: Syntax error
┌─ /src/parse/error.gleam:3:5
3 │ name: "Test User",
│ ^^^^ I was not expecting this
│ ^^^^ I'm expecting a type name here

Each custom type variant must have a constructor:

type User {
User(
name: Type,
)
}
Hint: Type names start with a uppercase letter, and can contain a-z, A-Z,
or 0-9.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@ Each custom type variant must have a constructor:

pub opaque type User {
User(
name: Type,
name,
)
}
23 changes: 23 additions & 0 deletions compiler-core/src/parse/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2510,3 +2510,26 @@ fn error_message_when_using_discard_pattern_for_as_pattern() {
}"
);
}

#[test]
fn missing_constructor_name_with_multiple_fields() {
assert_module_error!(
r#"
pub type Wibble(a) {
field: Int,
other: a
}
"#
);
}

#[test]
fn lowercase_constructor_name_in_custom_type() {
assert_module_error!(
r#"
pub type Wibble {
wibble(Int, String)
}
"#
);
}
Loading