From 36e6b27f636e1010734911092304993427eccfe0 Mon Sep 17 00:00:00 2001 From: Tsuf Cohen <39455181+tsuf239@users.noreply.github.com> Date: Tue, 6 Aug 2024 17:57:25 +0300 Subject: [PATCH] fix: cannot use `datetime` or `regex` as a type annotation (#7001) ## Checklist fixes #6376 Please pay attention that `std.Regex` and `std.Datetime` cannot be used by types now (similarly how `std.Json` can't be used as a type, if it's a bug let me know and I'll open an issue @Chriscbr ) - [ ] Title matches [Winglang's style guide](https://www.winglang.io/contributing/start-here/pull_requests#how-are-pull-request-titles-formatted) - [ ] Description explains motivation and solution - [ ] Tests added (always) - [ ] Docs updated (only required for features) - [ ] Added `pr/e2e-full` label if this feature requires end-to-end testing *By submitting this pull request, I confirm that my contribution is made under the terms of the [Wing Cloud Contribution License](https://github.com/winglang/wing/blob/main/CONTRIBUTION_LICENSE.md)*. --- .../tests/valid/std_type_annotation.test.w | 45 +++ libs/tree-sitter-wing/grammar.js | 33 ++- libs/tree-sitter-wing/src/grammar.json | 8 + libs/wingc/src/ast.rs | 4 + libs/wingc/src/docs.rs | 4 + libs/wingc/src/dtsify/extern_dtsify.rs | 26 +- libs/wingc/src/dtsify/mod.rs | 2 + libs/wingc/src/fold.rs | 2 + libs/wingc/src/lib.rs | 2 + libs/wingc/src/lsp/completions.rs | 2 + .../incomplete_inflight_namespace.snap | 2 +- .../completions/namespace_middle_dot.snap | 2 +- .../partial_type_reference_annotation.snap | 2 +- .../variable_type_annotation_namespace.snap | 2 +- libs/wingc/src/lsp/symbol_locator.rs | 2 + libs/wingc/src/parser.rs | 10 + libs/wingc/src/type_check.rs | 50 +++- .../wingc/src/type_check/inference_visitor.rs | 2 + libs/wingc/src/type_check/jsii_importer.rs | 7 +- libs/wingc/src/visit.rs | 2 + libs/wingc/src/visit_types.rs | 2 + ...d_type_annotation.test.w_compile_tf-aws.md | 260 ++++++++++++++++++ .../std_type_annotation.test.w_test_sim.md | 13 + 23 files changed, 464 insertions(+), 20 deletions(-) create mode 100644 examples/tests/valid/std_type_annotation.test.w create mode 100644 tools/hangar/__snapshots__/test_corpus/valid/std_type_annotation.test.w_compile_tf-aws.md create mode 100644 tools/hangar/__snapshots__/test_corpus/valid/std_type_annotation.test.w_test_sim.md diff --git a/examples/tests/valid/std_type_annotation.test.w b/examples/tests/valid/std_type_annotation.test.w new file mode 100644 index 00000000000..c80b12d1604 --- /dev/null +++ b/examples/tests/valid/std_type_annotation.test.w @@ -0,0 +1,45 @@ +bring expect; +let then: datetime = datetime.utcNow(); +let now: datetime = then; + + +let isOlderOrEqual = inflight (x: datetime, y: datetime) => { + return y.timestampMs >= x.timestampMs; +}; + +test "datetime is a valid type" { + let later: datetime = datetime.fromComponents({ + day: now.dayOfMonth, + hour: now.hours, + year: now.year + 1, + min: now.min, + month: now.month, + ms: now.ms, + sec: now.sec, + tz: now.timezone + }); + + expect.ok(isOlderOrEqual(then, now)); + expect.ok(isOlderOrEqual(now, later)); +} + + +let decimal: regex = regex.compile("^[0-9]*$"); + +let testString = inflight (re: regex, s: str) => { + return re.test(s); +}; + +test "regex is valid type" { + let binary: regex = regex.compile("^[0-1]*$"); + expect.equal(testString(decimal, "24"), true); + expect.equal(testString(decimal, "340523"), true); + expect.equal(testString(decimal, "a23"), false); + + expect.equal(testString(binary, "01101"), true); + expect.equal(testString(binary, "0"), true); + expect.equal(testString(binary, "120010"), false); +} + + + diff --git a/libs/tree-sitter-wing/grammar.js b/libs/tree-sitter-wing/grammar.js index 3d17e25194a..eb4c91ea436 100644 --- a/libs/tree-sitter-wing/grammar.js +++ b/libs/tree-sitter-wing/grammar.js @@ -235,12 +235,7 @@ module.exports = grammar({ field_modifiers: ($) => repeat1( - choice( - $.access_modifier, - $.static, - $.phase_specifier, - $.reassignable - ) + choice($.access_modifier, $.static, $.phase_specifier, $.reassignable) ), class_field: ($) => @@ -338,7 +333,12 @@ module.exports = grammar({ ), else_if_block: ($) => - seq("else", "if", field("condition", $.expression), field("block", $.block)), + seq( + "else", + "if", + field("condition", $.expression), + field("block", $.block) + ), try_catch_statement: ($) => seq( @@ -531,7 +531,16 @@ module.exports = grammar({ parameter_type_list: ($) => seq("(", commaSep($._type), ")"), builtin_type: ($) => - choice("num", "bool", "any", "str", "void", "duration"), + choice( + "num", + "bool", + "any", + "str", + "void", + "duration", + "datetime", + "regex" + ), initializer: ($) => seq( @@ -695,7 +704,8 @@ module.exports = grammar({ ), map_literal_member: ($) => seq($.expression, "=>", $.expression), - struct_literal_member: ($) => choice($.identifier, seq($.identifier, ":", $.expression)), + struct_literal_member: ($) => + choice($.identifier, seq($.identifier, ":", $.expression)), structured_access_expression: ($) => prec.right( PREC.STRUCTURED_ACCESS, @@ -714,7 +724,10 @@ module.exports = grammar({ json_map_literal: ($) => braced(commaSep(field("member", $.json_literal_member))), json_literal_member: ($) => - choice($.identifier, seq(choice($.identifier, $.string), ":", $.expression)), + choice( + $.identifier, + seq(choice($.identifier, $.string), ":", $.expression) + ), json_container_type: ($) => $._json_types, _json_types: ($) => choice("Json", "MutJson"), diff --git a/libs/tree-sitter-wing/src/grammar.json b/libs/tree-sitter-wing/src/grammar.json index 0cecda07eda..a531bc822fe 100644 --- a/libs/tree-sitter-wing/src/grammar.json +++ b/libs/tree-sitter-wing/src/grammar.json @@ -2921,6 +2921,14 @@ { "type": "STRING", "value": "duration" + }, + { + "type": "STRING", + "value": "datetime" + }, + { + "type": "STRING", + "value": "regex" } ] }, diff --git a/libs/wingc/src/ast.rs b/libs/wingc/src/ast.rs index 63b068b7d47..fbf876ac81e 100644 --- a/libs/wingc/src/ast.rs +++ b/libs/wingc/src/ast.rs @@ -129,6 +129,8 @@ pub enum TypeAnnotationKind { String, Bool, Duration, + Datetime, + Regex, Void, Json, MutJson, @@ -203,6 +205,8 @@ impl Display for TypeAnnotationKind { TypeAnnotationKind::String => write!(f, "str"), TypeAnnotationKind::Bool => write!(f, "bool"), TypeAnnotationKind::Duration => write!(f, "duration"), + TypeAnnotationKind::Datetime => write!(f, "datetime"), + TypeAnnotationKind::Regex => write!(f, "regex"), TypeAnnotationKind::Void => write!(f, "void"), TypeAnnotationKind::Json => write!(f, "Json"), TypeAnnotationKind::MutJson => write!(f, "MutJson"), diff --git a/libs/wingc/src/docs.rs b/libs/wingc/src/docs.rs index 1f936ee9b38..434fdd7b82e 100644 --- a/libs/wingc/src/docs.rs +++ b/libs/wingc/src/docs.rs @@ -155,6 +155,8 @@ impl Documented for TypeRef { | Type::Number | Type::String | Type::Duration + | Type::Datetime + | Type::Regex | Type::Boolean | Type::Void | Type::Json(_) @@ -190,6 +192,8 @@ impl Documented for TypeRef { | Type::Number | Type::String | Type::Duration + | Type::Datetime + | Type::Regex | Type::Boolean | Type::Void | Type::Json(_) diff --git a/libs/wingc/src/dtsify/extern_dtsify.rs b/libs/wingc/src/dtsify/extern_dtsify.rs index 23da13c1278..71c28779759 100644 --- a/libs/wingc/src/dtsify/extern_dtsify.rs +++ b/libs/wingc/src/dtsify/extern_dtsify.rs @@ -10,10 +10,12 @@ use crate::{ files::{remove_file, update_file, FilesError}, jsify::codemaker::CodeMaker, type_check::*, - WINGSDK_ASSEMBLY_NAME, WINGSDK_DURATION, + WINGSDK_ASSEMBLY_NAME, WINGSDK_DATETIME, WINGSDK_DURATION, WINGSDK_REGEX, }; const DURATION_FQN: &str = formatcp!("{WINGSDK_ASSEMBLY_NAME}.{WINGSDK_DURATION}"); +const DATETIME_FQN: &str = formatcp!("{WINGSDK_ASSEMBLY_NAME}.{WINGSDK_DATETIME}"); +const REGEX_FQN: &str = formatcp!("{WINGSDK_ASSEMBLY_NAME}.{WINGSDK_REGEX}"); /// Generates a self-contained .d.ts file for a given extern file. pub struct ExternDTSifier<'a> { @@ -134,6 +136,28 @@ impl<'a> ExternDTSifier<'a> { .unwrap(); self.dtsify_type(duration_type, false) } + Type::Datetime => { + let datetime_type = self + .types + .libraries + .lookup_nested_str(DATETIME_FQN, None) + .unwrap() + .0 + .as_type() + .unwrap(); + self.dtsify_type(datetime_type, false) + } + Type::Regex => { + let regex_type = self + .types + .libraries + .lookup_nested_str(REGEX_FQN, None) + .unwrap() + .0 + .as_type() + .unwrap(); + self.dtsify_type(regex_type, false) + } Type::Optional(t) => format!("({}) | undefined", self.dtsify_type(*t, is_inflight)), Type::Array(t) => format!("(readonly ({})[])", self.dtsify_type(*t, is_inflight)), Type::MutArray(t) => format!("({})[]", self.dtsify_type(*t, is_inflight)), diff --git a/libs/wingc/src/dtsify/mod.rs b/libs/wingc/src/dtsify/mod.rs index 1f8a2d5e5e9..34c8d8319e5 100644 --- a/libs/wingc/src/dtsify/mod.rs +++ b/libs/wingc/src/dtsify/mod.rs @@ -376,6 +376,8 @@ impl<'a> DTSifier<'a> { TypeAnnotationKind::Json => format!("Readonly<{TYPE_INTERNAL_NAMESPACE}.Json>"), TypeAnnotationKind::MutJson => format!("{TYPE_INTERNAL_NAMESPACE}.Json"), TypeAnnotationKind::Duration => format!("{TYPE_STD}.Duration"), + TypeAnnotationKind::Datetime => format!("{TYPE_STD}.Datetime"), + TypeAnnotationKind::Regex => format!("{TYPE_STD}.Regex"), TypeAnnotationKind::Optional(t) => { format!("({}) | undefined", self.dtsify_type_annotation(&t, ignore_phase)) } diff --git a/libs/wingc/src/fold.rs b/libs/wingc/src/fold.rs index d13c8d514e4..0a35536f173 100644 --- a/libs/wingc/src/fold.rs +++ b/libs/wingc/src/fold.rs @@ -534,6 +534,8 @@ where TypeAnnotationKind::String => TypeAnnotationKind::String, TypeAnnotationKind::Bool => TypeAnnotationKind::Bool, TypeAnnotationKind::Duration => TypeAnnotationKind::Duration, + TypeAnnotationKind::Datetime => TypeAnnotationKind::Datetime, + TypeAnnotationKind::Regex => TypeAnnotationKind::Regex, TypeAnnotationKind::Void => TypeAnnotationKind::Void, TypeAnnotationKind::Json => TypeAnnotationKind::Json, TypeAnnotationKind::MutJson => TypeAnnotationKind::MutJson, diff --git a/libs/wingc/src/lib.rs b/libs/wingc/src/lib.rs index 518464928ca..5267c4c9863 100644 --- a/libs/wingc/src/lib.rs +++ b/libs/wingc/src/lib.rs @@ -103,6 +103,8 @@ const WINGSDK_BRINGABLE_MODULES: [&'static str; 10] = [ const WINGSDK_GENERIC: &'static str = "std.T1"; const WINGSDK_DURATION: &'static str = "std.Duration"; +const WINGSDK_DATETIME: &'static str = "std.Datetime"; +const WINGSDK_REGEX: &'static str = "std.Regex"; const WINGSDK_MAP: &'static str = "std.Map"; const WINGSDK_MUT_MAP: &'static str = "std.MutMap"; const WINGSDK_ARRAY: &'static str = "std.Array"; diff --git a/libs/wingc/src/lsp/completions.rs b/libs/wingc/src/lsp/completions.rs index 9923890adcc..26ea89918fc 100644 --- a/libs/wingc/src/lsp/completions.rs +++ b/libs/wingc/src/lsp/completions.rs @@ -1124,6 +1124,8 @@ fn format_symbol_kind_as_completion(name: &str, symbol_kind: &SymbolKind) -> Opt | Type::String | Type::Stringable | Type::Duration + | Type::Datetime + | Type::Regex | Type::Boolean | Type::Void | Type::Json(_) diff --git a/libs/wingc/src/lsp/snapshots/completions/incomplete_inflight_namespace.snap b/libs/wingc/src/lsp/snapshots/completions/incomplete_inflight_namespace.snap index 95bd5c60057..a8b7305f4ea 100644 --- a/libs/wingc/src/lsp/snapshots/completions/incomplete_inflight_namespace.snap +++ b/libs/wingc/src/lsp/snapshots/completions/incomplete_inflight_namespace.snap @@ -275,7 +275,7 @@ source: libs/wingc/src/lsp/completions.rs kind: 22 documentation: kind: markdown - value: "```wing\nstruct ObjectMetadata {\n contentType?: str;\n lastModified: Datetime;\n size: num;\n}\n```\n---\nMetadata of a bucket object." + value: "```wing\nstruct ObjectMetadata {\n contentType?: str;\n lastModified: datetime;\n size: num;\n}\n```\n---\nMetadata of a bucket object." sortText: hh|ObjectMetadata - label: OnDeployProps kind: 22 diff --git a/libs/wingc/src/lsp/snapshots/completions/namespace_middle_dot.snap b/libs/wingc/src/lsp/snapshots/completions/namespace_middle_dot.snap index 95bd5c60057..a8b7305f4ea 100644 --- a/libs/wingc/src/lsp/snapshots/completions/namespace_middle_dot.snap +++ b/libs/wingc/src/lsp/snapshots/completions/namespace_middle_dot.snap @@ -275,7 +275,7 @@ source: libs/wingc/src/lsp/completions.rs kind: 22 documentation: kind: markdown - value: "```wing\nstruct ObjectMetadata {\n contentType?: str;\n lastModified: Datetime;\n size: num;\n}\n```\n---\nMetadata of a bucket object." + value: "```wing\nstruct ObjectMetadata {\n contentType?: str;\n lastModified: datetime;\n size: num;\n}\n```\n---\nMetadata of a bucket object." sortText: hh|ObjectMetadata - label: OnDeployProps kind: 22 diff --git a/libs/wingc/src/lsp/snapshots/completions/partial_type_reference_annotation.snap b/libs/wingc/src/lsp/snapshots/completions/partial_type_reference_annotation.snap index 95bd5c60057..a8b7305f4ea 100644 --- a/libs/wingc/src/lsp/snapshots/completions/partial_type_reference_annotation.snap +++ b/libs/wingc/src/lsp/snapshots/completions/partial_type_reference_annotation.snap @@ -275,7 +275,7 @@ source: libs/wingc/src/lsp/completions.rs kind: 22 documentation: kind: markdown - value: "```wing\nstruct ObjectMetadata {\n contentType?: str;\n lastModified: Datetime;\n size: num;\n}\n```\n---\nMetadata of a bucket object." + value: "```wing\nstruct ObjectMetadata {\n contentType?: str;\n lastModified: datetime;\n size: num;\n}\n```\n---\nMetadata of a bucket object." sortText: hh|ObjectMetadata - label: OnDeployProps kind: 22 diff --git a/libs/wingc/src/lsp/snapshots/completions/variable_type_annotation_namespace.snap b/libs/wingc/src/lsp/snapshots/completions/variable_type_annotation_namespace.snap index 95bd5c60057..a8b7305f4ea 100644 --- a/libs/wingc/src/lsp/snapshots/completions/variable_type_annotation_namespace.snap +++ b/libs/wingc/src/lsp/snapshots/completions/variable_type_annotation_namespace.snap @@ -275,7 +275,7 @@ source: libs/wingc/src/lsp/completions.rs kind: 22 documentation: kind: markdown - value: "```wing\nstruct ObjectMetadata {\n contentType?: str;\n lastModified: Datetime;\n size: num;\n}\n```\n---\nMetadata of a bucket object." + value: "```wing\nstruct ObjectMetadata {\n contentType?: str;\n lastModified: datetime;\n size: num;\n}\n```\n---\nMetadata of a bucket object." sortText: hh|ObjectMetadata - label: OnDeployProps kind: 22 diff --git a/libs/wingc/src/lsp/symbol_locator.rs b/libs/wingc/src/lsp/symbol_locator.rs index 7a63d94856c..083aac1fbdc 100644 --- a/libs/wingc/src/lsp/symbol_locator.rs +++ b/libs/wingc/src/lsp/symbol_locator.rs @@ -140,6 +140,8 @@ impl<'a> SymbolLocator<'a> { | Type::Number | Type::String | Type::Duration + | Type::Datetime + | Type::Regex | Type::Boolean => { if let Some((std_type, ..)) = self.types.get_std_class(&type_) { if let Some(t) = std_type.as_type_ref() { diff --git a/libs/wingc/src/parser.rs b/libs/wingc/src/parser.rs index eb5a1f102eb..aaad5ff4807 100644 --- a/libs/wingc/src/parser.rs +++ b/libs/wingc/src/parser.rs @@ -119,6 +119,8 @@ static RESERVED_WORDS: phf::Set<&'static str> = phf_set! { "num", "str", "duration", + "datetime", + "regex", "bool", "Json", "MutJson", @@ -1981,6 +1983,14 @@ impl<'s> Parser<'s> { kind: TypeAnnotationKind::Duration, span, }), + "datetime" => Ok(TypeAnnotation { + kind: TypeAnnotationKind::Datetime, + span, + }), + "regex" => Ok(TypeAnnotation { + kind: TypeAnnotationKind::Regex, + span, + }), "void" => Ok(TypeAnnotation { kind: TypeAnnotationKind::Void, span, diff --git a/libs/wingc/src/type_check.rs b/libs/wingc/src/type_check.rs index cbde94c77d2..9427cc7b870 100644 --- a/libs/wingc/src/type_check.rs +++ b/libs/wingc/src/type_check.rs @@ -28,10 +28,10 @@ use crate::visit_stmt_before_super::{CheckSuperCtorLocationVisitor, CheckValidBe use crate::visit_types::{VisitType, VisitTypeMut}; use crate::{ debug, CONSTRUCT_BASE_CLASS, CONSTRUCT_BASE_INTERFACE, CONSTRUCT_NODE_PROPERTY, DEFAULT_PACKAGE_NAME, - UTIL_CLASS_NAME, WINGSDK_ARRAY, WINGSDK_ASSEMBLY_NAME, WINGSDK_BRINGABLE_MODULES, WINGSDK_DURATION, WINGSDK_GENERIC, - WINGSDK_IRESOURCE, WINGSDK_JSON, WINGSDK_MAP, WINGSDK_MUT_ARRAY, WINGSDK_MUT_JSON, WINGSDK_MUT_MAP, WINGSDK_MUT_SET, - WINGSDK_NODE, WINGSDK_RESOURCE, WINGSDK_SET, WINGSDK_SIM_IRESOURCE_FQN, WINGSDK_STD_MODULE, WINGSDK_STRING, - WINGSDK_STRUCT, + UTIL_CLASS_NAME, WINGSDK_ARRAY, WINGSDK_ASSEMBLY_NAME, WINGSDK_BRINGABLE_MODULES, WINGSDK_DATETIME, WINGSDK_DURATION, + WINGSDK_GENERIC, WINGSDK_IRESOURCE, WINGSDK_JSON, WINGSDK_MAP, WINGSDK_MUT_ARRAY, WINGSDK_MUT_JSON, WINGSDK_MUT_MAP, + WINGSDK_MUT_SET, WINGSDK_NODE, WINGSDK_REGEX, WINGSDK_RESOURCE, WINGSDK_SET, WINGSDK_SIM_IRESOURCE_FQN, + WINGSDK_STD_MODULE, WINGSDK_STRING, WINGSDK_STRUCT, }; use camino::{Utf8Path, Utf8PathBuf}; use derivative::Derivative; @@ -228,6 +228,8 @@ pub enum Type { Number, String, Duration, + Datetime, + Regex, Boolean, Void, /// Immutable Json literals may store extra information about their known data @@ -874,6 +876,8 @@ impl Display for Type { Type::String => write!(f, "str"), Type::Stringable => write!(f, "stringable"), Type::Duration => write!(f, "duration"), + Type::Datetime => write!(f, "datetime"), + Type::Regex => write!(f, "regex"), Type::Boolean => write!(f, "bool"), Type::Void => write!(f, "void"), Type::Json(_) => write!(f, "Json"), @@ -1239,6 +1243,8 @@ impl TypeRef { Type::Enum(_) => true, // not serializable Type::Duration => false, + Type::Datetime => false, + Type::Regex => false, Type::Inferred(_) => false, Type::Set(_) => false, Type::MutSet(_) => false, @@ -1386,6 +1392,8 @@ pub struct Types { string_idx: usize, bool_idx: usize, duration_idx: usize, + datetime_idx: usize, + regex_idx: usize, anything_idx: usize, void_idx: usize, json_idx: usize, @@ -1421,6 +1429,10 @@ impl Types { let bool_idx = types.len() - 1; types.push(Box::new(Type::Duration)); let duration_idx = types.len() - 1; + types.push(Box::new(Type::Datetime)); + let datetime_idx = types.len() - 1; + types.push(Box::new(Type::Regex)); + let regex_idx = types.len() - 1; types.push(Box::new(Type::Anything)); let anything_idx = types.len() - 1; types.push(Box::new(Type::Void)); @@ -1445,6 +1457,8 @@ impl Types { string_idx, bool_idx, duration_idx, + datetime_idx, + regex_idx, anything_idx, void_idx, json_idx, @@ -1502,6 +1516,14 @@ impl Types { self.get_typeref(self.duration_idx) } + pub fn datetime(&self) -> TypeRef { + self.get_typeref(self.datetime_idx) + } + + pub fn regex(&self) -> TypeRef { + self.get_typeref(self.regex_idx) + } + pub fn anything(&self) -> TypeRef { self.get_typeref(self.anything_idx) } @@ -1769,6 +1791,8 @@ impl Types { Type::String => "String", Type::Boolean => "Boolean", Type::Duration => "Duration", + Type::Datetime => "Datetime", + Type::Regex => "Regex", Type::Json(_) => "Json", Type::MutJson => "MutJson", Type::Array(_) => "Array", @@ -3988,6 +4012,8 @@ It should primarily be used in preflight or in inflights that are guaranteed to TypeAnnotationKind::String => self.types.string(), TypeAnnotationKind::Bool => self.types.bool(), TypeAnnotationKind::Duration => self.types.duration(), + TypeAnnotationKind::Datetime => self.types.datetime(), + TypeAnnotationKind::Regex => self.types.regex(), TypeAnnotationKind::Void => self.types.void(), TypeAnnotationKind::Json => self.types.json(), TypeAnnotationKind::MutJson => self.types.mut_json(), @@ -4934,6 +4960,8 @@ It should primarily be used in preflight or in inflights that are guaranteed to | Type::Unresolved | Type::Number | Type::Duration + | Type::Datetime + | Type::Regex | Type::Boolean | Type::Void | Type::Nil @@ -6390,6 +6418,8 @@ It should primarily be used in preflight or in inflights that are guaranteed to } Type::Number | Type::Duration + | Type::Datetime + | Type::Regex | Type::Boolean | Type::Void | Type::Nil @@ -6493,6 +6523,18 @@ It should primarily be used in preflight or in inflights that are guaranteed to false, env, ), + Type::Datetime => self.get_property_from_class_like( + lookup_known_type(WINGSDK_DATETIME, env).as_class().unwrap(), + property, + false, + env, + ), + Type::Regex => self.get_property_from_class_like( + lookup_known_type(WINGSDK_REGEX, env).as_class().unwrap(), + property, + false, + env, + ), Type::Struct(ref s) => self.get_property_from_class_like(s, property, true, env), _ => self.spanned_error_with_var(property, "Property not found").0, } diff --git a/libs/wingc/src/type_check/inference_visitor.rs b/libs/wingc/src/type_check/inference_visitor.rs index 683576a0236..375803a5c14 100644 --- a/libs/wingc/src/type_check/inference_visitor.rs +++ b/libs/wingc/src/type_check/inference_visitor.rs @@ -109,6 +109,8 @@ impl<'a> crate::visit_types::VisitType<'_> for InferenceVisitor<'a> { | Type::String | Type::Stringable | Type::Duration + | Type::Datetime + | Type::Regex | Type::Boolean | Type::Void | Type::Json(_) diff --git a/libs/wingc/src/type_check/jsii_importer.rs b/libs/wingc/src/type_check/jsii_importer.rs index 858488e7f3c..74373bcb218 100644 --- a/libs/wingc/src/type_check/jsii_importer.rs +++ b/libs/wingc/src/type_check/jsii_importer.rs @@ -10,7 +10,8 @@ use crate::{ Class, FunctionParameter, FunctionSignature, Interface, ResolveSource, Struct, SymbolKind, Type, TypeRef, Types, CLASS_INIT_NAME, }, - CONSTRUCT_BASE_CLASS, WINGSDK_ASSEMBLY_NAME, WINGSDK_DURATION, WINGSDK_JSON, WINGSDK_MUT_JSON, WINGSDK_RESOURCE, + CONSTRUCT_BASE_CLASS, WINGSDK_ASSEMBLY_NAME, WINGSDK_DATETIME, WINGSDK_DURATION, WINGSDK_JSON, WINGSDK_MUT_JSON, + WINGSDK_REGEX, WINGSDK_RESOURCE, }; use colored::Colorize; use indexmap::IndexMap; @@ -96,6 +97,10 @@ impl<'a> JsiiImporter<'a> { let type_fqn = &named_ref.fqn; if type_fqn == &format!("{}.{}", WINGSDK_ASSEMBLY_NAME, WINGSDK_DURATION) { self.wing_types.duration() + } else if type_fqn == &format!("{}.{}", WINGSDK_ASSEMBLY_NAME, WINGSDK_DATETIME) { + self.wing_types.datetime() + } else if type_fqn == &format!("{}.{}", WINGSDK_ASSEMBLY_NAME, WINGSDK_REGEX) { + self.wing_types.regex() } else if type_fqn == &format!("{}.{}", WINGSDK_ASSEMBLY_NAME, WINGSDK_JSON) { self.wing_types.json() } else if type_fqn == &format!("{}.{}", WINGSDK_ASSEMBLY_NAME, WINGSDK_MUT_JSON) { diff --git a/libs/wingc/src/visit.rs b/libs/wingc/src/visit.rs index 2aee42a1002..e92bba6f237 100644 --- a/libs/wingc/src/visit.rs +++ b/libs/wingc/src/visit.rs @@ -498,6 +498,8 @@ where TypeAnnotationKind::String => {} TypeAnnotationKind::Bool => {} TypeAnnotationKind::Duration => {} + TypeAnnotationKind::Datetime => {} + TypeAnnotationKind::Regex => {} TypeAnnotationKind::Void => {} TypeAnnotationKind::Json => {} TypeAnnotationKind::MutJson => {} diff --git a/libs/wingc/src/visit_types.rs b/libs/wingc/src/visit_types.rs index df69f8404a0..a6447329d8d 100644 --- a/libs/wingc/src/visit_types.rs +++ b/libs/wingc/src/visit_types.rs @@ -47,6 +47,8 @@ where | Type::Number | Type::String | Type::Duration + | Type::Datetime + | Type::Regex | Type::Boolean | Type::Void | Type::Json(None) diff --git a/tools/hangar/__snapshots__/test_corpus/valid/std_type_annotation.test.w_compile_tf-aws.md b/tools/hangar/__snapshots__/test_corpus/valid/std_type_annotation.test.w_compile_tf-aws.md new file mode 100644 index 00000000000..9d6680eac0d --- /dev/null +++ b/tools/hangar/__snapshots__/test_corpus/valid/std_type_annotation.test.w_compile_tf-aws.md @@ -0,0 +1,260 @@ +# [std_type_annotation.test.w](../../../../../examples/tests/valid/std_type_annotation.test.w) | compile | tf-aws + +## inflight.$Closure1-1.cjs +```cjs +"use strict"; +const $helpers = require("@winglang/sdk/lib/helpers"); +const $macros = require("@winglang/sdk/lib/macros"); +module.exports = function({ }) { + class $Closure1 { + constructor($args) { + const { } = $args; + const $obj = (...args) => this.handle(...args); + Object.setPrototypeOf($obj, this); + return $obj; + } + async handle(x, y) { + return (y.timestampMs >= x.timestampMs); + } + } + return $Closure1; +} +//# sourceMappingURL=inflight.$Closure1-1.cjs.map +``` + +## inflight.$Closure2-1.cjs +```cjs +"use strict"; +const $helpers = require("@winglang/sdk/lib/helpers"); +const $macros = require("@winglang/sdk/lib/macros"); +module.exports = function({ $expect_Util, $isOlderOrEqual, $now, $std_Datetime, $then }) { + class $Closure2 { + constructor($args) { + const { } = $args; + const $obj = (...args) => this.handle(...args); + Object.setPrototypeOf($obj, this); + return $obj; + } + async handle() { + const later = (await $std_Datetime.fromComponents(({"day": $now.dayOfMonth, "hour": $now.hours, "year": ($now.year + 1), "min": $now.min, "month": $now.month, "ms": $now.ms, "sec": $now.sec, "tz": $now.timezone}))); + (await $expect_Util.ok((await $isOlderOrEqual($then, $now)))); + (await $expect_Util.ok((await $isOlderOrEqual($now, later)))); + } + } + return $Closure2; +} +//# sourceMappingURL=inflight.$Closure2-1.cjs.map +``` + +## inflight.$Closure3-1.cjs +```cjs +"use strict"; +const $helpers = require("@winglang/sdk/lib/helpers"); +const $macros = require("@winglang/sdk/lib/macros"); +module.exports = function({ }) { + class $Closure3 { + constructor($args) { + const { } = $args; + const $obj = (...args) => this.handle(...args); + Object.setPrototypeOf($obj, this); + return $obj; + } + async handle(re, s) { + return (await re.test(s)); + } + } + return $Closure3; +} +//# sourceMappingURL=inflight.$Closure3-1.cjs.map +``` + +## inflight.$Closure4-1.cjs +```cjs +"use strict"; +const $helpers = require("@winglang/sdk/lib/helpers"); +const $macros = require("@winglang/sdk/lib/macros"); +module.exports = function({ $decimal, $expect_Util, $std_Regex, $testString }) { + class $Closure4 { + constructor($args) { + const { } = $args; + const $obj = (...args) => this.handle(...args); + Object.setPrototypeOf($obj, this); + return $obj; + } + async handle() { + const binary = (await $std_Regex.compile("^[0-1]*$")); + (await $expect_Util.equal((await $testString($decimal, "24")), true)); + (await $expect_Util.equal((await $testString($decimal, "340523")), true)); + (await $expect_Util.equal((await $testString($decimal, "a23")), false)); + (await $expect_Util.equal((await $testString(binary, "01101")), true)); + (await $expect_Util.equal((await $testString(binary, "0")), true)); + (await $expect_Util.equal((await $testString(binary, "120010")), false)); + } + } + return $Closure4; +} +//# sourceMappingURL=inflight.$Closure4-1.cjs.map +``` + +## main.tf.json +```json +{ + "//": { + "metadata": { + "backend": "local", + "stackName": "root" + }, + "outputs": {} + }, + "provider": { + "aws": [ + {} + ] + } +} +``` + +## preflight.cjs +```cjs +"use strict"; +const $stdlib = require('@winglang/sdk'); +const $macros = require("@winglang/sdk/lib/macros"); +const $platforms = ((s) => !s ? [] : s.split(';'))(process.env.WING_PLATFORMS); +const $outdir = process.env.WING_SYNTH_DIR ?? "."; +const $wing_is_test = process.env.WING_IS_TEST === "true"; +const std = $stdlib.std; +const $helpers = $stdlib.helpers; +const $extern = $helpers.createExternRequire(__dirname); +const $PlatformManager = new $stdlib.platform.PlatformManager({platformPaths: $platforms}); +class $Root extends $stdlib.std.Resource { + constructor($scope, $id) { + super($scope, $id); + $helpers.nodeof(this).root.$preflightTypesMap = { }; + let $preflightTypesMap = {}; + const expect = $stdlib.expect; + $helpers.nodeof(this).root.$preflightTypesMap = $preflightTypesMap; + class $Closure1 extends $stdlib.std.AutoIdResource { + _id = $stdlib.core.closureId(); + constructor($scope, $id, ) { + super($scope, $id); + $helpers.nodeof(this).hidden = true; + } + static _toInflightType() { + return ` + require("${$helpers.normalPath(__dirname)}/inflight.$Closure1-1.cjs")({ + }) + `; + } + get _liftMap() { + return ({ + "handle": [ + ], + "$inflight_init": [ + ], + }); + } + } + class $Closure2 extends $stdlib.std.AutoIdResource { + _id = $stdlib.core.closureId(); + constructor($scope, $id, ) { + super($scope, $id); + $helpers.nodeof(this).hidden = true; + } + static _toInflightType() { + return ` + require("${$helpers.normalPath(__dirname)}/inflight.$Closure2-1.cjs")({ + $expect_Util: ${$stdlib.core.liftObject($stdlib.core.toLiftableModuleType(globalThis.$ClassFactory.resolveType("@winglang/sdk.expect.Util") ?? expect.Util, "@winglang/sdk/expect", "Util"))}, + $isOlderOrEqual: ${$stdlib.core.liftObject(isOlderOrEqual)}, + $now: ${$stdlib.core.liftObject(now)}, + $std_Datetime: ${$stdlib.core.liftObject($stdlib.core.toLiftableModuleType(globalThis.$ClassFactory.resolveType("@winglang/sdk.std.Datetime") ?? std.Datetime, "@winglang/sdk/std", "Datetime"))}, + $then: ${$stdlib.core.liftObject(then)}, + }) + `; + } + get _liftMap() { + return ({ + "handle": [ + [$stdlib.core.toLiftableModuleType(globalThis.$ClassFactory.resolveType("@winglang/sdk.expect.Util") ?? expect.Util, "@winglang/sdk/expect", "Util"), ["ok"]], + [$stdlib.core.toLiftableModuleType(globalThis.$ClassFactory.resolveType("@winglang/sdk.std.Datetime") ?? std.Datetime, "@winglang/sdk/std", "Datetime"), ["fromComponents"]], + [isOlderOrEqual, ["handle"]], + [now, [].concat(["dayOfMonth"], ["hours"], ["year"], ["min"], ["month"], ["ms"], ["sec"], ["timezone"])], + [then, []], + ], + "$inflight_init": [ + [$stdlib.core.toLiftableModuleType(globalThis.$ClassFactory.resolveType("@winglang/sdk.expect.Util") ?? expect.Util, "@winglang/sdk/expect", "Util"), []], + [$stdlib.core.toLiftableModuleType(globalThis.$ClassFactory.resolveType("@winglang/sdk.std.Datetime") ?? std.Datetime, "@winglang/sdk/std", "Datetime"), []], + [isOlderOrEqual, []], + [now, []], + [then, []], + ], + }); + } + } + class $Closure3 extends $stdlib.std.AutoIdResource { + _id = $stdlib.core.closureId(); + constructor($scope, $id, ) { + super($scope, $id); + $helpers.nodeof(this).hidden = true; + } + static _toInflightType() { + return ` + require("${$helpers.normalPath(__dirname)}/inflight.$Closure3-1.cjs")({ + }) + `; + } + get _liftMap() { + return ({ + "handle": [ + ], + "$inflight_init": [ + ], + }); + } + } + class $Closure4 extends $stdlib.std.AutoIdResource { + _id = $stdlib.core.closureId(); + constructor($scope, $id, ) { + super($scope, $id); + $helpers.nodeof(this).hidden = true; + } + static _toInflightType() { + return ` + require("${$helpers.normalPath(__dirname)}/inflight.$Closure4-1.cjs")({ + $decimal: ${$stdlib.core.liftObject(decimal)}, + $expect_Util: ${$stdlib.core.liftObject($stdlib.core.toLiftableModuleType(globalThis.$ClassFactory.resolveType("@winglang/sdk.expect.Util") ?? expect.Util, "@winglang/sdk/expect", "Util"))}, + $std_Regex: ${$stdlib.core.liftObject($stdlib.core.toLiftableModuleType(globalThis.$ClassFactory.resolveType("@winglang/sdk.std.Regex") ?? std.Regex, "@winglang/sdk/std", "Regex"))}, + $testString: ${$stdlib.core.liftObject(testString)}, + }) + `; + } + get _liftMap() { + return ({ + "handle": [ + [$stdlib.core.toLiftableModuleType(globalThis.$ClassFactory.resolveType("@winglang/sdk.expect.Util") ?? expect.Util, "@winglang/sdk/expect", "Util"), ["equal"]], + [$stdlib.core.toLiftableModuleType(globalThis.$ClassFactory.resolveType("@winglang/sdk.std.Regex") ?? std.Regex, "@winglang/sdk/std", "Regex"), ["compile"]], + [decimal, []], + [testString, ["handle"]], + ], + "$inflight_init": [ + [$stdlib.core.toLiftableModuleType(globalThis.$ClassFactory.resolveType("@winglang/sdk.expect.Util") ?? expect.Util, "@winglang/sdk/expect", "Util"), []], + [$stdlib.core.toLiftableModuleType(globalThis.$ClassFactory.resolveType("@winglang/sdk.std.Regex") ?? std.Regex, "@winglang/sdk/std", "Regex"), []], + [decimal, []], + [testString, []], + ], + }); + } + } + const then = (std.Datetime.utcNow()); + const now = then; + const isOlderOrEqual = new $Closure1(this, "$Closure1"); + globalThis.$ClassFactory.new("@winglang/sdk.std.Test", std.Test, this, "test:datetime is a valid type", new $Closure2(this, "$Closure2")); + const decimal = (std.Regex.compile("^[0-9]*$")); + const testString = new $Closure3(this, "$Closure3"); + globalThis.$ClassFactory.new("@winglang/sdk.std.Test", std.Test, this, "test:regex is valid type", new $Closure4(this, "$Closure4")); + } +} +const $APP = $PlatformManager.createApp({ outdir: $outdir, name: "std_type_annotation.test", rootConstruct: $Root, isTestEnvironment: $wing_is_test, entrypointDir: process.env['WING_SOURCE_DIR'], rootId: process.env['WING_ROOT_ID'] }); +$APP.synth(); +//# sourceMappingURL=preflight.cjs.map +``` + diff --git a/tools/hangar/__snapshots__/test_corpus/valid/std_type_annotation.test.w_test_sim.md b/tools/hangar/__snapshots__/test_corpus/valid/std_type_annotation.test.w_test_sim.md new file mode 100644 index 00000000000..e66568bfbee --- /dev/null +++ b/tools/hangar/__snapshots__/test_corpus/valid/std_type_annotation.test.w_test_sim.md @@ -0,0 +1,13 @@ +# [std_type_annotation.test.w](../../../../../examples/tests/valid/std_type_annotation.test.w) | test | sim + +## stdout.log +```log +pass ─ std_type_annotation.test.wsim » root/Default/test:datetime is a valid type +pass ─ std_type_annotation.test.wsim » root/Default/test:regex is valid type + +Tests 2 passed (2) +Snapshots 1 skipped +Test Files 1 passed (1) +Duration +``` +