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
33 changes: 11 additions & 22 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ repository = "https://github.com/wrightkit/wright"
# parser, emitter, detection, validation, and Workshop IR. This is the single
# released reference for the cutover — workspace crates consume it via
# `workshop-rs.workspace = true`.
workshop-rs = "=0.1.2"
workshop-rs = "=0.1.5"

[workspace.lints.rust]
# Unsafe operations inside an unsafe function still need an explicit block so
Expand Down
11 changes: 6 additions & 5 deletions compatibility/ostw/reconstruction/support-boundary.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"allSupportHeroes", "allTankHeroes", "allowedHeroes", "arrayContains",
"countOf", "crossProduct", "currentMap", "customColor",
"directionFromAngles", "evaluateOnce", "filteredArray",
"forward", "hasSpawned", "heroIconString", "horizontalAngleFromDirection",
"hasSpawned", "heroIconString", "horizontalAngleFromDirection",
"iconString", "indexOfArrayValue", "inputBindingString", "isButtonHeld",
"isInSpawnRoom", "isTrueForAll", "isWaitingForPlayers", "lastCreatedEntity",
"lastOf", "lastTextId", "localPlayer", "max", "min",
Expand Down Expand Up @@ -51,16 +51,17 @@
"syntaxActionIds": ["abort"],
"enumDomains": [
"Button", "Clipping", "Color", "DynamicEffect", "Hero", "HudPosition",
"HudReeval", "Icon", "Invis", "InworldTextReeval", "Map", "Rounding",
"SpecVisibility", "Team", "Wait"
"HudReeval", "Icon", "Invis", "Map", "Rounding",
"WorldTextReeval",
"SpecVisibility", "Team", "Vector", "Wait"
],
"enumMembers": [
"Button.ABILITY_1", "Button.ABILITY_2", "Clipping.DO_NOT_CLIP",
"Color.RED", "Color.WHITE", "DynamicEffect.BUFF_IMPACT_SOUND",
"Hero.ANA", "HudPosition.LEFT", "HudReeval.VISIBILITY", "Icon.SKULL",
"Invis.ALL", "InworldTextReeval.VISIBLE_TO_AND_COLOR", "Map.HANAMURA",
"Invis.ALL", "WorldTextReeval.VISIBILITY_AND_COLOR", "Map.HANAMURA",
"Rounding.DOWN", "Rounding.UP", "SpecVisibility.VISIBLE_ALWAYS",
"Team.ALL", "Wait.ABORT_WHEN_FALSE", "Wait.IGNORE_CONDITION"
"Team.ALL", "Vector.FORWARD", "Wait.ABORT_WHEN_FALSE", "Wait.IGNORE_CONDITION"
]
},
"rejected": [
Expand Down
11 changes: 11 additions & 0 deletions crates/wright-analyzer/src/analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,10 @@ fn visit_action_value_roots(
visit_value_with_parent(program, *player, parents, out);
visit_value_with_parent(program, *value, parents, out);
}
Action::AssignMember { target, value, .. } => {
visit_value_with_parent(program, *target, parents, out);
visit_value_with_parent(program, *value, parents, out);
}
Action::CallSubroutine { .. } => {}
Action::If { branches, .. } => {
for branch in branches {
Expand Down Expand Up @@ -643,6 +647,7 @@ fn visit_value_children(value: &Value, f: &mut impl FnMut(ValueId)) {
| Value::Null
| Value::Enum { .. }
| Value::GlobalVariable(_)
| Value::Subroutine(_)
| Value::EventPlayer => {}
}
}
Expand Down Expand Up @@ -904,6 +909,7 @@ fn action_writes(program: &wir::Program, action: &Action, variable: &Variable) -
body.iter().any(|id| subtree_writes(program, *id, variable))
}
Action::Debug { .. } | Action::Print { .. } => false,
Action::AssignMember { .. } => true,
}
}

