# \[Pitch\] Warning Control Settings for SwiftPM

**URL:** <https://forums.swift.org/t/pitch-warning-control-settings-for-swiftpm/78666>\
**Category:** Pitches\
**Created:** [March 19, 2025, 8:27pm UTC](https://forums.swift.org/t/pitch-warning-control-settings-for-swiftpm/78666 "2025-03-19T20:27:01Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![dmt](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/dmt/32/30830_2.png) [@dmt](https://forums.swift.org/u/dmt)\
**Post date:** [March 19, 2025, 8:27pm UTC](https://forums.swift.org/t/pitch-warning-control-settings-for-swiftpm/78666/1 "2025-03-19T20:27:01Z")

</div>

Hi everyone. This is a follow-up pitch to [SE-0443](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0443-warning-control-flags.md) that adds new settings to SwiftPM to control how compilers treat warnings.

SE-0443 left support for those new Swift compiler options in SwiftPM as a future direction. And SwiftPM never supported similar options for C/C++. This proposal aims to close this gap.

A plain markdown version is available [here](https://github.com/DmT021/swift-evolution/blob/wp/swiftpm-warning-controls/proposals/NNNN-swiftpm-warning-control.md).  
Implementation: [swiftlang/swift-package-manager#8315](https://github.com/swiftlang/swift-package-manager/pull/8315)

## Introduction

This proposal adds new settings to SwiftPM to control how the Swift, C, and C++ compilers treat warnings during the build process. It builds on [SE-0443](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0443-warning-control-flags.md), which introduced warning control flags for the Swift compiler but left SwiftPM support as a future direction.

## Motivation

The Swift Package Manager currently lacks a unified way to control warnings across Swift, C, and C++ compilation. This limitation forces developers to either use `unsafeFlags` or accept the default warning settings.

## Proposed solution

This proposal introduces new methods to SwiftPM's build settings API, allowing fine-grained control over warnings.

### API

#### Cross-language API (Swift, C, and C++)

```swift
/// The level at which a compiler warning should be treated.
public enum WarningTreatLevel: String {
    /// Treat as a warning.
    ///
    /// Warnings will be displayed during compilation but will not cause the build to fail.
    case warning

    /// Treat as an error.
    ///
    /// Warnings will be elevated to errors, causing the build to fail if any such warnings occur.
    case error
}

public static func treatAllWarnings(
    as level: WarningTreatLevel,
    _ condition: BuildSettingCondition? = nil
) -> SwiftSetting // or CSetting or CXXSetting

public static func treatWarning(
    name: String,
    as level: WarningTreatLevel,
    _ condition: BuildSettingCondition? = nil
) -> SwiftSetting // or CSetting or CXXSetting

```

#### C/C++-specific API

In C/C++ targets, we can also enable or disable specific warning groups, in addition to controlling their severity.

```swift
public static func enableWarning(
    name: String,
    _ condition: BuildSettingCondition? = nil
) -> CSetting // or CXXSetting

public static func disableWarning(
    name: String,
    _ condition: BuildSettingCondition? = nil
) -> CSetting // or CXXSetting

```

_The necessity of these functions is also explained below in the Alternatives considered section._

### Example usage

```swift
.target(
    name: "MyLib",
    swiftSettings: [
        .treatAllWarnings(as: .error),
        .treatWarning(name: "DeprecatedDeclaration", as: .warning),
    ],
    cSettings: [
        .enableWarning(name: "all"),
        .disableWarning(name: "unused-function"),

        .treatAllWarnings(as: .error),
        .treatWarning(name: "unused-variable", as: .warning),
    ],
    cxxSettings: [
        .enableWarning(name: "all"),
        .disableWarning(name: "unused-function"),

        .treatAllWarnings(as: .error),
        .treatWarning(name: "unused-variable", as: .warning),
    ]
)

```

## Detailed design

### Settings and their corresponding compiler flags

| Method | Swift | C/C++ |
| --- | --- | --- |
| `treatAllWarnings(as: .error)` | `-warnings-as-errors` | `-Werror` |
| `treatAllWarnings(as: .warning)` | `-no-warnings-as-errors` | `-Wno-error` |
| `treatWarning(name: "XXXX", as: .error)` | `-Werror XXXX` | `-Werror=XXXX` |
| `treatWarning(name: "XXXX", as: .warning)` | `-Wwarning XXXX` | `-Wno-error=XXXX` |
| `enableWarning(name: "XXXX")` | N/A | `-WXXXX` |
| `disableWarning(name: "XXXX")` | N/A | `-Wno-XXXX` |

### Order of settings evaluation

The order in which warning control settings are specified in a target's settings array directly affects the order of the resulting compiler flags. This is critical because when multiple flags affect the same warning group, compilers apply them sequentially with the last flag taking precedence.

For example, consider these two different orderings for C++ settings:

```swift
// Example 1: "unused-variable" in front of "unused"
cxxSettings: [
    .treatWarning(name: "unused-variable", as: .error),
    .treatWarning(name: "unused", as: .warning),
]

// Example 2: "unused" in front of "unused-variable"
cxxSettings: [
    .treatWarning(name: "unused", as: .warning),
    .treatWarning(name: "unused-variable", as: .error),
]

```

In Example 1, the compiler will receive flags in this order:

```swift
-Werror=unused-variable -Wno-error=unused

```

Since "unused-variable" is a specific subgroup of the broader "unused" group, and the "unused" flag is applied last, all unused warnings (including unused-variable) will be treated as warnings.

In Example 2, the compiler will receive flags in this order:

```swift
-Wno-error=unused -Werror=unused-variable

```

Due to the "last one wins" rule, unused-variable warnings will be treated as errors, while other unused warnings remain as warnings.

The same principle applies when combining any of the new build settings:

```swift
cxxSettings: [
    .enableWarning(name: "all"), // Enable the "all" warning group
    .enableWarning(name: "extra"), // Enable the "extra" warning group
    .disableWarning(name: "unused-parameter"), // Disable the "unused-parameter" warning group
    .treatAllWarnings(as: .error), // Treat all warnings as errors
    .treatWarning(name: "unused", as: .warning), // Keep warnings of the "unused" group as warnings
]

```

This will result in compiler flags:

```swift
-Wall -Wextra -Wno-unused-parameter -Werror -Wno-error=unused

```

When configuring warnings, be mindful of the order to achieve the desired behavior.

### Remote targets behavior

When a target is remote (pulled from a package dependency rather than defined in the local package), the warning control settings specified in the manifest do not apply to it. SwiftPM will strip all of the warning control flags for remote targets and substitute them with options for suppressing warnings (`-w` for Clang and `-suppress-warnings` for Swift).

This behavior is already in place but takes into account only `-warnings-as-errors` (for Swift) and `-Werror` (for Clang) flags. We expand this list to include the following warning-related flags:

**For C/C++:**

- `-Wxxxx`
- `-Wno-xxxx`
- `-Werror`
- `-Werror=xxxx`
- `-Wno-error`
- `-Wno-error=xxxx`

**For Swift:**

- `-warnings-as-errors`
- `-no-warnings-as-errors`
- `-Wwarning xxxx`
- `-Werror xxxx`

This approach ensures that warning control settings are applied only to the targets you directly maintain in your package, while dependencies remain buildable without warnings regardless of their warning settings.

### Interaction with command-line flags

SwiftPM allows users to pass additional flags to the compilers using the `-Xcc`, `-Xswiftc`, and `-Xcxx` options with the `swift build` command. These flags are appended **after** the flags generated from the package manifest.

This ordering enables users to modify or override package-defined warning settings without modifying the package manifest.

#### Example

```swift
let package = Package(
    name: "MyExecutable",
    targets: [
        // C target with warning settings
        .target(
            name: "cfoo",
            cSettings: [
                .enableWarning(name: "all"),
                .treatAllWarnings(as: .error),
                .treatWarning(name: "unused-variable", as: .warning),
            ]
        ),
        // Swift target with warning settings
        .executableTarget(
            name: "swiftfoo",
            swiftSettings: [
                .treatAllWarnings(as: .error),
                .treatWarning(name: "DeprecatedDeclaration", as: .warning),
            ]
        ),
    ]
)

```

When built with additional command-line flags:

```sh
swift build -Xcc -Wno-error -Xswiftc -no-warnings-as-errors

```

The resulting compiler invocations will include both sets of flags:

```swift
# C compiler invocation
clang ... -Wall -Werror -Wno-error=unused-variable ... -Wno-error ...

# Swift compiler invocation
swiftc ... -warnings-as-errors -Wwarning DeprecatedDeclaration ... -no-warnings-as-errors -Xcc -Wno-error ...

```

Flags are processed from left to right, and since `-no-warnings-as-errors` and `-Wno-error` apply globally to all warnings, they override the warning treating flags defined in the package manifest.

#### Limitations

This approach has a limitation when used with `-suppress-warnings`, which is mutually exclusive with other warning control flags:

```sh
swift build -Xswiftc -suppress-warnings

```

Results in compiler errors:

```swift
error: conflicting options '-warnings-as-errors' and '-suppress-warnings'
error: conflicting options '-Wwarning' and '-suppress-warnings'

```

## Security

This change has no impact on security, safety, or privacy.

## Impact on existing packages

The proposed API will only be available to packages that specify a tools version equal to or later than the SwiftPM version in which this functionality is implemented.

## Alternatives considered

### Disabling a warning via a treat level

Clang allows users to completely disable a specific warning, so for C/C++ settings we could implement that as a new case in the `WarningTreatLevel` enum:

```swift
public enum WarningTreatLevel {
    case warning
    case error
    case ignored
}

```

_(Since Swift doesn't allow selective warning suppression, we would actually have to split the enum into two: `SwiftWarningTreatLevel` and `CFamilyWarningTreatLevel`)_

But some warnings in Clang are disabled by default. If we simply pass `-Wno-error=unused-variable`, the compiler won't actually produce a warning for an unused variable. It only makes sense to use it if we have enabled the warning: `-Wunused-variable -Werror -Wno-error=unused-variable`.

This necessitates separate functions to enable and disable warnings. Therefore, instead of `case ignored`, we propose the functions `enableWarning` and `disableWarning`.

## Future directions

### Package-level settings

It has been noted that warning control settings are often similar across all targets. It makes sense to declare them at the package level while allowing target-level customizations. However, many other settings would also likely benefit from such inheritance, and SwiftPM doesn't currently provide such an option. Therefore, it was decided to factor this improvement out and look at all the settings holistically in the future.

## Acknowledgments

Thank you to [Doug Gregor](https://github.com/douggregor) for the motivation, and to both [Doug Gregor](https://github.com/douggregor) and [Holly Borla](https://github.com/hborla) for their guidance during the implementation of this API.

---

<div class="post-metadata">

**Author:** ![George](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/george/32/37757_2.png) [@George](https://forums.swift.org/u/George)\
**Post date:** [March 19, 2025, 9:01pm UTC](https://forums.swift.org/t/pitch-warning-control-settings-for-swiftpm/78666/2 "2025-03-19T21:01:35Z")

</div>

This would be great!

One small suggestion is to make the warning name a WarningName type that is ExpressibleByStringLiteral. This way, we leave the door open to generate static properties for known error classes which would show up in autocomplete (i.e. .useOfDeprecatedMethod or something like that)

---

<div class="post-metadata">

**Author:** ![KeithBauerANZ](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/keithbaueranz/32/27897_2.png) [@KeithBauerANZ](https://forums.swift.org/u/KeithBauerANZ)\
**Post date:** [March 20, 2025, 1:57am UTC](https://forums.swift.org/t/pitch-warning-control-settings-for-swiftpm/78666/3 "2025-03-20T01:57:42Z")

</div>

"WarningTreatLevel" is very strange English, I think just `WarningTreatment` would make better sense.

I like George's idea of making it strongly typed so that some common constants can be exposed where useful (we could start with "deprecation" for Swift, since I think the primary use-case for this is to treat every warning as an error except for deprecations).

---

<div class="post-metadata">

**Author:** ![George](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/george/32/37757_2.png) [@George](https://forums.swift.org/u/George)\
**Post date:** [March 20, 2025, 1:57pm UTC](https://forums.swift.org/t/pitch-warning-control-settings-for-swiftpm/78666/4 "2025-03-20T13:57:08Z")

</div>

Or even simpler, WarningLevel, since that what these are (the fact that we are “treating” certain warnings as one of these levels is more about how this thing is used rather than what this thing is).

---

<div class="post-metadata">

**Author:** ![hborla](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/hborla/32/27563_2.png) [@hborla](https://forums.swift.org/u/hborla)\
**Post date:** [March 20, 2025, 2:28pm UTC](https://forums.swift.org/t/pitch-warning-control-settings-for-swiftpm/78666/5 "2025-03-20T14:28:01Z")

</div>

I prefer `WarningLevel` or `DiagnosticLevel`, though I'm not sure if the term "diagnostic" is commonly used by programmers or if it's more on the side of compiler jargon.

---

<div class="post-metadata">

**Author:** ![dmt](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/dmt/32/30830_2.png) [@dmt](https://forums.swift.org/u/dmt)\
**Post date:** [March 20, 2025, 3:09pm UTC](https://forums.swift.org/t/pitch-warning-control-settings-for-swiftpm/78666/6 "2025-03-20T15:09:56Z")

</div>

Okay, I noted the enum's name. I'll rename it before publishing the proposal.

> [@George](#):
>
> One small suggestion is to make the warning name a WarningName type that is ExpressibleByStringLiteral.

Interesting idea. But then we would have to create at least two such types - for Swift and for Clang. And most likely we don't want to provide a list of existing diagnostic groups in SwiftPM. For example, we don't do this for `enableExperimentalFeature` and `enableUpcomingFeature`. Also maintaining such a list for Clang warnings would be completely outside the scope of SwiftPM's responsibility.

---

<div class="post-metadata">

**Author:** ![uliwitness](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/uliwitness/32/6272_2.png) [@uliwitness](https://forums.swift.org/u/uliwitness)\
**Post date:** [April 1, 2025, 6:57pm UTC](https://forums.swift.org/t/pitch-warning-control-settings-for-swiftpm/78666/7 "2025-04-01T18:57:24Z")

</div>

Great proposal! We currently have a Makefile so that our CI can add some flags to our build, but it's always so annoying that Xcode builds without any of these flags, so I don't find out about certain things until I've pushed my changes and CI starts to howl, so this would be very welcome.

Others have pointed out that `TreatLevel` is not really English, but let me +1 changing that.

Apart from that, I always wondered why these were considered "unsafe" flags, and my deduction was always that you can pass file paths that the compiler might pull in (like, I dunno, `/etc/htpasswd`).

So I'm wondering if we should codify that warning names should be limited to alphanumeric characters and dashes, or at least _must not_ contain spaces, to ensure people can't pass `"unused -framework /tmp/myvirus.framework"` as a warning name or whatever.

---

<div class="post-metadata">

**Author:** ![dmt](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/dmt/32/30830_2.png) [@dmt](https://forums.swift.org/u/dmt)\
**Post date:** [April 2, 2025, 1:59am UTC](https://forums.swift.org/t/pitch-warning-control-settings-for-swiftpm/78666/8 "2025-04-02T01:59:09Z")

</div>

> [@uliwitness](#):
>
> Others have pointed out that `TreatLevel` is not really English, but let me +1 changing that.

Yep, I've already changed that. The updated version is available on [GitHub](https://github.com/DmT021/swift-evolution/blob/wp/swiftpm-warning-controls/proposals/NNNN-swiftpm-warning-control.md).  
I also removed the "name" label in the new functions' parameters.

> [@uliwitness](#):
>
> _must not_ contain spaces, to ensure people can't pass `"unused -framework /tmp/myvirus.framework"` as a warning name or whatever.

I think it's okay as it is. Spaces are not argument separators in this case. The entire string will be passed to a compiler call as a single argument. Same as if we had passed it in quotes in shell. And the compilers will emit diagnostics for unknown arguments.

```swift
clang ... '-Wno-error=unused -framework /tmp/myvirus.framework'
error: unknown warning option '-Werror=unused-variable -framework /tmp/myvirus.framework';

swiftc ... -Wwarning 'DeprecatedDeclaration -framework /tmp/myvirus.framework'
<unknown>:0: error: unknown warning group: 'DeprecatedDeclaration -framework /tmp/myvirus.framework'

```

If it didn't work that way, it would be a serious security issue and would allow arbitrary arguments to be passed through the other settings, like `define`.  
To pass arbitrary arguments, you really need `unsafeFlags`, and each argument must be a separate string: `.unsafeFlags(["-framework", "/tmp/myvirus.framework"])`

Although maybe adding some kind of validation would allow us to fail faster, before we pass arguments to the underlying build system. But if we'll be going that alley, I'd rather do it separately from the proposal and take into account all the other build settings.

---

<div class="post-metadata">

**Author:** ![Gabardone](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/gabardone/32/3312_2.png) [@Gabardone](https://forums.swift.org/u/Gabardone)\
**Post date:** [July 19, 2025, 12:00am UTC](https://forums.swift.org/t/pitch-warning-control-settings-for-swiftpm/78666/9 "2025-07-19T00:00:05Z")

</div>

I definitely would appreciate this getting implemented. Although if every set of compiler "safe flags" needs its own proposal this is going to take forever.

But whether it has to happen at this level or Xcode's, I hope the issues with Xcode passing in `-suppress-warnings` and then failing to build get resolved.
