Summary
smithy-rs server codegen emits Rust source files that contain lines with only indentation (whitespace-only lines). When running cargo fmt against the generated workspace, rustfmt aborts with error[internal]: left behind trailing whitespace and refuses to format the file. The same condition also surfaces as a [WARNING] Failed to run cargo fmt: ... during the Gradle build, because the codegen pipeline invokes cargo fmt on its output.
This reproduces on the stock smithy-rust-quickstart template with no modifications.
Versions
- Template:
smithy init -t smithy-rust-quickstart
smithyRsVersion: 0.1.3 (from the template's gradle.properties) (updating to 0.1.14 and has the same issue too)
smithyVersion: 1.69.0
smithyGradleVersion: 1.4.0
- Smithy CLI:
1.69.0
- Rust toolchain verified: stable
1.95.0 (rustfmt 1.9.0) and 1.85.0 (rustfmt 1.8.0). Both fail with the same internal error.
- Platform: macOS aarch64
Reproducer 1 (stock template, no modifications)
smithy init -t smithy-rust-quickstart
cd smithy-rust-quickstart
./gradlew build
cargo fmt
The Gradle build already logs:
[WARNING] [rust-server-codegen] Generating Rust server for service ...
Failed to run cargo fmt: [com.example#CoffeeShop]
Unexpected exception thrown when executing subprocess:
...
And cargo fmt fails:
error[internal]: left behind trailing whitespace
--> server/build/smithyprojections/server/source/rust-server-codegen/src/model.rs:411:1
|
411 |
| ^^^^
|
warning: rustfmt has failed to format. See previous 1 errors.
Line 411 of the generated model.rs is 4 spaces followed by a newline. It sits inside ConstraintViolation::as_validation_exception_field for the Uuid shape (which has @length + @pattern). The surrounding block also has inconsistent indentation:
impl ConstraintViolation {
pub(crate) fn as_validation_exception_field(
self,
path: ::std::string::String,
) -> crate::model::ValidationExceptionField {
match self {
Self::Length(length) => crate::model::ValidationExceptionField {
message: format!("Value with length {} at '{}' failed to satisfy constraint: Member must have length between 1 and 128, inclusive", length, &path),
path,
},
⎵⎵⎵⎵ ← line 411: whitespace-only, 4 spaces
#[allow(unused_variables)]
Self::Pattern(_) => crate::model::ValidationExceptionField {
message: format!("Value at '{}' failed to satisfy constraint: Member must satisfy regular expression pattern: {}", &path, r#"..."#),
path
},
}
}
}
Reproducer 2 (union with constrained structure variants, inside operation input)
Add the following shapes to the template and wire the union into the CreateOrder input. The shape names are long enough that the generated variant expression breaks across multiple lines — with shorter names rustfmt collapses the expression onto one line, which hides the bug (see note below).
// common.smithy
/// Describes how a customer subscription is renewed
union CustomerSubscriptionRenewalPolicy {
scheduledRenewal: ScheduledRenewalWindow
autoRenewal: AutoRenewalConfiguration
cancelled: smithy.api#Unit
}
structure ScheduledRenewalWindow {
@required
scheduledAt: Timestamp
}
structure AutoRenewalConfiguration {
@required
intervalDays: Long
}
// order.smithy — inside CreateOrder.input
input := for Order {
@required
$coffeeType
@notProperty
renewalPolicy: CustomerSubscriptionRenewalPolicy
}
cargo fmt now reports the model.rs error from Reproducer 1 plus four additional errors in unconstrained.rs (two per constrained-structure variant):
error[internal]: left behind trailing whitespace
--> server/build/smithyprojections/server/source/rust-server-codegen/src/model.rs:532:1
error[internal]: left behind trailing whitespace
--> server/build/smithyprojections/server/source/rust-server-codegen/src/unconstrained.rs:34:1
error[internal]: left behind trailing whitespace
--> server/build/smithyprojections/server/source/rust-server-codegen/src/unconstrained.rs:35:1
error[internal]: left behind trailing whitespace
--> server/build/smithyprojections/server/source/rust-server-codegen/src/unconstrained.rs:42:1
error[internal]: left behind trailing whitespace
--> server/build/smithyprojections/server/source/rust-server-codegen/src/unconstrained.rs:43:1
warning: rustfmt has failed to format. See previous 5 errors.
The offending region in the generated unconstrained.rs (whitespace made visible with ⎵, one 40-space indent per ⎵⎵⎵… group shown):
match value {
crate::unconstrained::customer_subscription_renewal_policy_unconstrained::CustomerSubscriptionRenewalPolicyUnconstrained::AutoRenewal(unconstrained) => Self::AutoRenewal(
unconstrained
.try_into()
⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵ ← 40-space whitespace-only line
⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵ ← 40-space whitespace-only line
.map_err(Self::Error::AutoRenewal)?
),
crate::unconstrained::customer_subscription_renewal_policy_unconstrained::CustomerSubscriptionRenewalPolicyUnconstrained::Cancelled => Self::Cancelled,
crate::unconstrained::customer_subscription_renewal_policy_unconstrained::CustomerSubscriptionRenewalPolicyUnconstrained::ScheduledRenewal(unconstrained) => Self::ScheduledRenewal(
unconstrained
.try_into()
⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵ ← 40-space whitespace-only line
⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵⎵ ← 40-space whitespace-only line
.map_err(Self::Error::ScheduledRenewal)?
),
}
Note on name length: with a short union (e.g. union U { a: A, b: smithy.api#Unit } and structure A { @required field: Long }), the codegen emits the same template, but each variant's expression is short enough that rustfmt collapses it onto one line (Self::A(unconstrained.try_into().map_err(Self::Error::A)?)), absorbing the whitespace-only lines. The unconstrained.rs error only surfaces once a variant's expression exceeds rustfmt's line-break threshold.
Expected behavior
Generated .rs files should not contain whitespace-only lines (or any trailing whitespace). cargo fmt should succeed on a freshly generated workspace, and the codegen's internal cargo fmt invocation should not fail.
Summary
smithy-rsserver codegen emits Rust source files that contain lines with only indentation (whitespace-only lines). When runningcargo fmtagainst the generated workspace,rustfmtaborts witherror[internal]: left behind trailing whitespaceand refuses to format the file. The same condition also surfaces as a[WARNING] Failed to run cargo fmt: ...during the Gradle build, because the codegen pipeline invokescargo fmton its output.This reproduces on the stock
smithy-rust-quickstarttemplate with no modifications.Versions
smithy init -t smithy-rust-quickstartsmithyRsVersion:0.1.3(from the template'sgradle.properties) (updating to0.1.14and has the same issue too)smithyVersion:1.69.0smithyGradleVersion:1.4.01.69.01.95.0(rustfmt1.9.0) and1.85.0(rustfmt1.8.0). Both fail with the same internal error.Reproducer 1 (stock template, no modifications)
The Gradle build already logs:
And
cargo fmtfails:Line 411 of the generated
model.rsis 4 spaces followed by a newline. It sits insideConstraintViolation::as_validation_exception_fieldfor theUuidshape (which has@length+@pattern). The surrounding block also has inconsistent indentation:Reproducer 2 (union with constrained structure variants, inside operation input)
Add the following shapes to the template and wire the union into the
CreateOrderinput. The shape names are long enough that the generated variant expression breaks across multiple lines — with shorter namesrustfmtcollapses the expression onto one line, which hides the bug (see note below).cargo fmtnow reports themodel.rserror from Reproducer 1 plus four additional errors inunconstrained.rs(two per constrained-structure variant):The offending region in the generated
unconstrained.rs(whitespace made visible with⎵, one 40-space indent per⎵⎵⎵…group shown):Note on name length: with a short union (e.g.
union U { a: A, b: smithy.api#Unit }andstructure A { @required field: Long }), the codegen emits the same template, but each variant's expression is short enough thatrustfmtcollapses it onto one line (Self::A(unconstrained.try_into().map_err(Self::Error::A)?)), absorbing the whitespace-only lines. Theunconstrained.rserror only surfaces once a variant's expression exceedsrustfmt's line-break threshold.Expected behavior
Generated
.rsfiles should not contain whitespace-only lines (or any trailing whitespace).cargo fmtshould succeed on a freshly generated workspace, and the codegen's internalcargo fmtinvocation should not fail.