Skip to content

Commit

Permalink
Merge of #5455
Browse files Browse the repository at this point in the history
  • Loading branch information
mergify[bot] authored Jan 18, 2024
2 parents b5a506d + ed372a0 commit 25db9b3
Show file tree
Hide file tree
Showing 13 changed files with 205 additions and 86 deletions.
5 changes: 5 additions & 0 deletions docs/docs/03-language-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -813,6 +813,11 @@ Here's a quick summary of how optionality works in Wing:
* The `x ?? y` notation will return the value in `x` if there is one, `y` otherwise.
* The keyword `nil` can be used in assignment scenarios to indicate that an optional doesn't have a
value. It cannot be used to test if an optional has a value or not.
* A type annotation in Wing can always be enclosed in parentheses: `num` and `(num)` are the same type.
This is useful when you want to denote an optional function type. For example `((str):num)?` means
an **optional function** receiving a `str` and returning a `num`, while the similarly written
`(str):num?` means a function receiving a `str` and returning an **optional `num`**.
#### 1.7.1 Declaration
Expand Down
7 changes: 0 additions & 7 deletions examples/tests/invalid/function_type.test.w
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
// function type with no return type
let my_func = (callback: (num)) => { };
// ^^^^^ Expected function return type
let my_func2 = (callback: ((num)): (str)) => { };
// ^^^^^ Expected function return type
// ^^^^^ Expected function return type

// interface methods with no return type
interface IFace {
my_method(x: num);
Expand Down
8 changes: 7 additions & 1 deletion examples/tests/invalid/optionals.test.w
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,10 @@ optionalFunction();
//^ Cannot call optional function (unless it's part of a reference)

let optionalFunctionWithNoRetType: ()? = () => {};
// ^^ Expected function return type
// ^^ Unexpected syntax

// Pass incorrect optional function type to a function
let functionWithOptionalFuncParam1: ((num):void)? = (x: str):void => {};
// ^^^^^^^^^^^^^^^^^^^ Expected type to be "(preflight (num): void)?", but got "preflight (x: str): void" instead
let functionWithOptionalFuncParam2: (():num)? = ():str => { return "s"; };
// ^^^^^^^^^^^^^^^^^^^^^^^^^ Expected type to be "(preflight (): num)?", but got "preflight (): str" instead
15 changes: 15 additions & 0 deletions examples/tests/valid/optionals.test.w
Original file line number Diff line number Diff line change
Expand Up @@ -204,3 +204,18 @@ if let s1 = str1 {
} elif let s2 = str2 {
assert(true);
}

// Optional function return type
let var fn: (): ((): num)? = () => { return () => { return 1337; }; };
if let f = fn() {
assert(f() == 1337);
} else {
assert(false);
}
fn = () => { return nil; };
if let f = fn() {
assert(false);
} else {
assert(true);
}

26 changes: 14 additions & 12 deletions libs/tree-sitter-wing/grammar.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,13 @@ module.exports = grammar({
$.json_container_type
)
),
field("accessor_type", $._accessor),
field("accessor_type", $.accessor),
// While the "property" identifier is optional in this grammar, upstream parsing will fail if it is not present
optional(field("property", $._member_identifier))
)
),

_accessor: ($) => choice(".", "?."),
accessor: ($) => choice(".", "?."),

inflight_specifier: ($) => "inflight",

Expand Down Expand Up @@ -442,15 +442,17 @@ module.exports = grammar({
)
),

_type: ($) =>
choice(
$.custom_type,
$.builtin_type,
$._builtin_container_type,
$.json_container_type,
$.function_type,
$.optional
),
_type: ($) => choice(
$.custom_type,
$.builtin_type,
$._builtin_container_type,
$.json_container_type,
$.function_type,
$.optional,
$._parenthesized_type,
),

_parenthesized_type: ($) => seq("(", $._type, ")"),

optional: ($) => seq($._type, "?"),

Expand All @@ -459,7 +461,7 @@ module.exports = grammar({
seq(
optional(field("inflight", $.inflight_specifier)),
field("parameter_types", $.parameter_type_list),
optional(seq(":", field("return_type", $._type)))
seq(":", field("return_type", $._type))
)
),

Expand Down
4 changes: 4 additions & 0 deletions libs/tree-sitter-wing/test/corpus/expressions.txt
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ obj.method();
(nested_identifier
object: (reference
(reference_identifier))
accessor_type: (accessor)
property: (member_identifier)))
args: (argument_list))))

