Skip to content

Add PreferStructOverMap lint rule (Issue #717)#740

Closed
Abhijit1018 wants to merge 1 commit intostjude-rust-labs:mainfrom
Abhijit1018:feature/prefer-struct-over-map-rule
Closed

Add PreferStructOverMap lint rule (Issue #717)#740
Abhijit1018 wants to merge 1 commit intostjude-rust-labs:mainfrom
Abhijit1018:feature/prefer-struct-over-map-rule

Conversation

@Abhijit1018
Copy link

This PR introduces the PreferStructOverMap lint rule to the WDL pedantic suite. This rule flags uses of Map[String, *] in declarations, encouraging developers to use Structs for better type safety, validation, and self-documenting code.

📋 Rule Overview

Property | Details -- | -- Rule ID | PreferStructOverMap Tags | Clarity, Type Safety WDL Version | ≥ 1.1 Scope | Inputs, Outputs, and Private Declarations (Tasks & Workflows) Default Level | Pedantic (Warning)

💡 Rationale

Using a Map[String, *] for structured data is often an anti-pattern in WDL. While flexible, it lacks the explicit schema validation provided by a struct.

  • Before (Fragile): Relies on runtime key-checking and lacks clear documentation of expected fields.

  • After (Robust): Provides a clear interface, compile-time (or lint-time) validation, and better IDE support.


🛠 Implementation Details

The rule follows the standard Visitor pattern used throughout the wdl-lint crate.

Key Features:

  • Recursive Detection: Deeply inspects nested types (e.g., Array[Map[String, Int]]).

  • Key Filtering: Specifically targets String keys only; Map[Int, *] remains valid.

  • Version Gated: Automatically disables itself for WDL version < 1.1.

  • Suppression Support: Compatible with #@ except: PreferStructOverMap.


📂 File Changes

  • crates/wdl-lint/src/rules/prefer_struct_over_map.rs

    • Core logic for type traversal and diagnostic emission.

  • crates/wdl-lint/src/rules.rs

    • Registered the new module.

  • crates/wdl-lint/src/lib.rs

    • Integrated the rule into the primary lint engine.


📝 Examples

❌ Bad

Code snippet
task align {
  input {
    Map[String, String] sample_info
    Map[String, File] input_files
  }
}

✅ Good

Code snippet
struct Sample {
  String id
  String library
  File fastq_r1
}

task align {
input {
Sample sample
}
}


🔗 Context

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new PreferStructOverMap lint rule to wdl-lint intended to discourage Map[String, *] declarations in favor of struct usage for clearer, more type-safe WDL.

Changes:

  • Introduces PreferStructOverMapRule with recursive type traversal and WDL version gating (≥ 1.1).
  • Registers and re-exports the new rule module.
  • Adds the rule to the default rule list in wdl-lint.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
crates/wdl-lint/src/rules/prefer_struct_over_map.rs Implements the new rule, diagnostics, type traversal, and visitor hooks.
crates/wdl-lint/src/rules.rs Registers the new rule module and exports it.
crates/wdl-lint/src/lib.rs Adds the rule to the constructed rule list returned by rules().

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +115 to +117
Type::Map(_) => {
// Already checked above, no need to recurse into value type
// as that would cause duplicate diagnostics
Box::<rules::OutputNameRule>::default(),
Box::new(rules::DeclarationNameRule::new(config)),
Box::<rules::RedundantNone>::default(),
Box::new(rules::PreferStructOverMapRule::new(false)),
Comment on lines +188 to +227
fn bound_decl(&mut self, diagnostics: &mut Diagnostics, reason: VisitReason, decl: &BoundDecl) {
if reason == VisitReason::Exit {
return;
}

// Check version - only apply to WDL >= 1.1
if let Some(SupportedVersion::V1(version)) = self.version {
if version < V1::One {
return;
}
} else {
return;
}

let ty = decl.ty();
self.check_type(diagnostics, &ty, &self.exceptable_nodes());
}

fn unbound_decl(
&mut self,
diagnostics: &mut Diagnostics,
reason: VisitReason,
decl: &UnboundDecl,
) {
if reason == VisitReason::Exit {
return;
}

// Check version - only apply to WDL >= 1.1
if let Some(SupportedVersion::V1(version)) = self.version {
if version < V1::One {
return;
}
} else {
return;
}

let ty = decl.ty();
self.check_type(diagnostics, &ty, &self.exceptable_nodes());
}
//! by flagging Map[String, *] types in declarations where a Struct would provide
//! clearer semantics and better validation.

use std::fmt::Debug;
@adthrasher adthrasher closed this Mar 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants