diff --git a/include/swift/AST/ASTBridging.h b/include/swift/AST/ASTBridging.h index 9fde6d693a6f0..dbf13a1fd5efe 100644 --- a/include/swift/AST/ASTBridging.h +++ b/include/swift/AST/ASTBridging.h @@ -379,7 +379,7 @@ struct BridgedPatternBindingEntry { // NOTE: This must be the same underlying value as C++ 'swift::DiagID' defined // in 'DiagnosticList.cpp'. enum ENUM_EXTENSIBILITY_ATTR(open) BridgedDiagID : uint32_t { -#define DIAG(KIND, ID, Options, Text, Signature) BridgedDiagID_##ID, +#define DIAG(KIND, ID, Group, Options, Text, Signature) BridgedDiagID_##ID, #include "swift/AST/DiagnosticsAll.def" }; diff --git a/include/swift/AST/DefineDiagnosticGroupsMacros.h b/include/swift/AST/DefineDiagnosticGroupsMacros.h new file mode 100644 index 0000000000000..cc778beb9a3c0 --- /dev/null +++ b/include/swift/AST/DefineDiagnosticGroupsMacros.h @@ -0,0 +1,67 @@ +//===--- DefineDiagnosticGroupsMacros.def -----------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file defines macros defining diagnostic groups. +// +//===----------------------------------------------------------------------===// + +// Define macros +#if defined(DEFINE_DIAGNOSTIC_GROUPS_MACROS) && \ + !defined(UNDEFINE_DIAGNOSTIC_GROUPS_MACROS) + +#undef DEFINE_DIAGNOSTIC_GROUPS_MACROS + +#if !(defined(GROUP) || defined(GROUP_LINK)) +#error No reqired macros defined. Define at least one of the following macros: GROUP(Name, DocsFile), GROUP_LINK(Parent, Child) +#endif + +// GROUP macro: +// Declares a diagnostic group. +// Parameters: +// Name - group name as it appears in DiagGroupID enum and DiagGroupInfo.name +// DocsFile - file with a human readable description for the group located in +// userdocs/diagnostic_groups +#ifndef GROUP +#define GROUP(Name, DocsFile) +#endif + +// GROUP_LINK macro: +// Establishes an edge in the diagnostic group graph between +// a supergroup(Parent) and its subgroup(Child). +// Parameters: +// Parent - parent group name +// Child - child group name +#ifndef GROUP_LINK +#define GROUP_LINK(Parent, Child) +#endif + +// Undefine macros +#elif defined(UNDEFINE_DIAGNOSTIC_GROUPS_MACROS) && \ + !defined(DEFINE_DIAGNOSTIC_GROUPS_MACROS) + +#undef UNDEFINE_DIAGNOSTIC_GROUPS_MACROS + +#ifdef GROUP +#undef GROUP +#else +#error Trying to undefine the diagnostic groups macros, but GROUP macro wasn't defined +#endif + +#ifdef GROUP_LINK +#undef GROUP_LINK +#else +#error Trying to undefine the diagnostic groups macros, but GROUP_LINK macro wasn't defined +#endif + +#else +#error Invalid DefineDiagnosticGroupsMacros.h inclusion +#endif diff --git a/include/swift/AST/DefineDiagnosticMacros.h b/include/swift/AST/DefineDiagnosticMacros.h index 95649ee1b00ec..c71691ef21e1a 100644 --- a/include/swift/AST/DefineDiagnosticMacros.h +++ b/include/swift/AST/DefineDiagnosticMacros.h @@ -17,29 +17,39 @@ // Define macros #ifdef DEFINE_DIAGNOSTIC_MACROS -#if !(defined(DIAG) || (defined(ERROR) && defined(WARNING) && defined(NOTE) && \ - defined(REMARK))) -#error Must define either DIAG or the set {ERROR,WARNING,NOTE,REMARK} +#if !(defined(DIAG) || (defined(GROUPED_ERROR) && defined(GROUPED_WARNING) && \ + defined(NOTE) && defined(REMARK))) +#error Must define either DIAG or the set {GROUPED_ERROR,GROUPED_WARNING,NOTE,REMARK} +#endif + +#ifndef GROUPED_ERROR +#define GROUPED_ERROR(ID, Group, Options, Text, Signature) \ + DIAG(ERROR, ID, Group, Options, Text, Signature) #endif #ifndef ERROR #define ERROR(ID, Options, Text, Signature) \ - DIAG(ERROR, ID, Options, Text, Signature) + GROUPED_ERROR(ID, no_group, Options, Text, Signature) +#endif + +#ifndef GROUPED_WARNING +#define GROUPED_WARNING(ID, Group, Options, Text, Signature) \ + DIAG(WARNING, ID, Group, Options, Text, Signature) #endif #ifndef WARNING #define WARNING(ID, Options, Text, Signature) \ - DIAG(WARNING, ID, Options, Text, Signature) + GROUPED_WARNING(ID, no_group, Options, Text, Signature) #endif #ifndef NOTE #define NOTE(ID, Options, Text, Signature) \ - DIAG(NOTE, ID, Options, Text, Signature) + DIAG(NOTE, ID, no_group, Options, Text, Signature) #endif #ifndef REMARK #define REMARK(ID, Options, Text, Signature) \ - DIAG(REMARK, ID, Options, Text, Signature) + DIAG(REMARK, ID, no_group, Options, Text, Signature) #endif #ifndef FIXIT @@ -61,7 +71,9 @@ #undef REMARK #undef NOTE #undef WARNING +#undef GROUPED_WARNING #undef ERROR +#undef GROUPED_ERROR #undef FIXIT #endif diff --git a/include/swift/AST/DiagnosticEngine.h b/include/swift/AST/DiagnosticEngine.h index 980e50bd4226a..5e0fae681c620 100644 --- a/include/swift/AST/DiagnosticEngine.h +++ b/include/swift/AST/DiagnosticEngine.h @@ -24,6 +24,7 @@ #include "swift/AST/TypeLoc.h" #include "swift/Basic/Statistic.h" #include "swift/Basic/Version.h" +#include "swift/Basic/WarningAsErrorRule.h" #include "swift/Localization/LocalizationFormat.h" #include "llvm/ADT/BitVector.h" #include "llvm/ADT/StringRef.h" @@ -851,8 +852,8 @@ namespace swift { /// Don't emit any remarks bool suppressRemarks = false; - /// Emit all warnings as errors - bool warningsAsErrors = false; + /// Treat these warnings as errors. Indicies here corespond to DiagID enum + llvm::BitVector warningsAsErrors; /// Whether a fatal error has occurred bool fatalErrorOccurred = false; @@ -893,9 +894,22 @@ namespace swift { void setSuppressRemarks(bool val) { suppressRemarks = val; } bool getSuppressRemarks() const { return suppressRemarks; } - /// Whether to treat warnings as errors - void setWarningsAsErrors(bool val) { warningsAsErrors = val; } - bool getWarningsAsErrors() const { return warningsAsErrors; } + /// Whether a warning should be upgraded to an error or not + void setWarningAsErrorForDiagID(DiagID id, bool value) { + warningsAsErrors[(unsigned)id] = value; + } + bool getWarningAsErrorForDiagID(DiagID id) { + return warningsAsErrors[(unsigned)id]; + } + + /// Whether all warnings should be upgraded to errors or not + void setAllWarningsAsErrors(bool value) { + if (value) { + warningsAsErrors.set(); + } else { + warningsAsErrors.reset(); + } + } void resetHadAnyError() { anyErrorOccurred = false; @@ -1105,11 +1119,13 @@ namespace swift { return state.getSuppressRemarks(); } - /// Whether to treat warnings as errors - void setWarningsAsErrors(bool val) { state.setWarningsAsErrors(val); } - bool getWarningsAsErrors() const { - return state.getWarningsAsErrors(); - } + /// Apply rules specifing what warnings should or shouldn't be treated as + /// errors. For group rules the string is either a group name defined by + /// DiagnosticGroups.def + /// Rules are applied in order they appear in the vector. + /// In case the vector contains rules affecting the same diagnostic ID + /// the last rule wins. + void setWarningsAsErrorsRules(const std::vector &rules); /// Whether to print diagnostic names after their messages void setPrintDiagnosticNames(bool val) { diff --git a/include/swift/AST/DiagnosticGroups.def b/include/swift/AST/DiagnosticGroups.def new file mode 100644 index 0000000000000..23f618e51bd4c --- /dev/null +++ b/include/swift/AST/DiagnosticGroups.def @@ -0,0 +1,33 @@ +//===--- DiagnosticGroups.def - Diagnostic Groups ---------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file defines diagnostic groups and links between them. +// +//===----------------------------------------------------------------------===// + +#define DEFINE_DIAGNOSTIC_GROUPS_MACROS +#include "swift/AST/DefineDiagnosticGroupsMacros.h" + +// GROUP(Name, DocsFile) +// GROUP_LINK(Parent, Child) + +GROUP(no_group, "") + +GROUP(deprecated, "deprecated.md") +GROUP_LINK(deprecated, availability_deprecated) + +GROUP(availability_deprecated, "availability_deprecated.md") + +GROUP(unknown_warning_group, "unknown_warning_group.md") + +#define UNDEFINE_DIAGNOSTIC_GROUPS_MACROS +#include "swift/AST/DefineDiagnosticGroupsMacros.h" diff --git a/include/swift/AST/DiagnosticGroups.h b/include/swift/AST/DiagnosticGroups.h new file mode 100644 index 0000000000000..744d4911594b0 --- /dev/null +++ b/include/swift/AST/DiagnosticGroups.h @@ -0,0 +1,59 @@ +//===--- DiagnosticGroups.h - Diagnostic Groups -----------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file defines the diagnostic groups enumaration, group graph +// and auxilary functions. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_DIAGNOSTICGROUPS_H +#define SWIFT_DIAGNOSTICGROUPS_H + +#include "swift/AST/DiagnosticList.h" +#include "llvm/ADT/ArrayRef.h" +#include +#include +#include + +namespace swift { + +enum class DiagGroupID : uint16_t { +#define GROUP(Name, Version) Name, +#include "swift/AST/DiagnosticGroups.def" +}; + +constexpr const auto DiagGroupsCount = [] { + size_t count = 0; +#define GROUP(Name, Version) count++; +#include "DiagnosticGroups.def" + return count; +}(); + +struct DiagGroupInfo { + DiagGroupID id; + std::string_view name; + std::string_view version; + llvm::ArrayRef supergroups; + llvm::ArrayRef subgroups; + llvm::ArrayRef diagnostics; + + void traverseDepthFirst( + llvm::function_ref func) const; +}; + +extern const std::array diagnosticGroupsInfo; +const DiagGroupInfo &getDiagGroupInfoByID(DiagGroupID id); +std::optional getDiagGroupIDByName(std::string_view name); + +} // end namespace swift + +#endif /* SWIFT_DIAGNOSTICGROUPS_H */ diff --git a/include/swift/AST/DiagnosticList.h b/include/swift/AST/DiagnosticList.h new file mode 100644 index 0000000000000..6f1253fad9799 --- /dev/null +++ b/include/swift/AST/DiagnosticList.h @@ -0,0 +1,43 @@ +//===--- DiagnosticList.h - Diagnostic Definitions --------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file defines all of the diagnostics emitted by Swift. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_DIAGNOSTICLIST_H +#define SWIFT_DIAGNOSTICLIST_H + +#include + +namespace swift { + +/// Enumeration describing all of possible diagnostics. +/// +/// Each of the diagnostics described in Diagnostics.def has an entry in +/// this enumeration type that uniquely identifies it. +enum class DiagID : uint32_t { +#define DIAG(KIND, ID, Group, Options, Text, Signature) ID, +#include "swift/AST/DiagnosticsAll.def" +}; +static_assert(static_cast(swift::DiagID::invalid_diagnostic) == 0, + "0 is not the invalid diagnostic ID"); + +enum class FixItID : uint32_t { +#define DIAG(KIND, ID, Group, Options, Text, Signature) +#define FIXIT(ID, Text, Signature) ID, +#include "swift/AST/DiagnosticsAll.def" +}; + +} // end namespace swift + +#endif /* SWIFT_DIAGNOSTICLIST_H */ diff --git a/include/swift/AST/DiagnosticsClangImporter.h b/include/swift/AST/DiagnosticsClangImporter.h index 0ff12e43d4f25..e1e867de76e19 100644 --- a/include/swift/AST/DiagnosticsClangImporter.h +++ b/include/swift/AST/DiagnosticsClangImporter.h @@ -23,8 +23,8 @@ namespace swift { namespace diag { // Declare common diagnostics objects with their appropriate types. -#define DIAG(KIND,ID,Options,Text,Signature) \ - extern detail::DiagWithArguments::type ID; +#define DIAG(KIND, ID, Group, Options, Text, Signature) \ + extern detail::DiagWithArguments::type ID; #include "DiagnosticsClangImporter.def" } } diff --git a/include/swift/AST/DiagnosticsCommon.h b/include/swift/AST/DiagnosticsCommon.h index fe5703c1c562a..80d0c725ffe46 100644 --- a/include/swift/AST/DiagnosticsCommon.h +++ b/include/swift/AST/DiagnosticsCommon.h @@ -56,10 +56,10 @@ namespace swift { using DeclAttribute = const DeclAttribute *; // Declare common diagnostics objects with their appropriate types. -#define DIAG(KIND,ID,Options,Text,Signature) \ - extern detail::DiagWithArguments::type ID; -#define FIXIT(ID, Text, Signature) \ - extern detail::StructuredFixItWithArguments::type ID; +#define DIAG(KIND, ID, Group, Options, Text, Signature) \ + extern detail::DiagWithArguments::type ID; +#define FIXIT(ID, Text, Signature) \ + extern detail::StructuredFixItWithArguments::type ID; #include "DiagnosticsCommon.def" } // end namespace diag } // end namespace swift diff --git a/include/swift/AST/DiagnosticsDriver.h b/include/swift/AST/DiagnosticsDriver.h index f80dd9c1eeb55..67112f6f62d66 100644 --- a/include/swift/AST/DiagnosticsDriver.h +++ b/include/swift/AST/DiagnosticsDriver.h @@ -25,8 +25,8 @@ namespace swift { namespace diag { // Declare common diagnostics objects with their appropriate types. -#define DIAG(KIND,ID,Options,Text,Signature) \ - extern detail::DiagWithArguments::type ID; +#define DIAG(KIND, ID, Group, Options, Text, Signature) \ + extern detail::DiagWithArguments::type ID; #include "DiagnosticsDriver.def" } } diff --git a/include/swift/AST/DiagnosticsFrontend.def b/include/swift/AST/DiagnosticsFrontend.def index 361bd1cddbaf7..24cc3d74cffc7 100644 --- a/include/swift/AST/DiagnosticsFrontend.def +++ b/include/swift/AST/DiagnosticsFrontend.def @@ -89,6 +89,8 @@ ERROR(error_missing_arg_value,none, (StringRef, unsigned)) ERROR(error_unknown_arg,none, "unknown argument: '%0'", (StringRef)) +GROUPED_WARNING(unknown_warning_group, unknown_warning_group, none, + "unknown warning group: '%0'", (StringRef)) ERROR(error_invalid_arg_value,none, "invalid value '%1' in '%0'", (StringRef, StringRef)) ERROR(error_invalid_arg_combination,none, diff --git a/include/swift/AST/DiagnosticsFrontend.h b/include/swift/AST/DiagnosticsFrontend.h index a14d396a48358..1d0e213b5a907 100644 --- a/include/swift/AST/DiagnosticsFrontend.h +++ b/include/swift/AST/DiagnosticsFrontend.h @@ -23,8 +23,8 @@ namespace swift { namespace diag { // Declare common diagnostics objects with their appropriate types. -#define DIAG(KIND,ID,Options,Text,Signature) \ - extern detail::DiagWithArguments::type ID; +#define DIAG(KIND, ID, Group, Options, Text, Signature) \ + extern detail::DiagWithArguments::type ID; #include "DiagnosticsFrontend.def" } } diff --git a/include/swift/AST/DiagnosticsIDE.h b/include/swift/AST/DiagnosticsIDE.h index 293e7aac16864..c793d64bcc9d6 100644 --- a/include/swift/AST/DiagnosticsIDE.h +++ b/include/swift/AST/DiagnosticsIDE.h @@ -23,7 +23,7 @@ namespace swift { namespace diag { // Declare common diagnostics objects with their appropriate types. -#define DIAG(KIND,ID,Options,Text,Signature) \ +#define DIAG(KIND, ID, Group, Options, Text, Signature) \ extern detail::DiagWithArguments::type ID; #include "DiagnosticsIDE.def" } diff --git a/include/swift/AST/DiagnosticsIRGen.h b/include/swift/AST/DiagnosticsIRGen.h index fb6901fe55030..e1adc83d22f5c 100644 --- a/include/swift/AST/DiagnosticsIRGen.h +++ b/include/swift/AST/DiagnosticsIRGen.h @@ -23,8 +23,8 @@ namespace swift { namespace diag { // Declare common diagnostics objects with their appropriate types. -#define DIAG(KIND,ID,Options,Text,Signature) \ - extern detail::DiagWithArguments::type ID; +#define DIAG(KIND, ID, Group, Options, Text, Signature) \ + extern detail::DiagWithArguments::type ID; #include "DiagnosticsIRGen.def" } } diff --git a/include/swift/AST/DiagnosticsModuleDiffer.h b/include/swift/AST/DiagnosticsModuleDiffer.h index 24161941b02bf..f2f1de186ed9b 100644 --- a/include/swift/AST/DiagnosticsModuleDiffer.h +++ b/include/swift/AST/DiagnosticsModuleDiffer.h @@ -23,8 +23,8 @@ namespace swift { namespace diag { // Declare common diagnostics objects with their appropriate types. -#define DIAG(KIND,ID,Options,Text,Signature) \ - extern detail::DiagWithArguments::type ID; +#define DIAG(KIND, ID, Group, Options, Text, Signature) \ + extern detail::DiagWithArguments::type ID; #include "DiagnosticsModuleDiffer.def" } } diff --git a/include/swift/AST/DiagnosticsParse.h b/include/swift/AST/DiagnosticsParse.h index cebbcf3c943b9..b156b98d0bb1e 100644 --- a/include/swift/AST/DiagnosticsParse.h +++ b/include/swift/AST/DiagnosticsParse.h @@ -23,10 +23,10 @@ namespace swift { namespace diag { // Declare common diagnostics objects with their appropriate types. -#define DIAG(KIND,ID,Options,Text,Signature) \ - extern detail::DiagWithArguments::type ID; -#define FIXIT(ID,Text,Signature) \ - extern detail::StructuredFixItWithArguments::type ID; +#define DIAG(KIND, ID, Group, Options, Text, Signature) \ + extern detail::DiagWithArguments::type ID; +#define FIXIT(ID, Text, Signature) \ + extern detail::StructuredFixItWithArguments::type ID; #include "DiagnosticsParse.def" } } diff --git a/include/swift/AST/DiagnosticsRefactoring.h b/include/swift/AST/DiagnosticsRefactoring.h index 320c3989e31bc..3b54d574607d6 100644 --- a/include/swift/AST/DiagnosticsRefactoring.h +++ b/include/swift/AST/DiagnosticsRefactoring.h @@ -23,8 +23,8 @@ namespace swift { namespace diag { // Declare common diagnostics objects with their appropriate types. -#define DIAG(KIND,ID,Options,Text,Signature) \ - extern detail::DiagWithArguments::type ID; +#define DIAG(KIND, ID, Group, Options, Text, Signature) \ + extern detail::DiagWithArguments::type ID; #include "DiagnosticsRefactoring.def" } } diff --git a/include/swift/AST/DiagnosticsSIL.h b/include/swift/AST/DiagnosticsSIL.h index 177fa476d7eb8..c677b1c502d53 100644 --- a/include/swift/AST/DiagnosticsSIL.h +++ b/include/swift/AST/DiagnosticsSIL.h @@ -23,8 +23,8 @@ namespace swift { namespace diag { // Declare common diagnostics objects with their appropriate types. -#define DIAG(KIND,ID,Options,Text,Signature) \ - extern detail::DiagWithArguments::type ID; +#define DIAG(KIND, ID, Group, Options, Text, Signature) \ + extern detail::DiagWithArguments::type ID; #include "DiagnosticsSIL.def" } } diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 020338693de10..f880bfebc0ebc 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -6639,19 +6639,20 @@ NOTE(availability_obsoleted, none, "%0 was obsoleted in %1 %2", (const ValueDecl *, StringRef, llvm::VersionTuple)) -WARNING(availability_deprecated, Deprecation, - "%0 %select{is|%select{is|was}3}1 " - "deprecated%select{| in %2%select{| %4}3}1%select{|: %5}5", - (const ValueDecl *, bool, StringRef, bool, llvm::VersionTuple, - StringRef)) - -WARNING(availability_deprecated_rename, Deprecation, - "%0 %select{is|%select{is|was}3}1 " - "deprecated%select{| in %2%select{| %4}3}1: " - "%select{renamed to|replaced by}5%" REPLACEMENT_DECL_KIND_SELECT "6 " - "'%7'", - (const ValueDecl *, bool, StringRef, bool, llvm::VersionTuple, bool, - unsigned, StringRef)) +GROUPED_WARNING(availability_deprecated, availability_deprecated, Deprecation, + "%0 %select{is|%select{is|was}3}1 " + "deprecated%select{| in %2%select{| %4}3}1%select{|: %5}5", + (const ValueDecl *, bool, StringRef, bool, llvm::VersionTuple, + StringRef)) + +GROUPED_WARNING( + availability_deprecated_rename, availability_deprecated, Deprecation, + "%0 %select{is|%select{is|was}3}1 " + "deprecated%select{| in %2%select{| %4}3}1: " + "%select{renamed to|replaced by}5%" REPLACEMENT_DECL_KIND_SELECT "6 " + "'%7'", + (const ValueDecl *, bool, StringRef, bool, llvm::VersionTuple, bool, + unsigned, StringRef)) #undef REPLACEMENT_DECL_KIND_SELECT NOTE(note_deprecated_rename, none, diff --git a/include/swift/AST/DiagnosticsSema.h b/include/swift/AST/DiagnosticsSema.h index b00d80452c37b..90c9ac89107a5 100644 --- a/include/swift/AST/DiagnosticsSema.h +++ b/include/swift/AST/DiagnosticsSema.h @@ -33,10 +33,10 @@ namespace swift { }; // Declare common diagnostics objects with their appropriate types. -#define DIAG(KIND,ID,Options,Text,Signature) \ - extern detail::DiagWithArguments::type ID; -#define FIXIT(ID,Text,Signature) \ - extern detail::StructuredFixItWithArguments::type ID; +#define DIAG(KIND, ID, Group, Options, Text, Signature) \ + extern detail::DiagWithArguments::type ID; +#define FIXIT(ID, Text, Signature) \ + extern detail::StructuredFixItWithArguments::type ID; #include "DiagnosticsSema.def" } } diff --git a/include/swift/Basic/DiagnosticOptions.h b/include/swift/Basic/DiagnosticOptions.h index 6935af7ebb349..8353cf8c2cd76 100644 --- a/include/swift/Basic/DiagnosticOptions.h +++ b/include/swift/Basic/DiagnosticOptions.h @@ -13,7 +13,9 @@ #ifndef SWIFT_BASIC_DIAGNOSTICOPTIONS_H #define SWIFT_BASIC_DIAGNOSTICOPTIONS_H +#include "swift/Basic/WarningAsErrorRule.h" #include "llvm/ADT/Hashing.h" +#include namespace swift { @@ -58,8 +60,8 @@ class DiagnosticOptions { /// Suppress all remarks bool SuppressRemarks = false; - /// Treat all warnings as errors - bool WarningsAsErrors = false; + /// Rules for escalating warnings to errors + std::vector WarningsAsErrorsRules; /// When printing diagnostics, include the diagnostic name (diag::whatever) at /// the end. diff --git a/include/swift/Basic/WarningAsErrorRule.h b/include/swift/Basic/WarningAsErrorRule.h new file mode 100644 index 0000000000000..f0da7daca5b7b --- /dev/null +++ b/include/swift/Basic/WarningAsErrorRule.h @@ -0,0 +1,75 @@ +//===--- WarningAsErrorRule.h -----------------------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_BASIC_WARNINGASERRORRULE_H +#define SWIFT_BASIC_WARNINGASERRORRULE_H + +#include "llvm/Support/ErrorHandling.h" +#include +#include +#include + +namespace swift { + +/// Describes a rule how to treat a warning or all warnings. +class WarningAsErrorRule { +public: + enum class Action { Disable, Enable }; + struct TargetAll {}; + struct TargetGroup { + std::string name; + }; + using Target = std::variant; + + /// Init as a rule targeting all diagnostic groups + WarningAsErrorRule(Action action) : action(action), target(TargetAll()) {} + /// Init as a rule targeting a specific diagnostic group + WarningAsErrorRule(Action action, const std::string &group) + : action(action), target(TargetGroup{group}) {} + + Action getAction() const { return action; } + + Target getTarget() const { return target; } + + static bool hasConflictsWithSuppressWarnings( + const std::vector &rules) { + bool warningsAsErrorsAllEnabled = false; + for (const auto &rule : rules) { + const auto target = rule.getTarget(); + if (std::holds_alternative(target)) { + // Only `-warnings-as-errors` conflicts with `-suppress-warnings` + switch (rule.getAction()) { + case WarningAsErrorRule::Action::Enable: + warningsAsErrorsAllEnabled = true; + break; + case WarningAsErrorRule::Action::Disable: + warningsAsErrorsAllEnabled = false; + break; + } + } else if (std::holds_alternative(target)) { + // Both `-Wwarning` and `-Werror` conflict with `-suppress-warnings` + return true; + } else { + llvm_unreachable("unhandled WarningAsErrorRule::Target"); + } + } + return warningsAsErrorsAllEnabled; + } + +private: + Action action; + Target target; +}; + +} // end namespace swift + +#endif // SWIFT_BASIC_WARNINGASERRORRULE_H \ No newline at end of file diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index f8402a336d91b..7c46e40ce0200 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -799,14 +799,30 @@ def suppress_warnings : Flag<["-"], "suppress-warnings">, Flags<[FrontendOption]>, HelpText<"Suppress all warnings">; +def warning_treating_Group : OptionGroup<"">; + def warnings_as_errors : Flag<["-"], "warnings-as-errors">, + Group, Flags<[FrontendOption]>, HelpText<"Treat warnings as errors">; +def Werror : Separate<["-"], "Werror">, + Group, + Flags<[FrontendOption, HelpHidden]>, + MetaVarName<"">, + HelpText<"Treat this warning group as error">; + def no_warnings_as_errors : Flag<["-"], "no-warnings-as-errors">, + Group, Flags<[FrontendOption]>, - HelpText<"Don't treat warnings as errors">; - + HelpText<"Treat warnings as warnings">; + +def Wwarning : Separate<["-"], "Wwarning">, + Group, + Flags<[FrontendOption, HelpHidden]>, + MetaVarName<"">, + HelpText<"Treat this warning group as warning">; + def suppress_remarks : Flag<["-"], "suppress-remarks">, Flags<[FrontendOption]>, HelpText<"Suppress all remarks">; diff --git a/lib/APIDigester/ModuleDiagsConsumer.cpp b/lib/APIDigester/ModuleDiagsConsumer.cpp index f895eea7ab95a..57d33cadc31d5 100644 --- a/lib/APIDigester/ModuleDiagsConsumer.cpp +++ b/lib/APIDigester/ModuleDiagsConsumer.cpp @@ -26,7 +26,7 @@ namespace { // Reproduce the DiagIDs, as we want both the size and access to the raw ids // themselves. enum LocalDiagID : uint32_t { -#define DIAG(KIND, ID, Options, Text, Signature) ID, +#define DIAG(KIND, ID, Group, Options, Text, Signature) ID, #include "swift/AST/DiagnosticsAll.def" NumDiags }; @@ -90,9 +90,9 @@ ModuleDifferDiagsConsumer::ModuleDifferDiagsConsumer(bool DiagnoseModuleDiff, llvm::raw_ostream &OS): PrintingDiagnosticConsumer(OS), OS(OS), DiagnoseModuleDiff(DiagnoseModuleDiff) { -#define DIAG(KIND, ID, Options, Text, Signature) \ - auto ID = getCategoryName(LocalDiagID::ID); \ - assert(!ID.empty()); \ +#define DIAG(KIND, ID, Group, Options, Text, Signature) \ + auto ID = getCategoryName(LocalDiagID::ID); \ + assert(!ID.empty()); \ AllDiags[ID] = std::set(); #include "swift/AST/DiagnosticsModuleDiffer.def" } diff --git a/lib/AST/CMakeLists.txt b/lib/AST/CMakeLists.txt index d4291c6b9edc4..39b0827642009 100644 --- a/lib/AST/CMakeLists.txt +++ b/lib/AST/CMakeLists.txt @@ -41,6 +41,7 @@ add_swift_host_library(swiftAST STATIC DiagnosticBridge.cpp DiagnosticConsumer.cpp DiagnosticEngine.cpp + DiagnosticGroups.cpp DiagnosticList.cpp DistributedDecl.cpp DocComment.cpp diff --git a/lib/AST/DiagnosticEngine.cpp b/lib/AST/DiagnosticEngine.cpp index 16dffd5d02db5..f00ecc57bab4f 100644 --- a/lib/AST/DiagnosticEngine.cpp +++ b/lib/AST/DiagnosticEngine.cpp @@ -19,6 +19,7 @@ #include "swift/AST/ASTContext.h" #include "swift/AST/ASTPrinter.h" #include "swift/AST/Decl.h" +#include "swift/AST/DiagnosticGroups.h" #include "swift/AST/DiagnosticSuppression.h" #include "swift/AST/DiagnosticsCommon.h" #include "swift/AST/Expr.h" @@ -78,26 +79,29 @@ struct StoredDiagnosticInfo { bool isAPIDigesterBreakage : 1; bool isDeprecation : 1; bool isNoUsage : 1; + DiagGroupID groupID; constexpr StoredDiagnosticInfo(DiagnosticKind k, bool firstBadToken, bool fatal, bool isAPIDigesterBreakage, - bool deprecation, bool noUsage) + bool deprecation, bool noUsage, + DiagGroupID groupID) : kind(k), pointsToFirstBadToken(firstBadToken), isFatal(fatal), - isAPIDigesterBreakage(isAPIDigesterBreakage), isDeprecation(deprecation), - isNoUsage(noUsage) {} - constexpr StoredDiagnosticInfo(DiagnosticKind k, DiagnosticOptions opts) + isAPIDigesterBreakage(isAPIDigesterBreakage), + isDeprecation(deprecation), isNoUsage(noUsage), groupID(groupID) {} + constexpr StoredDiagnosticInfo(DiagnosticKind k, DiagnosticOptions opts, + DiagGroupID groupID) : StoredDiagnosticInfo(k, opts == DiagnosticOptions::PointsToFirstBadToken, opts == DiagnosticOptions::Fatal, opts == DiagnosticOptions::APIDigesterBreakage, opts == DiagnosticOptions::Deprecation, - opts == DiagnosticOptions::NoUsage) {} + opts == DiagnosticOptions::NoUsage, groupID) {} }; // Reproduce the DiagIDs, as we want both the size and access to the raw ids // themselves. enum LocalDiagID : uint32_t { -#define DIAG(KIND, ID, Options, Text, Signature) ID, +#define DIAG(KIND, ID, Group, Options, Text, Signature) ID, #include "swift/AST/DiagnosticsAll.def" NumDiags }; @@ -105,14 +109,18 @@ enum LocalDiagID : uint32_t { // TODO: categorization static const constexpr StoredDiagnosticInfo storedDiagnosticInfos[] = { -#define ERROR(ID, Options, Text, Signature) \ - StoredDiagnosticInfo(DiagnosticKind::Error, DiagnosticOptions::Options), -#define WARNING(ID, Options, Text, Signature) \ - StoredDiagnosticInfo(DiagnosticKind::Warning, DiagnosticOptions::Options), +#define GROUPED_ERROR(ID, Group, Options, Text, Signature) \ + StoredDiagnosticInfo(DiagnosticKind::Error, DiagnosticOptions::Options, \ + DiagGroupID::Group), +#define GROUPED_WARNING(ID, Group, Options, Text, Signature) \ + StoredDiagnosticInfo(DiagnosticKind::Warning, DiagnosticOptions::Options, \ + DiagGroupID::Group), #define NOTE(ID, Options, Text, Signature) \ - StoredDiagnosticInfo(DiagnosticKind::Note, DiagnosticOptions::Options), + StoredDiagnosticInfo(DiagnosticKind::Note, DiagnosticOptions::Options, \ + DiagGroupID::no_group), #define REMARK(ID, Options, Text, Signature) \ - StoredDiagnosticInfo(DiagnosticKind::Remark, DiagnosticOptions::Options), + StoredDiagnosticInfo(DiagnosticKind::Remark, DiagnosticOptions::Options, \ + DiagGroupID::no_group), #include "swift/AST/DiagnosticsAll.def" }; static_assert(sizeof(storedDiagnosticInfos) / sizeof(StoredDiagnosticInfo) == @@ -120,25 +128,25 @@ static_assert(sizeof(storedDiagnosticInfos) / sizeof(StoredDiagnosticInfo) == "array size mismatch"); static constexpr const char * const diagnosticStrings[] = { -#define DIAG(KIND, ID, Options, Text, Signature) Text, +#define DIAG(KIND, ID, Group, Options, Text, Signature) Text, #include "swift/AST/DiagnosticsAll.def" "", }; static constexpr const char *const debugDiagnosticStrings[] = { -#define DIAG(KIND, ID, Options, Text, Signature) Text " [" #ID "]", +#define DIAG(KIND, ID, Group, Options, Text, Signature) Text " [" #ID "]", #include "swift/AST/DiagnosticsAll.def" "", }; static constexpr const char *const diagnosticIDStrings[] = { -#define DIAG(KIND, ID, Options, Text, Signature) #ID, +#define DIAG(KIND, ID, Group, Options, Text, Signature) #ID, #include "swift/AST/DiagnosticsAll.def" "", }; static constexpr const char *const fixItStrings[] = { -#define DIAG(KIND, ID, Options, Text, Signature) +#define DIAG(KIND, ID, Group, Options, Text, Signature) #define FIXIT(ID, Text, Signature) Text, #include "swift/AST/DiagnosticsAll.def" "", @@ -171,6 +179,8 @@ static constexpr auto educationalNotes = _EducationalNotes.value; DiagnosticState::DiagnosticState() { // Initialize our ignored diagnostics to default ignoredDiagnostics.resize(LocalDiagID::NumDiags); + // Initialize warningsAsErrors to default + warningsAsErrors.resize(LocalDiagID::NumDiags); } static CharSourceRange toCharSourceRange(SourceManager &SM, SourceRange SR) { @@ -509,6 +519,44 @@ bool DiagnosticEngine::finishProcessing() { return hadError; } +void DiagnosticEngine::setWarningsAsErrorsRules( + const std::vector &rules) { + std::vector unknownGroups; + for (const auto &rule : rules) { + bool isEnabled = [&] { + switch (rule.getAction()) { + case WarningAsErrorRule::Action::Enable: + return true; + case WarningAsErrorRule::Action::Disable: + return false; + } + }(); + auto target = rule.getTarget(); + if (auto group = std::get_if(&target)) { + auto name = std::string_view(group->name); + // Validate the group name and set the new behavior for each diagnostic + // associated with the group and all its subgroups. + if (auto groupID = getDiagGroupIDByName(name); + groupID && *groupID != DiagGroupID::no_group) { + getDiagGroupInfoByID(*groupID).traverseDepthFirst([&](auto group) { + for (DiagID diagID : group.diagnostics) { + state.setWarningAsErrorForDiagID(diagID, isEnabled); + } + }); + } else { + unknownGroups.push_back(std::string(name)); + } + } else if (std::holds_alternative(target)) { + state.setAllWarningsAsErrors(isEnabled); + } else { + llvm_unreachable("unhandled WarningAsErrorRule::Target"); + } + } + for (const auto &unknownGroup : unknownGroups) { + diagnose(SourceLoc(), diag::unknown_warning_group, unknownGroup); + } +} + /// Skip forward to one of the given delimiters. /// /// \param Text The text to search through, which will be updated to point @@ -1191,7 +1239,7 @@ DiagnosticBehavior DiagnosticState::determineBehavior(const Diagnostic &diag) { // 4) If the user substituted a different behavior for this behavior, apply // that change if (lvl == DiagnosticBehavior::Warning) { - if (warningsAsErrors) + if (getWarningAsErrorForDiagID(diag.getID())) lvl = DiagnosticBehavior::Error; if (suppressWarnings) lvl = DiagnosticBehavior::Ignore; diff --git a/lib/AST/DiagnosticGroups.cpp b/lib/AST/DiagnosticGroups.cpp new file mode 100644 index 0000000000000..ea58ad4f6b420 --- /dev/null +++ b/lib/AST/DiagnosticGroups.cpp @@ -0,0 +1,225 @@ +//===--- DiagnosticGroups.cpp - Diagnostic Groups ---------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file defines the diagnostic groups enumaration, group graph +// and auxilary functions. +// +//===----------------------------------------------------------------------===// + +#include "swift/AST/DiagnosticGroups.h" +#include + +namespace swift { + +namespace { + +template +struct GroupConnections { + std::array supergroups; + std::array subgroups; + std::array diagnostics; + + constexpr GroupConnections( + const std::array &supergroups, + const std::array &subgroups, + const std::array &diagnostics) + : supergroups(supergroups), subgroups(subgroups), + diagnostics(diagnostics) {} +}; + +// This CTAD is needed in C++17 only. Remove after update to C++20. +template +GroupConnections(const std::array &, + const std::array &, + const std::array &) + -> GroupConnections; + +constexpr const auto diagnosticGroupConnections = [] { + constexpr auto sizes = [] { + std::array supergroupsCount{}; + std::array subgroupsCount{}; + std::array diagnosticsCount{}; + + // Count edges for each diagnostic group +#define GROUP_LINK(Parent, Child) \ + subgroupsCount[(size_t)DiagGroupID::Parent]++; \ + supergroupsCount[(size_t)DiagGroupID::Child]++; +#include "swift/AST/DiagnosticGroups.def" + + // Count attached diagnostic IDs for each diagnostic group +#define DIAG(KIND, ID, Group, Options, Text, Signature) \ + diagnosticsCount[(size_t)DiagGroupID::Group]++; +#include "swift/AST/DiagnosticsAll.def" + + return std::tuple{supergroupsCount, subgroupsCount, diagnosticsCount}; + }(); + constexpr auto supergroupsCount = std::get<0>(sizes); + constexpr auto subgroupsCount = std::get<1>(sizes); + constexpr auto diagnosticsCount = std::get<2>(sizes); + + // Declare all edges +#define GROUP(Name, Version) \ + std::array \ + Name##_supergroups{}; \ + std::array \ + Name##_subgroups{}; \ + std::array \ + Name##_diagnostics{}; \ + [[maybe_unused]] size_t Name##_supergroupsIndex = 0; \ + [[maybe_unused]] size_t Name##_subgroupsIndex = 0; \ + [[maybe_unused]] size_t Name##_diagnosticsIndex = 0; +#include "swift/AST/DiagnosticGroups.def" + + // Bind all groups to each other +#define GROUP_LINK(Parent, Child) \ + Parent##_subgroups[Parent##_subgroupsIndex++] = DiagGroupID::Child; \ + Child##_supergroups[Child##_supergroupsIndex++] = DiagGroupID::Parent; +#include "swift/AST/DiagnosticGroups.def" + + // Bind all diagnostics to their groups +#define DIAG(KIND, ID, Group, Options, Text, Signature) \ + Group##_diagnostics[Group##_diagnosticsIndex++] = DiagID::ID; +#include "swift/AST/DiagnosticsAll.def" + + // Produce the resulting structure with all the edges +#define GROUP(Name, Version) \ + GroupConnections(Name##_supergroups, Name##_subgroups, Name##_diagnostics), + return std::tuple{ +#include "swift/AST/DiagnosticGroups.def" + }; +}(); + +std::unordered_map nameToIDMap{ +#define GROUP(Name, Version) {#Name, DiagGroupID::Name}, +#include "swift/AST/DiagnosticGroups.def" +}; + +void traverseDepthFirst(DiagGroupID id, + std::unordered_set &visited, + llvm::function_ref func) { + if (visited.insert(id).second) { + const auto &info = getDiagGroupInfoByID(id); + func(info); + for (const auto subgroup : info.subgroups) { + traverseDepthFirst(subgroup, visited, func); + } + } +} + +} // end anonymous namespace + +constexpr const std::array diagnosticGroupsInfo{ +#define GROUP(Name, Version) \ + DiagGroupInfo{ \ + DiagGroupID::Name, \ + #Name, \ + #Version, \ + llvm::ArrayRef( \ + std::get<(size_t)DiagGroupID::Name>(diagnosticGroupConnections) \ + .supergroups), \ + llvm::ArrayRef( \ + std::get<(size_t)DiagGroupID::Name>(diagnosticGroupConnections) \ + .subgroups), \ + llvm::ArrayRef( \ + std::get<(size_t)DiagGroupID::Name>(diagnosticGroupConnections) \ + .diagnostics)}, +#include "swift/AST/DiagnosticGroups.def" +}; + +const DiagGroupInfo &getDiagGroupInfoByID(DiagGroupID id) { + return diagnosticGroupsInfo[(size_t)id]; +} + +std::optional getDiagGroupIDByName(std::string_view name) { + auto it = nameToIDMap.find(name); + if (it == nameToIDMap.end()) + return std::nullopt; + return it->second; +} + +void DiagGroupInfo::traverseDepthFirst( + llvm::function_ref func) const { + std::unordered_set visited; + ::swift::traverseDepthFirst(id, visited, func); +} + +namespace validation { + +template +constexpr void unfold(F &&function, std::index_sequence) { + (function(std::integral_constant{}), ...); +} + +template +constexpr void constexpr_for(F &&function) { + unfold(function, std::make_index_sequence()); +} + +template +constexpr bool +hasCycleFromGroup(std::array &visited, + std::array &recursionStack) { + if (!visited[Group]) { + visited[Group] = true; + recursionStack[Group] = true; + constexpr auto subgroups = + std::get(diagnosticGroupConnections).subgroups; + bool isCycleFound = false; + constexpr_for([&](auto i) { + constexpr auto subgroup = (size_t)subgroups[i]; + if (!visited[subgroup] && + hasCycleFromGroup(visited, recursionStack)) + isCycleFound = true; + else if (recursionStack[subgroup]) + isCycleFound = true; + }); + if (isCycleFound) + return true; + } + recursionStack[Group] = false; + return false; +} + +constexpr bool hasCycle() { + std::array recursionStack{}; + std::array visited{}; + bool isCycleFound = false; + constexpr_for([&](auto i) { + if (!visited[i] && hasCycleFromGroup(visited, recursionStack)) + isCycleFound = true; + }); + return isCycleFound; +} + +template +constexpr bool isGroupInSupergroup() { + for (const auto group : + std::get<(size_t)Child>(diagnosticGroupConnections).supergroups) + if (group == Parent) + return true; + return false; +} + +static_assert(!hasCycle(), "Diagnostic groups graph has a cycle!"); +// Sanity check for the "no_group" group +static_assert((uint16_t)DiagGroupID::no_group == 0, "0 isn't no_group"); +static_assert(std::get<0>(diagnosticGroupConnections).supergroups.size() == 0, + "no_group isn't a top-level group"); +static_assert(std::get<0>(diagnosticGroupConnections).subgroups.size() == 0, + "no_group shouldn't have subgroups"); +// Check groups have expected supergroups +static_assert(isGroupInSupergroup()); + +} // end namespace validation + +} // end namespace swift diff --git a/lib/AST/DiagnosticList.cpp b/lib/AST/DiagnosticList.cpp index db4b3fdc49e3a..5373131b8794c 100644 --- a/lib/AST/DiagnosticList.cpp +++ b/lib/AST/DiagnosticList.cpp @@ -14,29 +14,17 @@ // //===----------------------------------------------------------------------===// +#include "swift/AST/DiagnosticList.h" #include "swift/AST/DiagnosticsCommon.h" #include "swift/Basic/Assertions.h" using namespace swift; -enum class swift::DiagID : uint32_t { -#define DIAG(KIND,ID,Options,Text,Signature) ID, -#include "swift/AST/DiagnosticsAll.def" -}; -static_assert(static_cast(swift::DiagID::invalid_diagnostic) == 0, - "0 is not the invalid diagnostic ID"); - -enum class swift::FixItID : uint32_t { -#define DIAG(KIND, ID, Options, Text, Signature) -#define FIXIT(ID, Text, Signature) ID, -#include "swift/AST/DiagnosticsAll.def" -}; - // Define all of the diagnostic objects and initialize them with their // diagnostic IDs. namespace swift { namespace diag { -#define DIAG(KIND,ID,Options,Text,Signature) \ - detail::DiagWithArguments::type ID = { DiagID::ID }; +#define DIAG(KIND, ID, Group, Options, Text, Signature) \ + detail::DiagWithArguments::type ID = {DiagID::ID}; #define FIXIT(ID, Text, Signature) \ detail::StructuredFixItWithArguments::type ID = {FixItID::ID}; #include "swift/AST/DiagnosticsAll.def" diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index ef7059e3ba932..2400f0963c04b 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -158,11 +158,20 @@ static void validateBridgingHeaderArgs(DiagnosticEngine &diags, static void validateWarningControlArgs(DiagnosticEngine &diags, const ArgList &args) { - if (args.hasArg(options::OPT_suppress_warnings) && - args.hasFlag(options::OPT_warnings_as_errors, - options::OPT_no_warnings_as_errors, false)) { - diags.diagnose(SourceLoc(), diag::error_conflicting_options, - "-warnings-as-errors", "-suppress-warnings"); + if (args.hasArg(options::OPT_suppress_warnings)) { + if (args.hasFlag(options::OPT_warnings_as_errors, + options::OPT_no_warnings_as_errors, false)) { + diags.diagnose(SourceLoc(), diag::error_conflicting_options, + "-warnings-as-errors", "-suppress-warnings"); + } + if (args.hasArg(options::OPT_Wwarning)) { + diags.diagnose(SourceLoc(), diag::error_conflicting_options, "-Wwarning", + "-suppress-warnings"); + } + if (args.hasArg(options::OPT_Werror)) { + diags.diagnose(SourceLoc(), diag::error_conflicting_options, "-Werror", + "-suppress-warnings"); + } } } diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index 883cb302e7c8e..49acd6bef0066 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -311,8 +311,7 @@ void ToolChain::addCommonFrontendArgs(const OutputInfo &OI, inputArgs.AddLastArg(arguments, options::OPT_profile_generate); inputArgs.AddLastArg(arguments, options::OPT_profile_use); inputArgs.AddLastArg(arguments, options::OPT_profile_coverage_mapping); - inputArgs.AddAllArgs(arguments, options::OPT_warnings_as_errors, - options::OPT_no_warnings_as_errors); + inputArgs.AddAllArgs(arguments, options::OPT_warning_treating_Group); inputArgs.AddLastArg(arguments, options::OPT_sanitize_EQ); inputArgs.AddLastArg(arguments, options::OPT_sanitize_recover_EQ); inputArgs.AddLastArg(arguments, diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 3cdd621963e85..5e3faa962bdc6 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -2261,9 +2261,24 @@ static bool ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args, Opts.FixitCodeForAllDiagnostics |= Args.hasArg(OPT_fixit_all); Opts.SuppressWarnings |= Args.hasArg(OPT_suppress_warnings); Opts.SuppressRemarks |= Args.hasArg(OPT_suppress_remarks); - Opts.WarningsAsErrors = Args.hasFlag(options::OPT_warnings_as_errors, - options::OPT_no_warnings_as_errors, - false); + for (const Arg *arg : Args.filtered(OPT_warning_treating_Group)) { + Opts.WarningsAsErrorsRules.push_back([&] { + switch (arg->getOption().getID()) { + case OPT_warnings_as_errors: + return WarningAsErrorRule(WarningAsErrorRule::Action::Enable); + case OPT_no_warnings_as_errors: + return WarningAsErrorRule(WarningAsErrorRule::Action::Disable); + case OPT_Werror: + return WarningAsErrorRule(WarningAsErrorRule::Action::Enable, + arg->getValue()); + case OPT_Wwarning: + return WarningAsErrorRule(WarningAsErrorRule::Action::Disable, + arg->getValue()); + default: + llvm_unreachable("unhandled warning as error option"); + } + }()); + } Opts.PrintDiagnosticNames |= Args.hasArg(OPT_debug_diagnostic_names); Opts.PrintEducationalNotes |= Args.hasArg(OPT_print_educational_notes); if (Arg *A = Args.getLastArg(OPT_diagnostic_documentation_path)) { @@ -2306,7 +2321,9 @@ static bool ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args, Opts.LocalizationPath = A->getValue(); } } - assert(!(Opts.WarningsAsErrors && Opts.SuppressWarnings) && + assert(!(Opts.SuppressWarnings && + WarningAsErrorRule::hasConflictsWithSuppressWarnings( + Opts.WarningsAsErrorsRules)) && "conflicting arguments; should have been caught by driver"); return false; diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index 6e846ea00ae6e..7856902a08b53 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -723,9 +723,8 @@ void CompilerInstance::setUpDiagnosticOptions() { if (Invocation.getDiagnosticOptions().SuppressRemarks) { Diagnostics.setSuppressRemarks(true); } - if (Invocation.getDiagnosticOptions().WarningsAsErrors) { - Diagnostics.setWarningsAsErrors(true); - } + Diagnostics.setWarningsAsErrorsRules( + Invocation.getDiagnosticOptions().WarningsAsErrorsRules); if (Invocation.getDiagnosticOptions().PrintDiagnosticNames) { Diagnostics.setPrintDiagnosticNames(true); } diff --git a/lib/Localization/LocalizationFormat.cpp b/lib/Localization/LocalizationFormat.cpp index 24402cb723bd0..52f48b840a221 100644 --- a/lib/Localization/LocalizationFormat.cpp +++ b/lib/Localization/LocalizationFormat.cpp @@ -34,13 +34,13 @@ namespace { enum LocalDiagID : uint32_t { -#define DIAG(KIND, ID, Options, Text, Signature) ID, +#define DIAG(KIND, ID, Group, Options, Text, Signature) ID, #include "swift/AST/DiagnosticsAll.def" NumDiags }; static constexpr const char *const diagnosticNameStrings[] = { -#define DIAG(KIND, ID, Options, Text, Signature) " [" #ID "]", +#define DIAG(KIND, ID, Group, Options, Text, Signature) " [" #ID "]", #include "swift/AST/DiagnosticsAll.def" "", }; @@ -206,7 +206,7 @@ void StringsLocalizationProducer::forEachAvailable( void StringsLocalizationProducer::readStringsFile( llvm::MemoryBuffer *in, std::vector &diagnostics) { std::map diagLocs; -#define DIAG(KIND, ID, Options, Text, Signature) \ +#define DIAG(KIND, ID, Group, Options, Text, Signature) \ diagLocs[#ID] = static_cast(LocalDiagID::ID); #include "swift/AST/DiagnosticsAll.def" #undef DIAG diff --git a/test/Driver/warnings-control.swift b/test/Driver/warnings-control.swift index fbea6e14cc2a9..7883fe942a5db 100644 --- a/test/Driver/warnings-control.swift +++ b/test/Driver/warnings-control.swift @@ -2,8 +2,14 @@ // RUN: not %target-swiftc_driver -warnings-as-errors %s 2>&1 | %FileCheck -check-prefix=WERR %s // RUN: not %target-swiftc_driver -suppress-warnings %s 2>&1 | %FileCheck -check-prefix=NOWARN %s -// RUN: not %target-swiftc_driver -suppress-warnings -warnings-as-errors %s 2>&1 | %FileCheck -check-prefix=FLAGS_CONFLICT %s -// FLAGS_CONFLICT: error: conflicting options '-warnings-as-errors' and '-suppress-warnings' +// RUN: not %target-swiftc_driver -suppress-warnings -warnings-as-errors %s 2>&1 | %FileCheck -check-prefix=FLAGS_CONFLICT_WAE %s +// FLAGS_CONFLICT_WAE: error: conflicting options '-warnings-as-errors' and '-suppress-warnings' + +// RUN: not %target-swiftc_driver -suppress-warnings -Wwarning test %s 2>&1 | %FileCheck -check-prefix=FLAGS_CONFLICT_WW %s +// FLAGS_CONFLICT_WW: error: conflicting options '-Wwarning' and '-suppress-warnings' + +// RUN: not %target-swiftc_driver -suppress-warnings -Werror test %s 2>&1 | %FileCheck -check-prefix=FLAGS_CONFLICT_WE %s +// FLAGS_CONFLICT_WE: error: conflicting options '-Werror' and '-suppress-warnings' func foo() -> Int { let x = 1 diff --git a/test/diagnostics/warnings_as_errors_rules.swift b/test/diagnostics/warnings_as_errors_rules.swift new file mode 100644 index 0000000000000..8a17e4f284bad --- /dev/null +++ b/test/diagnostics/warnings_as_errors_rules.swift @@ -0,0 +1,60 @@ +// RUN: not %target-swift-frontend -typecheck -diagnostic-style llvm -warnings-as-errors %s 2>&1 | %FileCheck %s --check-prefix=CHECK-WAE +// RUN: not %target-swift-frontend -typecheck -diagnostic-style llvm -Werror availability_deprecated %s 2>&1 | %FileCheck %s --check-prefix=CHECK-WE-GROUP +// RUN: not %target-swift-frontend -typecheck -diagnostic-style llvm -Werror deprecated %s 2>&1 | %FileCheck %s --check-prefix=CHECK-WE-SUPERGROUP +// RUN: %target-swift-frontend -typecheck -diagnostic-style llvm -warnings-as-errors -no-warnings-as-errors %s 2>&1 | %FileCheck %s --check-prefix=CHECK-WAE-NWAE +// RUN: %target-swift-frontend -typecheck -diagnostic-style llvm -warnings-as-errors -Wwarning availability_deprecated %s 2>&1 | %FileCheck %s --check-prefix=CHECK-WAE-WW-GROUP +// RUN: %target-swift-frontend -typecheck -diagnostic-style llvm -warnings-as-errors -Wwarning deprecated %s 2>&1 | %FileCheck %s --check-prefix=CHECK-WAE-WW-SUPERGROUP +// RUN: %target-swift-frontend -typecheck -diagnostic-style llvm -Werror deprecated -Wwarning availability_deprecated %s 2>&1 | %FileCheck %s --check-prefix=CHECK-WE-SUPERGROUP-WW-GROUP + +// This test verifies that the warning control flags apply with respect to +// the order they are specified in the cmd line. +// Naming: +// WAE: -warnings-as-errors +// NWAE: -no-warnings-as-errors +// WE-xxxx: -Werror xxxx +// WW-xxxx: -Wwarning xxxx +// GROUP - refers to a narrower group +// SUPERGROUP - refers to a broader group that includes GROUP + + +@available(*, deprecated) +func foo() { +} + +@available(*, deprecated, renamed: "bar2") +func bar() { +} + + +// CHECK-WAE: error: 'foo()' is deprecated +// CHECK-WAE-NOT: warning: 'foo()' is deprecated +// CHECK-WE-GROUP: error: 'foo()' is deprecated +// CHECK-WE-GROUP-NOT: warning: 'foo()' is deprecated +// CHECK-WE-SUPERGROUP: error: 'foo()' is deprecated +// CHECK-WE-SUPERGROUP-NOT: warning: 'foo()' is deprecated +// CHECK-WAE-NWAE: warning: 'foo()' is deprecated +// CHECK-WAE-NWAE-NOT: error: 'foo()' is deprecated +// CHECK-WAE-WW-GROUP: warning: 'foo()' is deprecated +// CHECK-WAE-WW-GROUP-NOT: error: 'foo()' is deprecated +// CHECK-WAE-WW-SUPERGROUP: warning: 'foo()' is deprecated +// CHECK-WAE-WW-SUPERGROUP-NOT: error: 'foo()' is deprecated +// CHECK-WE-SUPERGROUP-WW-GROUP: warning: 'foo()' is deprecated +// CHECK-WE-SUPERGROUP-WW-GROUP-NOT: error: 'foo()' is deprecated +foo() + + +// CHECK-WAE: error: 'bar()' is deprecated: renamed to 'bar2' +// CHECK-WAE-NOT: warning: 'bar()' is deprecated: renamed to 'bar2' +// CHECK-WE-GROUP: error: 'bar()' is deprecated: renamed to 'bar2' +// CHECK-WE-GROUP-NOT: warning: 'bar()' is deprecated: renamed to 'bar2' +// CHECK-WE-SUPERGROUP: error: 'bar()' is deprecated: renamed to 'bar2' +// CHECK-WE-SUPERGROUP-NOT: warning: 'bar()' is deprecated: renamed to 'bar2' +// CHECK-WAE-NWAE: warning: 'bar()' is deprecated: renamed to 'bar2' +// CHECK-WAE-NWAE-NOT: error: 'bar()' is deprecated: renamed to 'bar2' +// CHECK-WAE-WW-GROUP: warning: 'bar()' is deprecated: renamed to 'bar2' +// CHECK-WAE-WW-GROUP-NOT: error: 'bar()' is deprecated: renamed to 'bar2' +// CHECK-WAE-WW-SUPERGROUP: warning: 'bar()' is deprecated: renamed to 'bar2' +// CHECK-WAE-WW-SUPERGROUP-NOT: error: 'bar()' is deprecated: renamed to 'bar2' +// CHECK-WE-SUPERGROUP-WW-GROUP: warning: 'bar()' is deprecated: renamed to 'bar2' +// CHECK-WE-SUPERGROUP-WW-GROUP-NOT: error: 'bar()' is deprecated: renamed to 'bar2' +bar() diff --git a/tools/swift-def-to-strings-converter/swift-def-to-strings-converter.cpp b/tools/swift-def-to-strings-converter/swift-def-to-strings-converter.cpp index 0d745a7d44d23..5bb5bf7ebda07 100644 --- a/tools/swift-def-to-strings-converter/swift-def-to-strings-converter.cpp +++ b/tools/swift-def-to-strings-converter/swift-def-to-strings-converter.cpp @@ -32,17 +32,17 @@ #include static constexpr const char *const diagnosticID[] = { -#define DIAG(KIND, ID, Options, Text, Signature) #ID, +#define DIAG(KIND, ID, Group, Options, Text, Signature) #ID, #include "swift/AST/DiagnosticsAll.def" }; static constexpr const char *const diagnosticMessages[] = { -#define DIAG(KIND, ID, Options, Text, Signature) Text, +#define DIAG(KIND, ID, Group, Options, Text, Signature) Text, #include "swift/AST/DiagnosticsAll.def" }; enum LocalDiagID : uint32_t { -#define DIAG(KIND, ID, Options, Text, Signature) ID, +#define DIAG(KIND, ID, Group, Options, Text, Signature) ID, #include "swift/AST/DiagnosticsAll.def" NumDiags }; diff --git a/unittests/Localization/LocalizationTest.h b/unittests/Localization/LocalizationTest.h index 7ca13314fcdfa..5d74375416df4 100644 --- a/unittests/Localization/LocalizationTest.h +++ b/unittests/Localization/LocalizationTest.h @@ -34,18 +34,18 @@ namespace swift { namespace unittests { enum LocalDiagID : uint32_t { -#define DIAG(KIND, ID, Options, Text, Signature) ID, +#define DIAG(KIND, ID, Group, Options, Text, Signature) ID, #include "swift/AST/DiagnosticsAll.def" NumDiags }; static constexpr const char *const diagnosticID[] = { -#define DIAG(KIND, ID, Options, Text, Signature) #ID, +#define DIAG(KIND, ID, Group, Options, Text, Signature) #ID, #include "swift/AST/DiagnosticsAll.def" }; static constexpr const char *const diagnosticMessages[] = { -#define DIAG(KIND, ID, Options, Text, Signature) Text, +#define DIAG(KIND, ID, Group, Options, Text, Signature) Text, #include "swift/AST/DiagnosticsAll.def" }; diff --git a/userdocs/diagnostic_groups/availability_deprecated.md b/userdocs/diagnostic_groups/availability_deprecated.md new file mode 100644 index 0000000000000..5169ad30567ee --- /dev/null +++ b/userdocs/diagnostic_groups/availability_deprecated.md @@ -0,0 +1,30 @@ +# Availability Deprecation Warnings (`availability_deprecated`) + +This diagnostic group includes warnings related to deprecated APIs that may be removed in future versions and should be replaced with more current alternatives. + +The `availability_deprecated` group covers the following warnings: +- Use of a function annotated with `@available(, deprecated: )` + ```swift + @available(iOS, deprecated: 10.0) + func oldFunction() { + // This function is deprecated and should not be used. + } + + oldFunction() // 'oldFunction()' is deprecated + ``` +- Use of a function annotated with `@available(, deprecated: , renamed: "")` + ```swift + @available(iOS, deprecated: 10.0, renamed: "newFunction") + func oldFunction() { + // This function is deprecated and should not be used. + } + + oldFunction() // 'oldFunction()' is deprecated: renamed to 'newFunction' + ``` + +## Usage Example + +```sh +swiftc -warning-as-error availability_deprecated file.swift +swiftc -warnings-as-errors -no-warning-as-error availability_deprecated file.swift +``` \ No newline at end of file diff --git a/userdocs/diagnostic_groups/deprecated.md b/userdocs/diagnostic_groups/deprecated.md new file mode 100644 index 0000000000000..db9e404ccea35 --- /dev/null +++ b/userdocs/diagnostic_groups/deprecated.md @@ -0,0 +1,12 @@ +# Deprecation Warnings (`deprecated`) + +The deprecated group is a supergroup designed to manage all kinds of warnings related to the use of deprecated elements. This group can include other diagnostic groups with similar meanings. The deprecated group includes the following groups: + +- `availability_deprecated`: Includes warnings for APIs marked as deprecated. + +## Usage Example + +```sh +swiftc -warning-as-error deprecated file.swift +swiftc -warnings-as-errors -no-warning-as-error deprecated file.swift +``` \ No newline at end of file diff --git a/userdocs/diagnostic_groups/unknown_warning_group.md b/userdocs/diagnostic_groups/unknown_warning_group.md new file mode 100644 index 0000000000000..ed42b6f368404 --- /dev/null +++ b/userdocs/diagnostic_groups/unknown_warning_group.md @@ -0,0 +1,15 @@ +# Unknown "Warning Group" Warnings (`unknown_warning_group`) + +The `unknown_warning_group` diagnostic group addresses warnings related to the specification of unrecognized warning groups in compilation flags. + +```sh +swiftc -warning-as-error non_existing_group file.swift +:0: warning: unknown warning group: 'non_existing_group' +``` + +Such warnings are emitted after the behavior for all specified warning groups has been processed, which means their behavior can also be specified. For example: + +```sh +swiftc -warning-as-error unknown_warning_group -warning-as-error non_existing_group file.swift +:0: error: unknown warning group: 'non_existing_group' +```