Expand All @@ -132,6 +133,7 @@ x == obj.method();
(nested_identifier
object: (reference
(reference_identifier))
accessor_type: (accessor)
property: (member_identifier)))
args: (argument_list)))))

Expand Down Expand Up @@ -489,7 +491,9 @@ a?;
(nested_identifier
object: (reference
(reference_identifier))
accessor_type: (accessor)
property: (member_identifier)))
accessor_type: (accessor)
property: (member_identifier)))))))

================================================================================
Expand Down
4 changes: 4 additions & 0 deletions libs/tree-sitter-wing/test/corpus/references.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ test1.test2.test3;
(nested_identifier
object: (reference
(reference_identifier))
accessor_type: (accessor)
property: (member_identifier)))
accessor_type: (accessor)
property: (member_identifier)))))

================================================================================
Expand All @@ -34,6 +36,8 @@ test1.test2.test3();
(nested_identifier
object: (reference
(reference_identifier))
accessor_type: (accessor)
property: (member_identifier)))
accessor_type: (accessor)
property: (member_identifier)))
args: (argument_list))))
10 changes: 8 additions & 2 deletions libs/tree-sitter-wing/test/corpus/statements/statements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ z -= 1;

let var y = "hello";

let w: (((num))) = 1;

--------------------------------------------------------------------------------

(source
Expand Down Expand Up @@ -107,7 +109,11 @@ let var y = "hello";
(variable_definition_statement
reassignable: (reassignable)
name: (identifier)
value: (string)))
value: (string))
(variable_definition_statement
name: (identifier)
type: (builtin_type)
value: (number)))

================================================================================
If
Expand Down Expand Up @@ -472,7 +478,7 @@ if let x = y {} elif let x = z {} else {}
(source
(if_let_statement
name: (identifier)
value: (reference
value: (reference
(reference_identifier))
block: (block)
elif_let_block: (elif_let_block
Expand Down
24 changes: 18 additions & 6 deletions libs/wingc/src/lsp/completions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,25 @@ pub fn on_completion(params: lsp_types::CompletionParams) -> CompletionResponse
node_to_complete_kind,
"." | "?." | "member_identifier" | "type_identifier"
) {
// We need to double-check for an invalid nested reference (e.g. If there are multiple dots in a row)
if preceding_text.ends_with("..")
|| preceding_text.ends_with(".?.?")
|| preceding_text.ends_with("..")
|| preceding_text.ends_with(".?.")
|| preceding_text.ends_with("..?")
{
return vec![];
}

let parent = node_to_complete.parent().expect("A dot must have a parent");

// If the parent is an accessor node (the container of the "." or ".?" nodes) then move up one more level
let parent = if parent.kind() == "accessor" {
parent.parent().expect("An accessor must have a parent")
} else {
parent
};

if let Some(nearest_expr) = scope_visitor.nearest_expr {
let mut nearest_expr_type = types.get_expr_type(nearest_expr);
if let ExprKind::Call { .. } = &nearest_expr.kind {
Expand All @@ -207,11 +224,6 @@ pub fn on_completion(params: lsp_types::CompletionParams) -> CompletionResponse

// If we are inside an incomplete reference, there is possibly a type error or an anything which has no completions
if !nearest_expr_type.is_unresolved() {
// We need to double-check for an invalid nested reference (e.g. If there are multiple dots in a row)
if preceding_text.ends_with("..") || preceding_text.ends_with(".?.?") {
return vec![];
}

let access_context = if let ExprKind::Reference(Reference::Identifier(ident)) = &nearest_expr.kind {
match ident.name.as_str() {
"this" => ObjectAccessContext::This,
Expand Down Expand Up @@ -739,7 +751,7 @@ fn nearest_non_reference_ancestor<'a>(start_node: &'a tree_sitter::Node<'a>) ->
|| !nearest_non_reference.is_named()
|| matches!(
nearest_non_reference.kind(),
"identifier" | "reference" | "reference_identifier" | "expression_statement"
"identifier" | "reference" | "reference_identifier" | "expression_statement" | "accessor"
) {
let parent = nearest_non_reference.parent();
if let Some(parent) = parent {
Expand Down
Loading

0 comments on commit 25db9b3

Please sign in to comment.