-
[SE-0522] Source-Level Control Over Compiler WarningsSwift 2026. 5. 23. 08:45
안녕하세요. 그린입니다 🍏
이번 포스팅에서는 SE-0522 — 소스 레벨에서의 컴파일러 경고 제어에 대해 정리해보겠습니다 🙋🏻

Intro
- Proposal: SE-0522
- Authors: Artem Chikin, Doug Gregor, Holly Borla
- Review Manager: Tony Allevato
- Status: Accepted
Motivation
SE-0443에서는 커맨드라인 플래그로 컴파일러 경고를 제어할 수 있게 되었습니다.
예를 들어
-Werror DeprecatedDeclaration을 사용하면 Deprecated 경고를 에러로 격상시킬 수 있죠.하지만 이 방식은 모듈 전체에 적용되는 블런트한 수단이에요.
특정 선언에서만 예외를 두고 싶은 경우에는 대응할 방법이 없었습니다.
// 모듈 전체에 -Werror DeprecatedDeclaration 적용 중 func bridgeToLegacySystem() { oldAPI() // 🟥 error: 'oldAPI()' is deprecated [#Deprecated] } // 이 함수만 warning으로 낮추고 싶어도 방법이 없었어요마이그레이션 중인 코드베이스, 레거시 호환성 유지, strict 언어 모드의 점진적 도입 등 현실에서 "딱 이 선언만 예외"가 필요한 순간은 생각보다 많거든요!
Proposed Solution
새로운 선언 어트리뷰트
@diagnose를 도입합니다.어노테이션된 선언의 렉시컬 스코프 내에서 특정 diagnostic group의 경고 동작을
error,warning,ignored세 가지 중 하나로 제어할 수 있어요.기본 문법
@diagnose(group-identifier, as: error | warning | ignored) @diagnose(group-identifier, as: behavior, reason: "이유")동작 예시
// 모듈 전체: -Werror DeprecatedDeclaration 적용 중 func normalFunction() { oldAPI() // 🟥 error } @diagnose(DeprecatedDeclaration, as: warning, reason: "Must maintain compatibility until end of release cycle") func bridgeToLegacySystem() { oldAPI() // 🟨 warning: 'oldAPI()' is deprecated [#Deprecated] }ignored로 지정하면 해당 스코프에서 완전히 경고를 억제할 수 있어요.@diagnose(UnsafeImportedAPI, as: warning) struct LegacyFormatReader { func read(_ data: UnsafeRawPointer, _ count: Int) -> Header { c_read_bundle(data, count) // 🟨 warning } @diagnose(UnsafeImportedAPI, as: ignored, reason: "input is validated upstream") func readTrustedInput(_ data: UnsafeRawPointer, _ count: Int) -> Header { c_read_bundle(data, count) // 진단 없음 } }서브그룹에도 개별적으로 적용이 가능합니다.
// UnsafeImportedOwnership은 UnsafeImportedAPI의 서브그룹 // 모듈 전체: -Werror UnsafeImportedAPI @diagnose(UnsafeImportedOwnership, as: warning) func createSession(_ ctx: OpaquePointer) -> Session { let session = c_create_session(ctx) // 🟨 warning: cannot infer ownership ... [#UnsafeImportedOwnership] c_bind_session(session, nil) // 🟥 error: call to imported function ... [#UnsafeImportedAPI] }
Detailed Design
적용 가능한 선언 종류
@diagnose는 func, struct, class, enum, actor, protocol, extension, init, deinit, subscript, macro, typealias, associatedtype, get/set, willSet/didSet, import, enum case 등 대부분의 선언에 적용할 수 있습니다.@diagnose(DiagGroupID, as: ignored) import bar @diagnose(DiagGroupID, as: ignored, reason: "Proposal Example") func foo() { ... } struct Foo { var property: Int { @diagnose(DiagGroupID, as: ignored) get { ... } @diagnose(DiagGroupID, as: ignored) set { ... } } }여러 @diagnose 어트리뷰트 — 순서가 중요합니다!
동일한 선언에 여러
@diagnose를 적용할 경우 마지막(렉시컬 순서상 가장 아래)에 있는 어트리뷰트가 우선합니다.@diagnose(DiagGroupID, as: error) @diagnose(DiagGroupID, as: warning) // 이 쪽이 최종 적용됨 public func foo() { ... }Swift 어트리뷰트는 일반적으로 순서 무관하지만, @diagnose는 SE-0443의 커맨드라인 플래그 동작과 일관성을 맞추기 위해 순서가 중요합니다!
-suppress-warnings와의 관계
-suppress-warnings플래그가 적용된 상태에서는@diagnose가 완전히 무시됩니다.as: error를 지정해도 에러로 격상되지 않아요.이는 SE-0480에서 SwiftPM이 외부 패키지 의존성 빌드 시 경고 관련 플래그를
-suppress-warnings로 대체하는 방식과의 일관성을 위한 것입니다.매크로와의 상호작용
@diagnose가 적용된 스코프 내에서 매크로가 코드를 생성하면, 그 생성된 코드에도 어트리뷰트의 효과가 적용됩니다.또한 매크로 작성자가 매크로 확장 코드 안에 직접
@diagnose를 생성할 수도 있어요.// 매크로 확장 결과물에 @diagnose가 포함될 수 있어요 @diagnose(DeprecatedDeclaration, as: error) public func endpoint() -> UserProvidedType { // 🟥 error: 'UserProvidedType' is deprecated [#DeprecatedDeclaration] }단, 어트리뷰트가 적용된 선언에 peer 매크로가 붙어 있는 경우,
@diagnose의 효과는 peer로 생성된 독립 선언에는 전파되지 않습니다.
Source Compatibility / ABI
- 이번 변경은 순수 additive 변경으로 기존 소스 호환성에 영향이 없습니다.
- ABI에도 영향이 없으며, 텍스트 모듈 인터페이스에도 방출되지 않습니다.
- 배포 제약 없이 자유롭게 도입하거나 제거할 수 있습니다.
Alternatives Considered
Region-based #pragma 스타일 지시문
Clang 등 C계열 컴파일러처럼 임의 코드 영역에
#pragma clang diagnostic push/pop을 사용하는 방식도 고려되었어요.하지만 Swift는 메타데이터와 동작을 선언에 귀속시키는 방식을 선호하고, 영역의 끝을 수동으로 관리해야 한다는 점이 복잡한 버그로 이어질 수 있어 채택되지 않았습니다.
매크로 생성 코드에서 @diagnose 금지하기
모든 경고 제어가 개발자 소스 코드에서 명시적으로 보이도록 매크로 확장에서
@diagnose생성을 금지하는 방법도 고려되었지만, 매크로 작성자가 자신이 생성하는 코드에 대한 진단 정책을 표현할 수 없게 되므로 허용하는 방향으로 결정되었습니다.-suppress-warnings 하에서도 as: error 적용하기
무조건적인 소스 레벨 에러 격상을 위해
-suppress-warnings하에서도as: error를 적용하는 방법도 고려되었지만, SE-0443 및 SE-0480과의 일관성 유지를 위해 채택되지 않았습니다.
Future Directions
- 로컬 렉시컬 스코프 제어 —
do {}블록처럼 선언이 아닌 임의 스코프에도 적용하는 방향 - 클로저 표현식 — 특정 클로저 body의 경고 동작 제어
- 파일 스코프 제어 —
using @diagnose(...)형태의 파일 범위 제어 - 서드파티 툴 통합 —
@diagnose(rule, from: SwiftLint, as: ignored)같은 형태로 린터 등과의 통합
Conclusion
커맨드라인 플래그로만 가능했던 경고 제어를 소스 레벨의 선언 단위로 세밀하게 다룰 수 있게 해주는 실용적인 변경입니다 🙌
마이그레이션 중인 코드베이스, 레거시 호환성 유지, strict 언어 모드의 점진적 도입 등 현실적인 시나리오에서 개발자가 예외의 의도를 코드에 명확하게 문서화할 수 있게 됩니다.
Swift가 더 엄격한 언어 모드와 정적 분석을 강화해가는 흐름 속에서,
@diagnose는 그 여정을 훨씬 유연하게 해줄 도구라고 생각합니다 😄
References
swift-evolution/proposals/0522-source-warning-control.md at main · swiftlang/swift-evolution
This maintains proposals for changes and user-visible enhancements to the Swift Programming Language. - swiftlang/swift-evolution
github.com
'Swift' 카테고리의 다른 글
[SE-0523] Hashable conformance for UnownedTaskExecutor (1) 2026.05.15 [SE-0524] Add withTemporaryAllocation using Output(Raw)Span (1) 2026.05.09 [SE-0521] Improved Syntax for Optionals of Opaque and Existential Types (0) 2026.05.01 [SE-0520] Discardable result use in Task initializers (0) 2026.04.25 [SE-0519] Borrow and Inout types for safe, first-class references (2) 2026.04.11