Expand Down Expand Up @@ -1079,6 +1085,7 @@ fn visit_actions(
| Action::CallSubroutine { .. }
| Action::Debug { .. }
| Action::Print { .. }
| Action::AssignMember { .. }
| Action::Call { .. } => {}
}
}
Expand All @@ -1096,6 +1103,10 @@ fn visit_values_in_action(program: &wir::Program, action: &Action, f: &mut impl
visit_value(program, *player, f);
visit_value(program, *value, f);
}
Action::AssignMember { target, value, .. } => {
visit_value(program, *target, f);
visit_value(program, *value, f);
}
Action::CallSubroutine { .. } => {}
Action::If { branches, .. } => {
for branch in branches {
Expand Down
1 change: 1 addition & 0 deletions crates/wright-analyzer/src/cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,7 @@ fn action_name(program: &wir::Program, action: ActionId) -> String {
.map_or_else(|| "<dangling>".to_string(), |v| v.name.clone());
format!("modifyPlayerVariable {name} {}", op.as_str())
}
Some(Action::AssignMember { .. }) => "assignMember".to_string(),
Some(Action::If { .. }) => "if".to_string(),
Some(Action::While { .. }) => "while".to_string(),
Some(Action::ForGlobalVariable { variable, .. }) => {
Expand Down
5 changes: 5 additions & 0 deletions crates/wright-analyzer/src/symbols.rs
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,10 @@ impl<'a> Builder<'a> {
self.walk_value(*player, rule, Some(action_id))?;
self.walk_value(*value, rule, Some(action_id))
}
Action::AssignMember { target, value, .. } => {
self.walk_value(*target, rule, Some(action_id))?;
self.walk_value(*value, rule, Some(action_id))
}
Action::CallSubroutine {
subroutine,
callee_span,
Expand Down Expand Up @@ -536,6 +540,7 @@ impl<'a> Builder<'a> {
| Value::Bool(_)
| Value::Null
| Value::Enum { .. }
| Value::Subroutine(_)
| Value::EventPlayer => Ok(()),
}
}
Expand Down
6 changes: 3 additions & 3 deletions crates/wright-core/tests/hir_ingest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -429,13 +429,13 @@ fn settings_mode_subset_violations_are_rejected() {
matches!(&error, HirError::Invalid { span: Some(_), .. }),
"the violation carries the key span"
);
// gamemodes.general.roleLimit is outside the per-key subsets (roleLimit
// is evidenced under assault/control/escort/hybrid only, #86).
// The released settings table recognizes general.roleLimit as a key but
// rejects the inherited `off` value at this path.
let payload = VALID_SETTINGS
.replace("__SPAN__", SPAN)
.replace("\"name\": \"heroLimit\"", "\"name\": \"roleLimit\"");
let error = hir::parse_str(&payload).unwrap_err();
assert_eq!(error.code(), "settings-unknown-key");
assert_eq!(error.code(), "settings-unknown-value");
}

#[test]
Expand Down
3 changes: 1 addition & 2 deletions crates/wright-driver/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,5 @@ wright-opy = { path = "../wright-opy" }
wright-ostw = { path = "../wright-ostw" }
wright-transform = { path = "../wright-transform" }
wright-workshop = { path = "../wright-workshop" }
# Single pinned reference: `[workspace.dependencies]` in the root Cargo.toml.
# Single released reference: `[workspace.dependencies]` in the root Cargo.toml.
workshop-rs.workspace = true
workshop-rs-provider = { package = "workshop-rs", git = "https://github.com/wrightkit/workshop-rs.git", rev = "34d2a55c0170d80b7359140e4280d47f0c42dd60" }
66 changes: 26 additions & 40 deletions crates/wright-driver/src/workshop_provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,35 +10,31 @@ use wright_core::signatures::ExpectedDomain as WrightExpectedDomain;

/// Wright's in-process provider for localized raw Workshop source.
pub struct WorkshopProvider {
catalog: workshop_rs_provider::catalog::Catalog,
catalog: workshop_rs::catalog::Catalog,
}

impl WorkshopProvider {
/// Construct a provider from the canonical Workshop catalog.
pub fn new() -> ProviderResult<Self> {
let catalog = workshop_rs_provider::catalog::Catalog::builtin()
let catalog = workshop_rs::catalog::Catalog::builtin()
.map_err(|error| ProviderError::new("workshop.catalog", error.to_string()))?;
Ok(Self { catalog })
}
}

impl LanguageProvider for WorkshopProvider {
fn check(&self, source: &str, path: &Path) -> ProviderResult<Vec<ProviderDiagnostic>> {
let locale = workshop_rs_provider::detect::resolve_locale(source, &self.catalog, None)
let locale = workshop_rs::detect::resolve_locale(source, &self.catalog, None)
.map_err(|error| ProviderError::new("workshop.locale", error.to_string()))?;
let manifest = wright_opy::manifest::Manifest::builtin()
.map_err(|error| ProviderError::new("workshop.manifest", error.to_string()))?;
let context = ProviderExpectedDomain {
manifest,
catalog: &self.catalog,
};
let program = workshop_rs_provider::parser::parse_with_context(
source,
&self.catalog,
&locale,
&context,
)
.map_err(|error| ProviderError::new("workshop.parse", error.to_string()))?;
let program =
workshop_rs::parser::parse_with_context(source, &self.catalog, &locale, &context)
.map_err(|error| ProviderError::new("workshop.parse", error.to_string()))?;
program
.validate()
.map_err(|error| ProviderError::new("workshop.validate", error.to_string()))?;
Expand All @@ -53,13 +49,13 @@ impl LanguageProvider for WorkshopProvider {

struct ProviderExpectedDomain<'a> {
manifest: &'a wright_opy::manifest::Manifest,
catalog: &'a workshop_rs_provider::catalog::Catalog,
catalog: &'a workshop_rs::catalog::Catalog,
}

impl workshop_rs_provider::signatures::ExpectedDomain for ProviderExpectedDomain<'_> {
impl workshop_rs::signatures::ExpectedDomain for ProviderExpectedDomain<'_> {
fn expected_domain(&self, catalog_id: &str, arg_index: usize) -> Option<&str> {
WrightExpectedDomain::expected_domain(self.manifest, catalog_id, arg_index).or_else(|| {
workshop_rs_provider::signatures::ExpectedDomain::expected_domain(
workshop_rs::signatures::ExpectedDomain::expected_domain(
self.catalog,
catalog_id,
arg_index,
Expand All @@ -68,21 +64,18 @@ impl workshop_rs_provider::signatures::ExpectedDomain for ProviderExpectedDomain
}
}

fn map_issue(
issue: workshop_rs_provider::semantic::SemanticIssue,
path: &Path,
) -> ProviderDiagnostic {
fn map_issue(issue: workshop_rs::semantic::SemanticIssue, path: &Path) -> ProviderDiagnostic {
let (kind_code, severity) = match issue.kind {
workshop_rs_provider::semantic::IncompletenessKind::RawSetting => {
workshop_rs::semantic::IncompletenessKind::RawSetting => {
("raw-setting", ProviderSeverity::Warning)
}
workshop_rs_provider::semantic::IncompletenessKind::UnknownAction => {
workshop_rs::semantic::IncompletenessKind::UnknownAction => {
("unknown-action", ProviderSeverity::Error)
}
workshop_rs_provider::semantic::IncompletenessKind::UnknownValue => {
workshop_rs::semantic::IncompletenessKind::UnknownValue => {
("unknown-value", ProviderSeverity::Error)
}
workshop_rs_provider::semantic::IncompletenessKind::OpaqueAction => {
workshop_rs::semantic::IncompletenessKind::OpaqueAction => {
("opaque-action", ProviderSeverity::Error)
}
};
Expand All @@ -104,16 +97,14 @@ fn map_issue(
}

pub fn status_for_classification(
classification: workshop_rs_provider::semantic::ResidualClassification,
classification: workshop_rs::semantic::ResidualClassification,
) -> Status {
match classification {
workshop_rs_provider::semantic::ResidualClassification::ProjectDefinedConstruct
| workshop_rs_provider::semantic::ResidualClassification::SourceDeclaredVariable => {
Status::Partial
}
workshop_rs_provider::semantic::ResidualClassification::ProducerExtension
| workshop_rs_provider::semantic::ResidualClassification::LegacyOpaque
| workshop_rs_provider::semantic::ResidualClassification::UnresolvedIdentifier => {
workshop_rs::semantic::ResidualClassification::ProjectDefinedConstruct
| workshop_rs::semantic::ResidualClassification::SourceDeclaredVariable => Status::Partial,
workshop_rs::semantic::ResidualClassification::ProducerExtension
| workshop_rs::semantic::ResidualClassification::LegacyOpaque
| workshop_rs::semantic::ResidualClassification::UnresolvedIdentifier => {
Status::Unsupported
}
}
Expand Down Expand Up @@ -143,10 +134,7 @@ fn status_name(status: Status) -> &'static str {
}
}

fn provider_span(
span: Option<workshop_rs_provider::source::Span>,
path: &Path,
) -> ProviderSourceSpan {
fn provider_span(span: Option<workshop_rs::source::Span>, path: &Path) -> ProviderSourceSpan {
let (start_line, start_col, end_line, end_col) = span
.map(|span| (span.start.line, span.start.col, span.end.line, span.end.col))
.unwrap_or((1, 1, 1, 1));
Expand All @@ -167,31 +155,29 @@ mod tests {
fn maps_residual_classifications_fail_closed() {
assert_eq!(
status_for_classification(
workshop_rs_provider::semantic::ResidualClassification::ProjectDefinedConstruct
workshop_rs::semantic::ResidualClassification::ProjectDefinedConstruct
),
Status::Partial
);
assert_eq!(
status_for_classification(
workshop_rs_provider::semantic::ResidualClassification::SourceDeclaredVariable
workshop_rs::semantic::ResidualClassification::SourceDeclaredVariable
),
Status::Partial
);
assert_eq!(
status_for_classification(
workshop_rs_provider::semantic::ResidualClassification::ProducerExtension
workshop_rs::semantic::ResidualClassification::ProducerExtension
),
Status::Unsupported
);
assert_eq!(
status_for_classification(
workshop_rs_provider::semantic::ResidualClassification::LegacyOpaque
),
status_for_classification(workshop_rs::semantic::ResidualClassification::LegacyOpaque),
Status::Unsupported
);
assert_eq!(
status_for_classification(
workshop_rs_provider::semantic::ResidualClassification::UnresolvedIdentifier
workshop_rs::semantic::ResidualClassification::UnresolvedIdentifier
),
Status::Unsupported
);
Expand Down
Loading
Loading