diff --git a/include/swift/AST/ASTBridging.h b/include/swift/AST/ASTBridging.h index 6e8fdc6cbc8a6..e1948f04b720d 100644 --- a/include/swift/AST/ASTBridging.h +++ b/include/swift/AST/ASTBridging.h @@ -26,12 +26,15 @@ #include "swift/AST/LayoutConstraintKind.h" #include "swift/AST/PlatformKind.h" #include "swift/Basic/BasicBridging.h" +#include "swift/Basic/WarningGroupBehavior.h" #ifdef NOT_COMPILED_WITH_SWIFT_PURE_BRIDGING_MODE #include "swift/AST/Attr.h" #include "swift/AST/Decl.h" #endif +#include + SWIFT_BEGIN_NULLABILITY_ANNOTATIONS namespace llvm { @@ -1104,11 +1107,36 @@ BridgedSwiftNativeObjCRuntimeBaseAttr_createParsed(BridgedASTContext cContext, swift::SourceRange range, swift::Identifier name); +SWIFT_NAME("BridgedWarnAttr.createParsed(_:atLoc:range:diagGroupName:behavior:reason:)") +BridgedWarnAttr +BridgedWarnAttr_createParsed(BridgedASTContext cContext, + swift::SourceLoc atLoc, + swift::SourceRange range, + swift::Identifier diagGroupName, + swift::WarningGroupBehavior behavior, + BridgedStringRef reason); + enum ENUM_EXTENSIBILITY_ATTR(closed) BridgedNonSendableKind { BridgedNonSendableKindSpecific, BridgedNonSendableKindAssumed, }; +SWIFT_NAME("BridgedWarningGroupBehaviorRule.getGroupName(self:)") +BridgedStringRef BridgedWarningGroupBehaviorRule_getGroupName(BridgedWarningGroupBehaviorRule rule); + +SWIFT_NAME("BridgedWarningGroupBehaviorRule.getBehavior(self:)") +swift::WarningGroupBehavior +BridgedWarningGroupBehaviorRule_getBehavior(BridgedWarningGroupBehaviorRule rule); + + +SWIFT_NAME("getDiagnosticGroupLinksCount()") +SwiftInt +BridgedDiagnosticGroupLinks_getCount(); + +SWIFT_NAME("getDiagnosticGroupLink(at:)") +std::pair +BridgedDiagnosticGroupLinks_getLink(SwiftInt index); + SWIFT_NAME("BridgedNonSendableAttr.createParsed(_:atLoc:range:kind:)") BridgedNonSendableAttr BridgedNonSendableAttr_createParsed( BridgedASTContext cContext, swift::SourceLoc atLoc, diff --git a/include/swift/AST/ASTBridgingWrappers.def b/include/swift/AST/ASTBridgingWrappers.def index 19af5d314740c..475bb77b6c486 100644 --- a/include/swift/AST/ASTBridgingWrappers.def +++ b/include/swift/AST/ASTBridgingWrappers.def @@ -114,6 +114,7 @@ AST_BRIDGING_WRAPPER_NONNULL(CustomAttribute) AST_BRIDGING_WRAPPER_NULLABLE(ArgumentList) AST_BRIDGING_WRAPPER_NULLABLE(AvailabilitySpec) AST_BRIDGING_WRAPPER_CONST_NONNULL(AvailabilityMacroMap) +AST_BRIDGING_WRAPPER_NULLABLE(WarningGroupBehaviorRule) AST_BRIDGING_WRAPPER_NONNULL(PoundAvailableInfo) AST_BRIDGING_WRAPPER_NONNULL(LifetimeEntry) diff --git a/include/swift/AST/Attr.h b/include/swift/AST/Attr.h index 9439df3870813..06de88cf69832 100644 --- a/include/swift/AST/Attr.h +++ b/include/swift/AST/Attr.h @@ -23,6 +23,7 @@ #include "swift/AST/AvailabilityRange.h" #include "swift/AST/ConcreteDeclRef.h" #include "swift/AST/DeclNameLoc.h" +#include "swift/AST/DiagnosticGroups.h" #include "swift/AST/Identifier.h" #include "swift/AST/KnownProtocols.h" #include "swift/AST/LifetimeDependence.h" @@ -41,6 +42,7 @@ #include "swift/Basic/SourceLoc.h" #include "swift/Basic/UUID.h" #include "swift/Basic/Version.h" +#include "swift/Basic/WarningGroupBehavior.h" #include "llvm/ADT/DenseMapInfo.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" @@ -3641,6 +3643,38 @@ class NonexhaustiveAttr : public DeclAttribute { } }; +class WarnAttr : public DeclAttribute { +public: + WarnAttr(DiagGroupID DiagnosticGroupID, WarningGroupBehavior Behavior, + std::optional Reason, SourceLoc AtLoc, SourceRange Range, + bool Implicit) + : DeclAttribute(DeclAttrKind::Warn, AtLoc, Range, Implicit), + DiagnosticBehavior(Behavior), DiagnosticGroupID(DiagnosticGroupID), + Reason(Reason) {} + + WarnAttr(DiagGroupID DiagnosticGroupID, WarningGroupBehavior Behavior, bool Implicit) + : WarnAttr(DiagnosticGroupID, Behavior, std::nullopt, SourceLoc(), + SourceRange(), Implicit) {} + + WarningGroupBehavior DiagnosticBehavior; + DiagGroupID DiagnosticGroupID; + const std::optional Reason; + + static bool classof(const DeclAttribute *DA) { + return DA->getKind() == DeclAttrKind::Warn; + } + + WarnAttr *clone(ASTContext &ctx) const { + return new (ctx) WarnAttr(DiagnosticGroupID, DiagnosticBehavior, Reason, + AtLoc, Range, isImplicit()); + } + + bool isEquivalent(const WarnAttr *other, + Decl *attachedTo) const { + return Reason == other->Reason; + } +}; + /// The kind of unary operator, if any. enum class UnaryOperatorKind : uint8_t { None, Prefix, Postfix }; diff --git a/include/swift/AST/DeclAttr.def b/include/swift/AST/DeclAttr.def index 5140bf0117a10..b48af9bbb0d34 100644 --- a/include/swift/AST/DeclAttr.def +++ b/include/swift/AST/DeclAttr.def @@ -902,12 +902,18 @@ DECL_ATTR(specialized, Specialized, AllowMultipleAttributes | LongAttribute | UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, 172) + SIMPLE_DECL_ATTR(_unsafeSelfDependentResult, UnsafeSelfDependentResult, OnAccessor, UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIBreakingToRemove | EquivalentInABIAttr, 173) -LAST_DECL_ATTR(UnsafeSelfDependentResult) +DECL_ATTR(warn, Warn, + OnFunc | OnConstructor | OnDestructor | OnSubscript | OnVar | OnNominalType | OnExtension | OnAccessor | OnImport, + AllowMultipleAttributes | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr, + 174) + +LAST_DECL_ATTR(Warn) #undef DECL_ATTR_ALIAS #undef CONTEXTUAL_DECL_ATTR_ALIAS diff --git a/include/swift/AST/DiagnosticEngine.h b/include/swift/AST/DiagnosticEngine.h index e382ffbaf0de2..6a6ba1c0d8c8d 100644 --- a/include/swift/AST/DiagnosticEngine.h +++ b/include/swift/AST/DiagnosticEngine.h @@ -25,7 +25,7 @@ #include "swift/Basic/PrintDiagnosticNamesMode.h" #include "swift/Basic/Statistic.h" #include "swift/Basic/Version.h" -#include "swift/Basic/WarningAsErrorRule.h" +#include "swift/Basic/WarningGroupBehaviorRule.h" #include "swift/Localization/LocalizationFormat.h" #include "llvm/ADT/BitVector.h" #include "llvm/ADT/StringRef.h" @@ -612,6 +612,10 @@ namespace swift { ArrayRef Args); }; + + using WarningGroupBehaviorMap = + std::unordered_map; + /// Class to track, map, and remap diagnostic severity and fatality /// class DiagnosticState { @@ -628,13 +632,13 @@ namespace swift { /// Don't emit any remarks bool suppressRemarks = false; - /// A mapping from `DiagGroupID` identifiers to Boolean values indicating - /// whether warnings belonging to the respective diagnostic groups should be - /// escalated to errors. - llvm::BitVector warningsAsErrors; - - /// Track which diagnostic group (`DiagGroupID`) warnings should be ignored. - llvm::BitVector ignoredDiagnosticGroups; + /// A mapping from `DiagGroupID` identifiers to `WarningGroupBehaviorRule` + /// values indicating how warnings belonging to the respective diagnostic groups + /// should be emitted. While there is duplication between this data structure + /// being a map keyed with `DiagGroupID` and containing a rule object which also + /// contains a matching `DiagGroupID`, this significantly simplifies bridging + /// values of this map to Swift clients. + WarningGroupBehaviorMap warningGroupBehaviorMap; /// For compiler-internal purposes only, track which diagnostics should /// be ignored completely. For example, this is used by LLDB to @@ -657,7 +661,8 @@ namespace swift { /// Figure out the Behavior for the given diagnostic, taking current /// state such as fatality into account. - DiagnosticBehavior determineBehavior(const Diagnostic &diag) const; + DiagnosticBehavior determineBehavior(const Diagnostic &diag, + SourceManager &sourceMgr) const; /// Updates the diagnostic state for a diagnostic to emit. void updateFor(DiagnosticBehavior behavior); @@ -684,28 +689,30 @@ namespace swift { void setSuppressRemarks(bool val) { suppressRemarks = val; } bool getSuppressRemarks() const { return suppressRemarks; } - /// Sets whether warnings belonging to the diagnostic group identified by - /// `id` should be escalated to errors. - void setWarningsAsErrorsForDiagGroupID(DiagGroupID id, bool value) { - warningsAsErrors[(unsigned)id] = value; + /// Configure the command-line warning group handling + /// rules (`-Werrr`,`-Wwarning`,`-warnings-as-errors`) + void setWarningGroupControlRules( + const llvm::SmallVector &rules); + + /// Add an individual command-line warning group behavior + void addWarningGroupControl(const DiagGroupID &groupID, + WarningGroupBehavior behavior) { + warningGroupBehaviorMap.insert_or_assign( + groupID, WarningGroupBehaviorRule(behavior, groupID)); } - /// Returns a Boolean value indicating whether warnings belonging to the - /// diagnostic group identified by `id` should be escalated to errors. - bool getWarningsAsErrorsForDiagGroupID(DiagGroupID id) const { - return warningsAsErrors[(unsigned)id]; + const WarningGroupBehaviorMap& + getWarningGroupBehaviorControlMap() const { + return warningGroupBehaviorMap; } - /// Whether all warnings should be upgraded to errors or not. - void setAllWarningsAsErrors(bool value) { - // This works as intended because every diagnostic belongs to either a - // custom group or the top-level `DiagGroupID::no_group`, which is also - // a group. - if (value) { - warningsAsErrors.set(); - } else { - warningsAsErrors.reset(); - } + const std::vector + getWarningGroupBehaviorControlRefArray() const { + std::vector ruleRefArray; + ruleRefArray.reserve(warningGroupBehaviorMap.size()); + for (const auto &rule: warningGroupBehaviorMap) + ruleRefArray.push_back(&rule.second); + return ruleRefArray; } void resetHadAnyError() { @@ -713,16 +720,6 @@ namespace swift { fatalErrorOccurred = false; } - /// Set whether a diagnostic group should be ignored. - void setIgnoredDiagnosticGroup(DiagGroupID id, bool ignored) { - ignoredDiagnosticGroups[(unsigned)id] = ignored; - } - - /// Query whether a specific diagnostic group is ignored. - bool isIgnoredDiagnosticGroup(DiagGroupID id) const { - return ignoredDiagnosticGroups[(unsigned)id]; - } - /// Set a specific diagnostic to be ignored by the compiler. void compilerInternalIgnoreDiagnostic(DiagID id) { compilerIgnoredDiagnostics[(unsigned)id] = true; @@ -738,11 +735,10 @@ namespace swift { std::swap(suppressWarnings, other.suppressWarnings); std::swap(suppressNotes, other.suppressNotes); std::swap(suppressRemarks, other.suppressRemarks); - std::swap(warningsAsErrors, other.warningsAsErrors); + std::swap(warningGroupBehaviorMap, other.warningGroupBehaviorMap); std::swap(fatalErrorOccurred, other.fatalErrorOccurred); std::swap(anyErrorOccurred, other.anyErrorOccurred); std::swap(previousBehavior, other.previousBehavior); - std::swap(ignoredDiagnosticGroups, other.ignoredDiagnosticGroups); } private: @@ -752,6 +748,13 @@ namespace swift { DiagnosticState(DiagnosticState &&) = default; DiagnosticState &operator=(DiagnosticState &&) = default; + + /// If this diagnostic is a warning belonging to a diagnostic group, + /// figure out if there is a source-level (`@warn`) control for this group + /// for this diagnostic's source location. + std::optional + determineUserControlledWarningBehavior(const Diagnostic &diag, + SourceManager &sourceMgr) const; }; /// A lightweight reference to a diagnostic that's been fully applied to @@ -951,7 +954,20 @@ namespace swift { /// 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); + void setWarningGroupControlRules( + const llvm::SmallVector &rules) { + state.setWarningGroupControlRules(rules); + } + + const WarningGroupBehaviorMap& + getWarningGroupBehaviorControlMap() const { + return state.getWarningGroupBehaviorControlMap(); + } + + const std::vector + getWarningGroupBehaviorControlRefArray() const { + return state.getWarningGroupBehaviorControlRefArray(); + } /// Whether to print diagnostic names after their messages void setPrintDiagnosticNamesMode(PrintDiagnosticNamesMode val) { @@ -982,13 +998,9 @@ namespace swift { localization = diag::LocalizationProducer::producerFor(locale, path); } - bool isIgnoredDiagnosticGroup(DiagGroupID id) const { - return state.isIgnoredDiagnosticGroup(id); - } - - bool isIgnoredDiagnosticGroupTree(DiagGroupID id) const { - return state.isIgnoredDiagnosticGroupTree(id); - } + /// Whether the specified SourceFile enables a given diagnostic group + /// either syntactically, or via command-line flags. + bool isDiagnosticGroupEnabled(SourceFile *sf, DiagGroupID groupID) const; void ignoreDiagnostic(DiagID id) { state.compilerInternalIgnoreDiagnostic(id); @@ -1366,7 +1378,8 @@ namespace swift { Engine.TentativeDiagnostics.end()); for (auto &diagnostic : diagnostics) { - auto behavior = Engine.state.determineBehavior(diagnostic.Diag); + auto behavior = Engine.state.determineBehavior(diagnostic.Diag, + Engine.SourceMgr); if (behavior == DiagnosticBehavior::Fatal || behavior == DiagnosticBehavior::Error) return true; diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index 95b9690a064af..ce8503fb8aa10 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -1768,6 +1768,13 @@ WARNING(attr_warn_unused_result_removed,none, "'warn_unused_result' attribute behavior is now the default", ()) ERROR(attr_warn_unused_result_expected_rparen,none, "expected ')' after 'warn_unused_result' attribute", ()) + +// warn +ERROR(attr_warn_expected_diagnostic_group_identifier,none, + "expected '%0' option to be a diagnostic group identifier", (StringRef)) + +ERROR(attr_warn_expected_known_behavior,none, + "expected diagnostic behavior argument '%0' to be either 'error', 'warning' or 'ignored'", (StringRef)) // _specialize ERROR(attr_specialize_missing_colon,none, diff --git a/include/swift/Basic/DiagnosticOptions.h b/include/swift/Basic/DiagnosticOptions.h index 376ad905f8c99..b192ef1aecd8c 100644 --- a/include/swift/Basic/DiagnosticOptions.h +++ b/include/swift/Basic/DiagnosticOptions.h @@ -14,7 +14,7 @@ #define SWIFT_BASIC_DIAGNOSTICOPTIONS_H #include "swift/Basic/PrintDiagnosticNamesMode.h" -#include "swift/Basic/WarningAsErrorRule.h" +#include "swift/Basic/WarningGroupBehaviorRule.h" #include "llvm/ADT/Hashing.h" #include @@ -76,7 +76,7 @@ class DiagnosticOptions { bool SuppressRemarks = false; /// Rules for escalating warnings to errors - std::vector WarningsAsErrorsRules; + llvm::SmallVector WarningGroupControlRules; /// When printing diagnostics, include either the diagnostic name /// (diag::whatever) at the end or the associated diagnostic group. diff --git a/include/swift/Basic/Features.def b/include/swift/Basic/Features.def index 9721da54b3608..74c3b701840c5 100644 --- a/include/swift/Basic/Features.def +++ b/include/swift/Basic/Features.def @@ -557,6 +557,9 @@ EXPERIMENTAL_FEATURE(AnyAppleOSAvailability, true) /// Check @_implementationOnly imports in non-library-evolution mode. EXPERIMENTAL_FEATURE(CheckImplementationOnly, true) +/// Enable source-level warning control with `@warn` +EXPERIMENTAL_FEATURE(SourceWarningControl, true) + #undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE #undef EXPERIMENTAL_FEATURE #undef UPCOMING_FEATURE diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index e3d5b1ebdc700..10266e836778a 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -674,7 +674,7 @@ namespace swift { /// Whether or not to allow experimental features that are only available /// in "production". #ifdef NDEBUG - bool RestrictNonProductionExperimentalFeatures = true; + bool RestrictNonProductionExperimentalFeatures = false; #else bool RestrictNonProductionExperimentalFeatures = false; #endif diff --git a/include/swift/Basic/WarningAsErrorRule.h b/include/swift/Basic/WarningAsErrorRule.h deleted file mode 100644 index f0da7daca5b7b..0000000000000 --- a/include/swift/Basic/WarningAsErrorRule.h +++ /dev/null @@ -1,75 +0,0 @@ -//===--- 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/Basic/WarningGroupBehavior.h b/include/swift/Basic/WarningGroupBehavior.h new file mode 100644 index 0000000000000..2b62a963db4f2 --- /dev/null +++ b/include/swift/Basic/WarningGroupBehavior.h @@ -0,0 +1,50 @@ +//===-- WarningGroupBehavior.h ----------------------------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 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_WARNINGBEHAVIOR_H +#define SWIFT_BASIC_WARNINGBEHAVIOR_H + +/// `WarningGroupBehavior.h` is imported into Swift. Be *very* careful with what you +/// include here and keep these includes minimal! +/// +/// See include guidelines and caveats in `BasicBridging.h`. +#include "swift/Basic/SwiftBridging.h" +#include +#include +#include + +namespace swift { + +// Describes how a diagnostic group's warnings are to be emitted +enum ENUM_EXTENSIBILITY_ATTR(closed) WarningGroupBehavior { + AsError SWIFT_NAME("error"), + AsWarning SWIFT_NAME("warning"), + Ignored SWIFT_NAME("ignored"), + None SWIFT_NAME("none") +}; + +constexpr const auto DiagLinksCount = [] { + size_t count = 0; +#define GROUP_LINK(Parent, Child) ++count; +#include "swift/AST/DiagnosticGroups.def" + return count; +}(); + +constexpr std::array, swift::DiagLinksCount> DiagnosticGroupLinks = {{ +#define GROUP_LINK(Parent, Child) {#Parent, #Child}, +#include "swift/AST/DiagnosticGroups.def" +}}; + +} // namespace swift + +#endif // SWIFT_BASIC_WARNINGBEHAVIOR_H + diff --git a/include/swift/Basic/WarningGroupBehaviorRule.h b/include/swift/Basic/WarningGroupBehaviorRule.h new file mode 100644 index 0000000000000..4540367f0b911 --- /dev/null +++ b/include/swift/Basic/WarningGroupBehaviorRule.h @@ -0,0 +1,48 @@ +//===--- WarningGroupBehaviorRule.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_WARNINGBEHAVIORRULE_H +#define SWIFT_BASIC_WARNINGBEHAVIORRULE_H + +#include "swift/AST/DiagnosticGroups.h" +#include "swift/Basic/Assertions.h" +#include "swift/Basic/WarningGroupBehavior.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/ErrorHandling.h" +#include +#include +#include + +namespace swift { + +/// Describes a rule how to treat a warning group or all warnings. +class WarningGroupBehaviorRule { +public: + WarningGroupBehaviorRule(WarningGroupBehavior behavior, + std::optional targetGroup = std::nullopt) + : behavior(behavior), targetGroup(targetGroup) {} + + WarningGroupBehavior getBehavior() const { return behavior; } + + bool hasGroup() const { return targetGroup.has_value(); } + DiagGroupID getGroup() const { + ASSERT(hasGroup()); + return *targetGroup; + } + +private: + WarningGroupBehavior behavior; + std::optional targetGroup; +}; +} // end namespace swift + +#endif // SWIFT_BASIC_WARNINGBEHAVIORRULE_H diff --git a/include/swift/Bridging/ASTGen.h b/include/swift/Bridging/ASTGen.h index 857a82edf42e7..2987a3fb6ca49 100644 --- a/include/swift/Bridging/ASTGen.h +++ b/include/swift/Bridging/ASTGen.h @@ -117,6 +117,17 @@ intptr_t swift_ASTGen_configuredRegions( void swift_ASTGen_freeConfiguredRegions( BridgedIfConfigClauseRangeInfo *_Nullable regions, intptr_t numRegions); +swift::WarningGroupBehavior swift_ASTGen_warningGroupBehaviorAtPosition( + void *_Nonnull sourceFile, + BridgedArrayRef globalRules, + BridgedStringRef diagnosticGroupNameStrRef, + swift::SourceLoc loc); + +bool swift_ASTGen_isWarningGroupEnabledInFile( + void *_Nonnull sourceFile, + BridgedArrayRef globalRules, + BridgedStringRef diagnosticGroupNameStrRef); + intptr_t swift_ASTGen_activeInEmbeddedSwift( BridgedASTContext astContext, void *_Nonnull sourceFile, diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index e1c7f80493ee3..fc039a15bee55 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -61,11 +61,13 @@ #include "swift/AST/TypeCheckRequests.h" #include "swift/Basic/APIntMap.h" #include "swift/Basic/Assertions.h" +#include "swift/Basic/BasicBridging.h" #include "swift/Basic/BlockList.h" #include "swift/Basic/Compiler.h" #include "swift/Basic/SourceManager.h" #include "swift/Basic/Statistic.h" #include "swift/Basic/StringExtras.h" +#include "swift/Bridging/ASTGen.h" #include "swift/ClangImporter/ClangModule.h" #include "swift/Frontend/ModuleInterfaceLoader.h" #include "swift/Serialization/SerializedModuleLoader.h" diff --git a/lib/AST/ASTDumper.cpp b/lib/AST/ASTDumper.cpp index d121ffc015674..e31ca184e6ad8 100644 --- a/lib/AST/ASTDumper.cpp +++ b/lib/AST/ASTDumper.cpp @@ -5584,6 +5584,31 @@ class PrintAttribute : public AttributeVisitor, } printFoot(); } + + void visitWarnAttr(WarnAttr *Attr, Label label) { + printCommon(Attr, "warn", label); + auto &diagGroupInfo = getDiagGroupInfoByID(Attr->DiagnosticGroupID); + printFieldRaw([&](raw_ostream &out) { out << diagGroupInfo.name; }, + Label::always("diagGroupID:")); + switch (Attr->DiagnosticBehavior) { + case WarningGroupBehavior::None: + case WarningGroupBehavior::AsWarning: + printFieldRaw([&](raw_ostream &out) { out << "warning"; }, + Label::always("as:")); + break; + case WarningGroupBehavior::AsError: + printFieldRaw([&](raw_ostream &out) { out << "error"; }, + Label::always("as:")); + break; + case WarningGroupBehavior::Ignored: + printFieldRaw([&](raw_ostream &out) { out << "ignored"; }, + Label::always("as:")); + break; + } + if (Attr->Reason) + printFieldQuoted(Attr->Reason, Label::always("reason:")); + printFoot(); + } }; } // end anonymous namespace diff --git a/lib/AST/Attr.cpp b/lib/AST/Attr.cpp index 7cb992ea47479..5a117a8571fdb 100644 --- a/lib/AST/Attr.cpp +++ b/lib/AST/Attr.cpp @@ -1325,6 +1325,32 @@ bool DeclAttribute::printImpl(ASTPrinter &Printer, const PrintOptions &Options, Printer.printAttrName("@section"); Printer << "(\"" << cast(this)->Name << "\")"; break; + + case DeclAttrKind::Warn: { + auto warnAttr = cast(this); + Printer.printAttrName("@warn("); + + auto &diagGroupInfo = getDiagGroupInfoByID(warnAttr->DiagnosticGroupID); + Printer.printText(diagGroupInfo.name); + Printer << ", "; + switch (cast(this)->DiagnosticBehavior) { + case WarningGroupBehavior::None: + case WarningGroupBehavior::AsWarning: + Printer << "as: warning"; + break; + case WarningGroupBehavior::AsError: + Printer << "as: error"; + break; + case WarningGroupBehavior::Ignored: + Printer << "as: ignored"; + break; + } + if (cast(this)->Reason) { + Printer << ", \"" << *(cast(this)->Reason) << "\""; + } + Printer <<")"; + } + break; case DeclAttrKind::ObjC: { Printer.printAttrName("@objc"); @@ -2007,6 +2033,8 @@ StringRef DeclAttribute::getAttrName() const { return "_rawLayout"; case DeclAttrKind::Extern: return "_extern"; + case DeclAttrKind::Warn: + return "warn"; case DeclAttrKind::AllowFeatureSuppression: if (cast(this)->getInverted()) { return "_disallowFeatureSuppression"; diff --git a/lib/AST/Bridging/DeclAttributeBridging.cpp b/lib/AST/Bridging/DeclAttributeBridging.cpp index 3af8c1e5a8327..07da7cad0c909 100644 --- a/lib/AST/Bridging/DeclAttributeBridging.cpp +++ b/lib/AST/Bridging/DeclAttributeBridging.cpp @@ -492,6 +492,39 @@ BridgedSwiftNativeObjCRuntimeBaseAttr_createParsed(BridgedASTContext cContext, SwiftNativeObjCRuntimeBaseAttr(name, atLoc, range, /*Implicit=*/false); } +BridgedWarnAttr +BridgedWarnAttr_createParsed(BridgedASTContext cContext, + SourceLoc atLoc, + SourceRange range, + Identifier diagGroupName, + WarningGroupBehavior behavior, + BridgedStringRef reason) { + ASTContext &context = cContext.unbridged(); + auto diagGroupID = getDiagGroupIDByName(diagGroupName.str()); + + WarningGroupBehavior attrBehavior; + switch (behavior) { + case None: + case AsWarning: + attrBehavior = WarningGroupBehavior::AsWarning; + break; + case AsError: + attrBehavior = WarningGroupBehavior::AsError; + break; + case Ignored: + attrBehavior = WarningGroupBehavior::Ignored; + break; + } + + std::optional reasonText = std::nullopt; + if (!reason.getIsEmpty()) + reasonText = context.AllocateCopy(reason.unbridged()); + + return new (context) WarnAttr(*diagGroupID, attrBehavior, + reasonText, atLoc, range, + /*Implicit=*/false); +} + static NonSendableKind unbridged(BridgedNonSendableKind kind) { switch (kind) { case BridgedNonSendableKindSpecific: diff --git a/lib/AST/Bridging/DiagnosticsBridging.cpp b/lib/AST/Bridging/DiagnosticsBridging.cpp index 6f151ae25a906..287e2034f0f98 100644 --- a/lib/AST/Bridging/DiagnosticsBridging.cpp +++ b/lib/AST/Bridging/DiagnosticsBridging.cpp @@ -16,6 +16,7 @@ #include "swift/AST/DiagnosticsCommon.h" #include "swift/Basic/Assertions.h" #include "swift/Basic/SourceManager.h" +#include "swift/Basic/WarningGroupBehaviorRule.h" using namespace swift; @@ -164,3 +165,28 @@ void BridgedDiagnostic_finish(BridgedDiagnostic cDiag) { BridgedDiagnostic::Impl *diag = cDiag.unbridged(); delete diag; } + +//===----------------------------------------------------------------------===// +// MARK: WarningGroupBehaviorRule +//===----------------------------------------------------------------------===// + +BridgedStringRef BridgedWarningGroupBehaviorRule_getGroupName(BridgedWarningGroupBehaviorRule rule) { + if (rule.unbridged()->hasGroup()) + return BridgedStringRef(getDiagGroupInfoByID(rule.unbridged()->getGroup()).name); + return BridgedStringRef("no_group"); +} + +swift::WarningGroupBehavior +BridgedWarningGroupBehaviorRule_getBehavior(BridgedWarningGroupBehaviorRule rule) { + return rule.unbridged()->getBehavior(); +} + +SwiftInt BridgedDiagnosticGroupLinks_getCount() { + return DiagLinksCount; +} + +std::pair +BridgedDiagnosticGroupLinks_getLink(SwiftInt index) { + return std::make_pair(BridgedStringRef(DiagnosticGroupLinks[index].first), + BridgedStringRef(DiagnosticGroupLinks[index].second)); +} diff --git a/lib/AST/DiagnosticEngine.cpp b/lib/AST/DiagnosticEngine.cpp index 620019f587d68..e3499d196a099 100644 --- a/lib/AST/DiagnosticEngine.cpp +++ b/lib/AST/DiagnosticEngine.cpp @@ -17,6 +17,7 @@ #include "swift/AST/DiagnosticEngine.h" #include "swift/AST/AvailabilityDomain.h" +#include "swift/AST/ASTBridging.h" #include "swift/AST/ASTContext.h" #include "swift/AST/ASTPrinter.h" #include "swift/AST/Decl.h" @@ -28,6 +29,7 @@ #include "swift/AST/Expr.h" #include "swift/AST/Module.h" #include "swift/AST/Pattern.h" +#include "swift/AST/ParseRequests.h" #include "swift/AST/PrintOptions.h" #include "swift/AST/SourceFile.h" #include "swift/AST/Stmt.h" @@ -35,6 +37,7 @@ #include "swift/AST/TypeRepr.h" #include "swift/Basic/Assertions.h" #include "swift/Basic/SourceManager.h" +#include "swift/Bridging/ASTGen.h" #include "swift/Config.h" #include "swift/Localization/LocalizationFormat.h" #include "swift/Parse/Lexer.h" // bad dependency @@ -148,18 +151,12 @@ static constexpr const char *const fixItStrings[] = { DiagnosticState::DiagnosticState() { // Initialize ignored diagnostic groups to defaults - ignoredDiagnosticGroups.resize(DiagGroupsCount); - // Ensure that for each `DefaultIgnoreWarnings` group, - // we propagate this behavior to its child groups. for (const auto &groupInfo : diagnosticGroupsInfo) if (groupInfo.defaultIgnoreWarnings) getDiagGroupInfoByID(groupInfo.id).traverseDepthFirst([&](auto group) { - ignoredDiagnosticGroups[(unsigned)group.id] = true; + addWarningGroupControl(group.id, WarningGroupBehavior::Ignored); }); - // Initialize warningsAsErrors to default - warningsAsErrors.resize(DiagGroupsCount); - // Initialize compilerIgnoredDiagnostics compilerIgnoredDiagnostics.resize(NumDiagIDs); } @@ -167,7 +164,9 @@ DiagnosticState::DiagnosticState() { bool DiagnosticState::isIgnoredDiagnosticGroupTree(DiagGroupID id) const { bool anyEnabled = false; getDiagGroupInfoByID(id).traverseDepthFirst([&](auto group) { - anyEnabled |= !isIgnoredDiagnosticGroup(group.id); + anyEnabled |= + warningGroupBehaviorMap.at(group.id).getBehavior() != + WarningGroupBehavior::Ignored; }); return !anyEnabled; } @@ -585,41 +584,43 @@ bool DiagnosticEngine::finishProcessing() { return hadError; } -void DiagnosticEngine::setWarningsAsErrorsRules( - const std::vector &rules) { - std::vector unknownGroups; +bool DiagnosticEngine::isDiagnosticGroupEnabled(SourceFile *sf, DiagGroupID groupID) const { +#if SWIFT_BUILD_SWIFT_SYNTAX + auto ruleRefArray = getWarningGroupBehaviorControlRefArray(); + bool anyEnabled = false; + getDiagGroupInfoByID(groupID).traverseDepthFirst([&](auto group) { + anyEnabled |= swift_ASTGen_isWarningGroupEnabledInFile( + sf->getExportedSourceFile(), + BridgedArrayRef(ruleRefArray.data(), ruleRefArray.size()), + StringRef(getDiagGroupInfoByID(group.id).name)); + }); + return anyEnabled; +#else + // Fallback to checking only the command-line configuration + return !state.isIgnoredDiagnosticGroupTree(groupID); +#endif +} + +void DiagnosticState::setWarningGroupControlRules( + const llvm::SmallVector &rules) { 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) { - state.setWarningsAsErrorsForDiagGroupID(group.id, isEnabled); - state.setIgnoredDiagnosticGroup(group.id, false); - }); - } else { - unknownGroups.push_back(std::string(name)); - } - } else if (std::holds_alternative(target)) { - state.setAllWarningsAsErrors(isEnabled); + if (rule.hasGroup()) { + getDiagGroupInfoByID(rule.getGroup()).traverseDepthFirst([&](auto group) { + addWarningGroupControl(group.id, rule.getBehavior()); + }); } else { - llvm_unreachable("unhandled WarningAsErrorRule::Target"); + // A global override for all groups of `-warnings-as-errors` or + // `-no-warnings-as-errors`. + for (const auto &groupInfo : diagnosticGroupsInfo) { + // Does not apply to warnings which are default ignored + if (warningGroupBehaviorMap.count(groupInfo.id) && + warningGroupBehaviorMap.at(groupInfo.id).getBehavior() == WarningGroupBehavior::Ignored) + continue; + + addWarningGroupControl(groupInfo.id, rule.getBehavior()); + } } } - for (const auto &unknownGroup : unknownGroups) { - diagnose(SourceLoc(), diag::unknown_warning_group, unknownGroup); - } } /// Skip forward to one of the given delimiters. @@ -1318,8 +1319,73 @@ llvm::cl::opt AssertOnError("swift-diagnostics-assert-on-error", llvm::cl::opt AssertOnWarning("swift-diagnostics-assert-on-warning", llvm::cl::init(false)); +std::optional +DiagnosticState::determineUserControlledWarningBehavior( + const Diagnostic &diag, SourceManager &sourceMgr) const { + auto &diagInfo = storedDiagnosticInfos[(unsigned)diag.getID()]; + if (diagInfo.kind != DiagnosticKind::Warning) + return std::nullopt; + + // Compute a behavior relying strictly on command-line provided + // `warningGroupBehaviorMap`. + std::optional userControlledBehavior; + if (warningGroupBehaviorMap.count(diag.getGroupID())) { + switch (warningGroupBehaviorMap.at(diag.getGroupID()).getBehavior()) { + case WarningGroupBehavior::AsWarning: + userControlledBehavior = DiagnosticBehavior::Warning; + break; + case WarningGroupBehavior::AsError: + userControlledBehavior = DiagnosticBehavior::Error; + break; + case WarningGroupBehavior::Ignored: + userControlledBehavior = DiagnosticBehavior::Ignore; + break; + case WarningGroupBehavior::None: + userControlledBehavior = std::nullopt; + break; + } + } else + userControlledBehavior = std::nullopt; + +#if SWIFT_BUILD_SWIFT_SYNTAX + // Use the combined global controls (command-line flags such as `-Werror`) + // and syntactic controls at the source location of this diagnostic to + // compute the configured emission behavior for this diagnostic group. + SourceLoc loc = diag.getLocOrDeclLoc(); + if (loc.isValid()) { + auto sourceFiles = sourceMgr.getSourceFilesForBufferID( + sourceMgr.findBufferContainingLoc(loc)); + SourceFile *SF = sourceFiles.front(); + if (SF) { + auto ruleRefArray = getWarningGroupBehaviorControlRefArray(); + WarningGroupBehavior behavior = + swift_ASTGen_warningGroupBehaviorAtPosition( + SF->getExportedSourceFile(), + BridgedArrayRef(ruleRefArray.data(), ruleRefArray.size()), + StringRef(getDiagGroupInfoByID(diag.getGroupID()).name), loc); + switch (behavior) { + case AsError: + userControlledBehavior = DiagnosticBehavior::Error; + break; + case AsWarning: + userControlledBehavior = DiagnosticBehavior::Warning; + break; + case Ignored: + userControlledBehavior = DiagnosticBehavior::Ignore; + break; + case None: + break; + } + } + } +#endif + + return userControlledBehavior; +} + DiagnosticBehavior -DiagnosticState::determineBehavior(const Diagnostic &diag) const { +DiagnosticState::determineBehavior(const Diagnostic &diag, + SourceManager &sourceMgr) const { // We determine how to handle a diagnostic based on the following rules // 1) Map the diagnostic to its "intended" behavior, applying the behavior // limit for this particular emission @@ -1356,10 +1422,9 @@ DiagnosticState::determineBehavior(const Diagnostic &diag) const { // 3) If the user substituted a different behavior for this warning, apply // that change if (lvl == DiagnosticBehavior::Warning) { - if (isIgnoredDiagnosticGroup(diag.getGroupID())) - lvl = DiagnosticBehavior::Ignore; - else if (getWarningsAsErrorsForDiagGroupID(diag.getGroupID())) - lvl = DiagnosticBehavior::Error; + if (auto userControlBehavior = + determineUserControlledWarningBehavior(diag, sourceMgr)) + lvl = *userControlBehavior; if (suppressWarnings) lvl = DiagnosticBehavior::Ignore; } @@ -1449,7 +1514,7 @@ void DiagnosticEngine::forwardTentativeDiagnosticsTo( std::optional DiagnosticEngine::diagnosticInfoForDiagnostic(const Diagnostic &diagnostic, bool includeDiagnosticName) { - auto behavior = state.determineBehavior(diagnostic); + auto behavior = state.determineBehavior(diagnostic, SourceMgr); state.updateFor(behavior); if (behavior == DiagnosticBehavior::Ignore) diff --git a/lib/AST/DiagnosticGroups.cpp b/lib/AST/DiagnosticGroups.cpp index 40a499e355848..49f8b6cd4e7ed 100644 --- a/lib/AST/DiagnosticGroups.cpp +++ b/lib/AST/DiagnosticGroups.cpp @@ -92,7 +92,7 @@ constexpr const auto diagnosticGroupConnections = [] { #include "swift/AST/DiagnosticsAll.def" // Produce the resulting structure with all the edges -#define GROUP(Name, Option, DocsFile) \ +#define GROUP(Name, Option, DocsFile) \ GroupConnections(Name##_supergroups, Name##_subgroups, Name##_diagnostics), return std::tuple{ #include "swift/AST/DiagnosticGroups.def" diff --git a/lib/AST/FeatureSet.cpp b/lib/AST/FeatureSet.cpp index 7d40a91ba92f3..8cb283fee8655 100644 --- a/lib/AST/FeatureSet.cpp +++ b/lib/AST/FeatureSet.cpp @@ -309,6 +309,10 @@ static bool usesFeatureConcurrencySyntaxSugar(Decl *decl) { return false; } +static bool usesFeatureSourceWarningControl(Decl *decl) { + return decl->getAttrs().hasAttribute(); +} + static bool usesFeatureCompileTimeValues(Decl *decl) { return decl->getAttrs().hasAttribute() || decl->getAttrs().hasAttribute(); diff --git a/lib/ASTGen/Sources/ASTGen/CMakeLists.txt b/lib/ASTGen/Sources/ASTGen/CMakeLists.txt index 678a23509cc43..aaaada42ad49e 100644 --- a/lib/ASTGen/Sources/ASTGen/CMakeLists.txt +++ b/lib/ASTGen/Sources/ASTGen/CMakeLists.txt @@ -23,6 +23,7 @@ add_pure_swift_host_library(swiftASTGen STATIC CXX_INTEROP Stmts.swift TypeAttrs.swift Types.swift + WarningGroupBehaviorConfiguration.swift DEPENDENCIES swiftAST @@ -31,6 +32,7 @@ add_pure_swift_host_library(swiftASTGen STATIC CXX_INTEROP _CompilerRegexParser _CompilerSwiftSyntax _CompilerSwiftIfConfig + _CompilerSwiftWarningControl _CompilerSwiftLexicalLookup _CompilerSwiftOperators _CompilerSwiftSyntaxBuilder diff --git a/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift b/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift index 5e66153edcd3e..67af63aa71741 100644 --- a/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift +++ b/lib/ASTGen/Sources/ASTGen/DeclAttrs.swift @@ -196,6 +196,8 @@ extension ASTGenVisitor { return handle(self.generateStorageRestrictionAttr(attribute: node)?.asDeclAttribute) case .SwiftNativeObjCRuntimeBase: return handle(self.generateSwiftNativeObjCRuntimeBaseAttr(attribute: node)?.asDeclAttribute) + case .Warn: + return handle(self.generateWarnAttr(attribute: node)?.asDeclAttribute) case .Transpose: return handle(self.generateTransposeAttr(attribute: node)?.asDeclAttribute) case .TypeEraser: @@ -2169,6 +2171,56 @@ extension ASTGenVisitor { name: name ) } + + /// E.g.: + /// ``` + /// @warn(DiagGroupID, as: Behavior, reason: String?) + /// ``` + func generateWarnAttr(attribute node: AttributeSyntax) -> BridgedWarnAttr? { + guard let diagGroupIdentifier: swift.Identifier = self.generateWithLabeledExprListArguments(attribute: node, { args in + self.generateConsumingAttrOption(args: &args, label: nil) { expr in + guard let declRefExpr = expr.as(DeclReferenceExprSyntax.self) else { + return nil + } + return self.generateIdentifier(declRefExpr.baseName) + } + }) else { + return nil + } + + guard let behavior: swift.WarningGroupBehavior = self.generateWithLabeledExprListArguments(attribute: node, { args in + self.generateConsumingAttrOption(args: &args, label: "as") { expr in + guard let declRefExpr = expr.as(DeclReferenceExprSyntax.self) else { + return nil + } + switch declRefExpr.baseName.text { + case "error": return swift.WarningGroupBehavior.error + case "warning": return swift.WarningGroupBehavior.warning + case "ignored": return swift.WarningGroupBehavior.ignored + default: return nil + } + } + }) else { + return nil + } + + let reason: BridgedStringRef + if let userSpecifiedReason = self.generateWithLabeledExprListArguments(attribute: node, { args in + self.generateConsumingSimpleStringLiteralAttrOption(args: &args, label: "reason")}) { + reason = userSpecifiedReason + } else { + reason = allocateBridgedString("") + } + + return .createParsed( + self.ctx, + atLoc: self.generateSourceLoc(node.atSign), + range: self.generateAttrSourceRange(node), + diagGroupName: diagGroupIdentifier, + behavior: behavior, + reason: reason + ) + } /// E.g.: /// ``` diff --git a/lib/ASTGen/Sources/ASTGen/SourceFile.swift b/lib/ASTGen/Sources/ASTGen/SourceFile.swift index b6823c5a5f944..7793e54de9453 100644 --- a/lib/ASTGen/Sources/ASTGen/SourceFile.swift +++ b/lib/ASTGen/Sources/ASTGen/SourceFile.swift @@ -13,6 +13,7 @@ import ASTBridging import SwiftDiagnostics import SwiftIfConfig +@_spi(ExperimentalLanguageFeatures) import SwiftWarningControl @_spi(ExperimentalLanguageFeatures) import SwiftParser import SwiftParserDiagnostics @_spi(Compiler) import SwiftSyntax @@ -43,6 +44,11 @@ public struct ExportedSourceFile { /// This is a cached value; access via configuredRegions(astContext:). var _configuredRegions: ConfiguredRegions? = nil + /// Warning group control regions for this source file + /// + /// This is a cached value; access via warningGroupControlRegionTree(astContext:) + var _warningControlRegionTree: WarningControlRegionTree? = nil + /// Configured regions for this source file assuming if it were being treated /// as Embedded Swift. This is used only when we are compiling non-Embedded /// Swift but diagnosing uses of constructs that aren't allowed in Embedded diff --git a/lib/ASTGen/Sources/ASTGen/WarningGroupBehaviorConfiguration.swift b/lib/ASTGen/Sources/ASTGen/WarningGroupBehaviorConfiguration.swift new file mode 100644 index 0000000000000..b9d65f7b00f23 --- /dev/null +++ b/lib/ASTGen/Sources/ASTGen/WarningGroupBehaviorConfiguration.swift @@ -0,0 +1,97 @@ +//===--- WarningGroupBehaviorConfiguration.swift --------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022-2025 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 +// +//===----------------------------------------------------------------------===// + +import BasicBridging +import ASTBridging +import swiftBasicSwift +import SwiftDiagnostics +@_spi(ExperimentalLanguageFeatures) import SwiftWarningControl +@_spi(ExperimentalLanguageFeatures) import SwiftParser +@_spi(ExperimentalLanguageFeatures) import SwiftSyntax + +@_cdecl("swift_ASTGen_warningGroupBehaviorAtPosition") +public func warningGroupBehaviorAtPosition( + sourceFilePtr: UnsafeMutableRawPointer, + globalRules: BridgedArrayRef, + diagnosticGroupNameStrRef: BridgedStringRef, + loc: SourceLoc) -> swift.WarningGroupBehavior { + let sourceFilePtr = sourceFilePtr.bindMemory(to: ExportedSourceFile.self, capacity: 1) + let regions = sourceFilePtr.pointee.warningGroupControlRegionTree(globalRules: globalRules) + + let diagnosticGroupIdentifier = DiagnosticGroupIdentifier(String(bridged: diagnosticGroupNameStrRef)) + guard let position = sourceFilePtr.pointee.position(of: loc) else { + return swift.WarningGroupBehavior.none + } + + guard let control = regions.warningGroupControl(at: position, + for: diagnosticGroupIdentifier) else { + return swift.WarningGroupBehavior.none + } + + switch control { + case .error: return swift.WarningGroupBehavior.error + case .warning: return swift.WarningGroupBehavior.warning + case .ignored: return swift.WarningGroupBehavior.ignored + } +} + +@_cdecl("swift_ASTGen_isWarningGroupEnabledInFile") +public func isWarningGroupEnabledInFile( + sourceFilePtr: UnsafeMutableRawPointer, + globalRules: BridgedArrayRef, + diagnosticGroupNameStrRef: BridgedStringRef) -> Bool { + let sourceFilePtr = sourceFilePtr.bindMemory(to: ExportedSourceFile.self, capacity: 1) + let regions = sourceFilePtr.pointee.warningGroupControlRegionTree(globalRules: globalRules) + let diagnosticGroupIdentifier = DiagnosticGroupIdentifier(String(bridged: diagnosticGroupNameStrRef)) + return regions.enabledGroups.contains(diagnosticGroupIdentifier) +} + +extension ExportedSourceFile { + /// Return the warning group behavior regions for this source file. + mutating func warningGroupControlRegionTree(globalRules: BridgedArrayRef) -> WarningControlRegionTree { + if let _warningControlRegionTree { + return _warningControlRegionTree + } + + // Bridge the global configuration (`-Werror`, `-Wwarning`, etc.) + var globalGroupRules: [DiagnosticGroupIdentifier: WarningGroupControl] = [:] + let pointer = globalRules.data?.assumingMemoryBound(to: BridgedWarningGroupBehaviorRule.self) + let globalRuleArray = Array(UnsafeBufferPointer(start: pointer, count: globalRules.count)) + for rule in globalRuleArray { + let groupIdentifier = DiagnosticGroupIdentifier(String(bridged: rule.getGroupName())) + globalGroupRules[groupIdentifier] = switch rule.getBehavior() { + case .error: WarningGroupControl.error + case .warning: WarningGroupControl.warning + case .ignored: WarningGroupControl.ignored + case .none: fatalError("unexpected 'none' warning group behavior control") + } + } + + // Bridge the compiler's diagnostic group links + var subGroups: [DiagnosticGroupIdentifier : [DiagnosticGroupIdentifier]] = [:] + for i in 0...getDiagnosticGroupLinksCount() { + let linkPair = getDiagnosticGroupLink(at: i) + let superGroupID = DiagnosticGroupIdentifier(String(bridged: linkPair.first)) + let subGroupID = DiagnosticGroupIdentifier(String(bridged: linkPair.second)) + if subGroups[superGroupID] == nil { + subGroups[superGroupID] = [subGroupID] + } else { + subGroups[superGroupID]!.append(subGroupID) + } + } + let groupInheritanceTree = (try? DiagnosticGroupInheritanceTree(subGroups: subGroups)) ?? nil + let regions = syntax.warningGroupControlRegionTree(globalControls: globalGroupRules, + groupInheritanceTree: groupInheritanceTree) + _warningControlRegionTree = regions + return regions + } +} diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index c15d59e5eb1f0..82d0d9ea3824e 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -2697,39 +2697,41 @@ static bool ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args, // If the "embedded" flag was provided, enable the EmbeddedRestrictions // warning group. This group is opt-in in non-Embedded builds. if (isEmbedded(Args) && !Args.hasArg(OPT_suppress_warnings) && - !temporarilySuppressEmbeddedRestrictionDiagnostics(Args)) { - Opts.WarningsAsErrorsRules.push_back( - WarningAsErrorRule(WarningAsErrorRule::Action::Disable, - "EmbeddedRestrictions")); - } + !temporarilySuppressEmbeddedRestrictionDiagnostics(Args)) + Opts.WarningGroupControlRules.emplace_back(WarningGroupBehavior::AsWarning, + DiagGroupID::EmbeddedRestrictions); Opts.SuppressWarnings |= Args.hasArg(OPT_suppress_warnings); Opts.SuppressNotes |= Args.hasArg(OPT_suppress_notes); Opts.SuppressRemarks |= Args.hasArg(OPT_suppress_remarks); 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"); - } - }()); + switch (arg->getOption().getID()) { + case OPT_warnings_as_errors: + Opts.WarningGroupControlRules.emplace_back(WarningGroupBehavior::AsError); + break; + case OPT_no_warnings_as_errors: + Opts.WarningGroupControlRules.emplace_back( + WarningGroupBehavior::AsWarning); + break; + case OPT_Werror: + Opts.WarningGroupControlRules.emplace_back( + WarningGroupBehavior::AsError, getDiagGroupIDByName(arg->getValue())); + break; + case OPT_Wwarning: + Opts.WarningGroupControlRules.emplace_back( + WarningGroupBehavior::AsWarning, + getDiagGroupIDByName(arg->getValue())); + break; + default: + llvm_unreachable("unhandled warning as error option"); + }; } // `-require-explicit-sendable` is an alias to `-Wwarning ExplicitSendable`. if (Args.hasArg(OPT_require_explicit_sendable) && !Args.hasArg(OPT_suppress_warnings)) { - Opts.WarningsAsErrorsRules.push_back(WarningAsErrorRule( - WarningAsErrorRule::Action::Disable, "ExplicitSendable")); + Opts.WarningGroupControlRules.emplace_back( + WarningGroupBehavior::AsWarning, DiagGroupID::ExplicitSendable); } if (Args.hasArg(OPT_debug_diagnostic_names)) { @@ -2775,11 +2777,6 @@ static bool ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args, Opts.LocalizationPath = A->getValue(); } } - assert(!(Opts.SuppressWarnings && - WarningAsErrorRule::hasConflictsWithSuppressWarnings( - Opts.WarningsAsErrorsRules)) && - "conflicting arguments; should have been caught by driver"); - return false; } @@ -2799,7 +2796,7 @@ static void configureDiagnosticEngine( if (Options.SuppressRemarks) { Diagnostics.setSuppressRemarks(true); } - Diagnostics.setWarningsAsErrorsRules(Options.WarningsAsErrorsRules); + Diagnostics.setWarningGroupControlRules(Options.WarningGroupControlRules); Diagnostics.setPrintDiagnosticNamesMode(Options.PrintDiagnosticNames); std::string docsPath = Options.DiagnosticDocumentationPath; diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index 863ea99a88484..9f3130442030a 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -3221,6 +3221,97 @@ ParserStatus Parser::parseNewDeclAttribute(DeclAttributes &Attributes, break; } + case DeclAttrKind::Warn: { + if (!consumeIfAttributeLParen()) { + diagnose(Loc, diag::attr_expected_lparen, AttrName, + DeclAttribute::isDeclModifier(DK)); + return makeParserSuccess(); + } + + // Parse the diagnostic group identifier + StringRef ParsedCategoryIdentifier = Tok.getText(); + if (!consumeIf(tok::identifier)) { + diagnose(Loc, diag::attr_expected_option_identifier, AttrName); + return makeParserSuccess(); + } + auto DiagGroupID = getDiagGroupIDByName(ParsedCategoryIdentifier); + if (!DiagGroupID) { + diagnose(Loc, diag::attr_warn_expected_diagnostic_group_identifier, + ParsedCategoryIdentifier); + } + + if (!consumeIf(tok::comma)) { + diagnose(Tok, diag::attr_expected_comma, "@warn", false); + return makeParserSuccess(); + } + + // Parse the `as: error|warning|ignored` behavior specifier + Identifier AsLabel; + consumeArgumentLabel(AsLabel, true); + if (AsLabel.str() != "as") { + diagnose(Loc, diag::attr_expected_label, "as:", AttrName); + return makeParserSuccess(); + } + if (!consumeIf(tok::colon)) { + diagnose(Loc, diag::attr_expected_colon_after_label, AsLabel.str()); + return makeParserSuccess(); + } + // Map the behavior identifier to WarnAttr::Behavior + StringRef ParsedBehaviorIdentifier = Tok.getText(); + if (!consumeIf(tok::identifier)) { + diagnose(Loc, diag::attr_expected_option_identifier, AttrName); + return makeParserSuccess(); + } + auto BehaviorSpecifier = + llvm::StringSwitch>( + ParsedBehaviorIdentifier) + .Case("error", WarningGroupBehavior::AsError) + .Case("warning", WarningGroupBehavior::AsWarning) + .Case("ignored", WarningGroupBehavior::Ignored) + .Default(std::nullopt); + if (!BehaviorSpecifier) { + diagnose(Loc, diag::attr_warn_expected_known_behavior, + ParsedBehaviorIdentifier); + } + + // Parse the optional `reason:` argument + std::optional ReasonString = std::nullopt; + if (consumeIf(tok::comma)) { + Identifier ReasonLabel; + consumeArgumentLabel(ReasonLabel, true); + if (ReasonLabel.str() != "reason") { + diagnose(Loc, diag::attr_expected_label, "reason:", AttrName); + return makeParserSuccess(); + } + if (!consumeIf(tok::colon)) { + diagnose(Loc, diag::attr_expected_colon_after_label, AsLabel.str()); + return makeParserSuccess(); + } + + // Parse out the given reason string literal + if (Tok.isNot(tok::string_literal)) { + diagnose(Loc, diag::attr_expected_string_literal, AttrName); + skipUntilTokenOrEndOfLine(tok::r_paren); + } else { + ReasonString = + getStringLiteralIfNotInterpolated(Loc, ("'" + AttrName + "'").str()); + consumeToken(tok::string_literal); + } + } + + if (!consumeIf(tok::r_paren)) { + diagnose(Loc, diag::attr_expected_rparen, AttrName, + DeclAttribute::isDeclModifier(DK)); + return makeParserSuccess(); + } + + if (!DiscardAttribute) + Attributes.add(new (Context) WarnAttr(*DiagGroupID, *BehaviorSpecifier, + ReasonString, AtLoc, AttrRange, + /*Implicit=*/false)); + break; + } + case DeclAttrKind::Alignment: { if (!consumeIfAttributeLParen()) { diagnose(Loc, diag::attr_expected_lparen, AttrName, diff --git a/lib/Sema/MiscDiagnostics.h b/lib/Sema/MiscDiagnostics.h index 654afdd5b9ea7..3ba457af05e92 100644 --- a/lib/Sema/MiscDiagnostics.h +++ b/lib/Sema/MiscDiagnostics.h @@ -141,7 +141,7 @@ namespace swift { ForEachStmt *forEach); /// Determine if any of the performance hint diagnostics are enabled. - bool performanceHintDiagnosticsEnabled(ASTContext &ctx); + bool performanceHintDiagnosticsEnabled(ASTContext &ctx, SourceFile *sf); class BaseDiagnosticWalker : public ASTWalker { protected: diff --git a/lib/Sema/PerformanceHints.cpp b/lib/Sema/PerformanceHints.cpp index 08c668cb45e93..04f45499e7572 100644 --- a/lib/Sema/PerformanceHints.cpp +++ b/lib/Sema/PerformanceHints.cpp @@ -30,8 +30,8 @@ using namespace swift; -bool swift::performanceHintDiagnosticsEnabled(ASTContext &ctx) { - return !ctx.Diags.isIgnoredDiagnosticGroupTree(DiagGroupID::PerformanceHints) && +bool swift::performanceHintDiagnosticsEnabled(ASTContext &ctx, SourceFile *sf) { + return ctx.Diags.isDiagnosticGroupEnabled(sf, DiagGroupID::PerformanceHints) && (ctx.TypeCheckerOpts.SkipFunctionBodies == FunctionBodySkipping::None); } diff --git a/lib/Sema/TypeCheckAttr.cpp b/lib/Sema/TypeCheckAttr.cpp index a3379c8a0d0da..47ef31479f522 100644 --- a/lib/Sema/TypeCheckAttr.cpp +++ b/lib/Sema/TypeCheckAttr.cpp @@ -207,6 +207,7 @@ class AttributeChecker : public AttributeVisitor { IGNORED_ATTR(AllowFeatureSuppression) IGNORED_ATTR(PreInverseGenerics) IGNORED_ATTR(Safe) + IGNORED_ATTR(Warn) #undef IGNORED_ATTR void visitABIAttr(ABIAttr *attr) { diff --git a/lib/Sema/TypeCheckConcurrency.cpp b/lib/Sema/TypeCheckConcurrency.cpp index 27685d80a55c5..8a8bb237297b0 100644 --- a/lib/Sema/TypeCheckConcurrency.cpp +++ b/lib/Sema/TypeCheckConcurrency.cpp @@ -1377,12 +1377,16 @@ static bool checkSendableInstanceStorage( void swift::diagnoseMissingExplicitSendable(NominalTypeDecl *nominal) { // Only diagnose when explicitly requested. ASTContext &ctx = nominal->getASTContext(); - if (ctx.Diags.isIgnoredDiagnosticGroupTree(DiagGroupID::ExplicitSendable)) - return; if (nominal->getLoc().isInvalid()) return; + auto locBufferID = ctx.SourceMgr.findBufferContainingLoc(nominal->getLoc()); + auto locSF = ctx.SourceMgr.getSourceFilesForBufferID(locBufferID).front(); + if (!ctx.Diags.isDiagnosticGroupEnabled(locSF, + DiagGroupID::ExplicitSendable)) + return; + // Protocols aren't checked. if (isa(nominal)) return; diff --git a/lib/Sema/TypeCheckDeclOverride.cpp b/lib/Sema/TypeCheckDeclOverride.cpp index 5861b1b0dfbfa..af78e333626c6 100644 --- a/lib/Sema/TypeCheckDeclOverride.cpp +++ b/lib/Sema/TypeCheckDeclOverride.cpp @@ -1755,6 +1755,7 @@ namespace { UNINTERESTING_ATTR(Safe) UNINTERESTING_ATTR(AddressableForDependencies) UNINTERESTING_ATTR(UnsafeSelfDependentResult) + UNINTERESTING_ATTR(Warn) #undef UNINTERESTING_ATTR void visitABIAttr(ABIAttr *attr) { diff --git a/lib/Sema/TypeCheckEmbedded.cpp b/lib/Sema/TypeCheckEmbedded.cpp index 39a4bd1849329..35b307b464878 100644 --- a/lib/Sema/TypeCheckEmbedded.cpp +++ b/lib/Sema/TypeCheckEmbedded.cpp @@ -49,9 +49,10 @@ swift::shouldDiagnoseEmbeddedLimitations(const DeclContext *dc, SourceLoc loc, // Check if the Embedded restriction diagnostics, which are ignored by // default, have been enabled. If it's still ignored, we won't diagnose - // anything. limitations. + // anything. auto &diags = dc->getASTContext().Diags; - if (diags.isIgnoredDiagnosticGroup(DiagGroupID::EmbeddedRestrictions)) + if (!diags.isDiagnosticGroupEnabled( + dc->getParentSourceFile(), DiagGroupID::EmbeddedRestrictions)) return std::nullopt; #if SWIFT_BUILD_SWIFT_SYNTAX diff --git a/lib/Sema/TypeChecker.cpp b/lib/Sema/TypeChecker.cpp index bb49ccc5d914d..b9a104ccbdb84 100644 --- a/lib/Sema/TypeChecker.cpp +++ b/lib/Sema/TypeChecker.cpp @@ -385,7 +385,7 @@ TypeCheckPrimaryFileRequest::evaluate(Evaluator &eval, SourceFile *SF) const { CheckInconsistentWeakLinkedImportsRequest{SF->getParentModule()}, {}); // Opt-in performance hint diagnostics for performance-critical code. - if (performanceHintDiagnosticsEnabled(Ctx)) + if (performanceHintDiagnosticsEnabled(Ctx, SF)) evaluateOrDefault(Ctx.evaluator, EmitPerformanceHints{SF}, {}); // Perform various AST transforms we've been asked to perform. diff --git a/lib/Serialization/ModuleFormat.h b/lib/Serialization/ModuleFormat.h index 2e10089b6b0c3..e709452195bcf 100644 --- a/lib/Serialization/ModuleFormat.h +++ b/lib/Serialization/ModuleFormat.h @@ -2337,6 +2337,11 @@ namespace decls_block { TypeIDField // result type >; + using WarnDeclAttrLayout = BCRecordLayout< + Warn_DECL_ATTR, + BCFixed<1> // implicit flag + >; + using ForeignAsyncConventionLayout = BCRecordLayout< FOREIGN_ASYNC_CONVENTION, TypeIDField, // completion handler type diff --git a/lib/Serialization/Serialization.cpp b/lib/Serialization/Serialization.cpp index d520e7602e6a4..93f9f1030fdc2 100644 --- a/lib/Serialization/Serialization.cpp +++ b/lib/Serialization/Serialization.cpp @@ -2952,6 +2952,7 @@ class Serializer::DeclSerializer : public DeclVisitor { case DeclAttrKind::ClangImporterSynthesizedType: case DeclAttrKind::PrivateImport: case DeclAttrKind::AllowFeatureSuppression: + case DeclAttrKind::Warn: llvm_unreachable("cannot serialize attribute"); #define SIMPLE_DECL_ATTR(_, CLASS, ...) \ diff --git a/test/Parse/warn_attr_misc.swift b/test/Parse/warn_attr_misc.swift new file mode 100644 index 0000000000000..dcfdc1a670a72 --- /dev/null +++ b/test/Parse/warn_attr_misc.swift @@ -0,0 +1,31 @@ +// REQUIRES: swift_feature_SourceWarningControl +// RUN: %target-typecheck-verify-swift -enable-experimental-feature SourceWarningControl + +@warn(ExistentialAny, as: error) +import Swift + +struct garply: ~Copyable { + @warn(ExistentialAny, as: ignored) + init() {} + + @warn(ExistentialAny, as: ignored) + subscript(index: Int) -> Int { + return 11 + } + + @warn(ExistentialAny, as: ignored) + var computedProperty: Int { + return 11 + } + + var property: Int { + @warn(ExistentialAny, as: error) + get { + return 11 + } + @warn(ExistentialAny, as: warning) + set { + let _ = 11 + } + } +} diff --git a/test/Parse/warn_attr_nominal.swift b/test/Parse/warn_attr_nominal.swift new file mode 100644 index 0000000000000..20e4a666ac447 --- /dev/null +++ b/test/Parse/warn_attr_nominal.swift @@ -0,0 +1,22 @@ +// REQUIRES: swift_feature_SourceWarningControl +// RUN: %target-typecheck-verify-swift -enable-experimental-feature SourceWarningControl + +@warn(ExistentialAny, as: error) +class Foo {} + +@warn(ExistentialAny, as: ignored) +struct Bar {} + +@warn(ExistentialAny, as: warning) +enum Baz {} + +@warn(ExistentialAny, as: error) +actor Qux { + @warn(ExistentialAny, as: ignored) + struct Quux {} +} +@warn(ExistentialAny, as: warning) +protocol Corge {} + +@warn(ExistentialAny, as: ignored) +extension Bar {} diff --git a/test/Parse/warn_attribute.swift b/test/Parse/warn_attribute.swift new file mode 100644 index 0000000000000..3123fe6daf418 --- /dev/null +++ b/test/Parse/warn_attribute.swift @@ -0,0 +1,21 @@ +// REQUIRES: swift_feature_SourceWarningControl +// RUN: %target-typecheck-verify-swift -enable-experimental-feature SourceWarningControl + +@warn(DeprecatedDeclaration, as: error) +func foo() {} + +@warn(Deprecate, as: warning) // expected-error {{expected 'Deprecate' option to be a diagnostic group identifier}} +func bar() {} + +@warn(DeprecatedDeclaration, as: what) // expected-error {{expected diagnostic behavior argument 'what' to be either 'error', 'warning' or 'ignored'}} +func baz() {} + +// expected-error @+1 {{expected 'Hmm' option to be a diagnostic group identifier}} +@warn(Hmm, as: what) // expected-error {{expected diagnostic behavior argument 'what' to be either 'error', 'warning' or 'ignored'}} +func qux() {} + +@warn(ExistentialAny, as: ignored, reason: "for a lit test") +func quux() {} + +@warn(ExistentialAny, as: ignored, reason: BadReason) // expected-error {{expected string literal in 'warn' attribute}} +func corge() {} diff --git a/test/attr/warn.swift b/test/attr/warn.swift new file mode 100644 index 0000000000000..f6f97ee8fcaea --- /dev/null +++ b/test/attr/warn.swift @@ -0,0 +1,15 @@ +// REQUIRES: swift_feature_SourceWarningControl +// RUN: %target-typecheck-verify-swift -enable-experimental-feature SourceWarningControl + +@available(*, deprecated) +func bar() -> [Int] { return [1,2,3] } + +@warn(DeprecatedDeclaration, as: error) +func foo() -> [Int] { + return bar() // expected-error {{'bar()' is deprecated}} +} + +@warn(DeprecatedDeclaration, as: ignored) +func baz() -> [Int] { + return bar() +} diff --git a/test/attr/warn_global_downgrade.swift b/test/attr/warn_global_downgrade.swift new file mode 100644 index 0000000000000..c91fbd273b0f6 --- /dev/null +++ b/test/attr/warn_global_downgrade.swift @@ -0,0 +1,12 @@ +// REQUIRES: swift_feature_SourceWarningControl +// RUN: %target-typecheck-verify-swift -enable-experimental-feature SourceWarningControl -warnings-as-errors + +@warn(PerformanceHints, as: warning) +func foo() -> [Int] { // expected-warning {{Performance: 'foo()' returns an array, leading to implicit copies. Consider using an 'inout' parameter instead.}} + return [1,2,3] +} + +@warn(ReturnTypeImplicitCopy, as: ignored) +func bar() -> [Int] { + return [1,2,3] +} diff --git a/test/attr/warn_global_enable.swift b/test/attr/warn_global_enable.swift new file mode 100644 index 0000000000000..83d8db058cf25 --- /dev/null +++ b/test/attr/warn_global_enable.swift @@ -0,0 +1,12 @@ +// REQUIRES: swift_feature_SourceWarningControl +// RUN: %target-typecheck-verify-swift -enable-experimental-feature SourceWarningControl + +@warn(PerformanceHints, as: error) +func foo() -> [Int] { // expected-error {{Performance: 'foo()' returns an array, leading to implicit copies. Consider using an 'inout' parameter instead.}} + return [1,2,3] +} + +@warn(ReturnTypeImplicitCopy, as: warning) +func bar() -> [Int] { // expected-warning {{Performance: 'bar()' returns an array, leading to implicit copies. Consider using an 'inout' parameter instead.}} + return [1,2,3] +} diff --git a/test/attr/warn_group_inheritance.swift b/test/attr/warn_group_inheritance.swift new file mode 100644 index 0000000000000..faec307bd1127 --- /dev/null +++ b/test/attr/warn_group_inheritance.swift @@ -0,0 +1,11 @@ +// REQUIRES: swift_feature_SourceWarningControl +// RUN: %target-typecheck-verify-swift -enable-experimental-feature SourceWarningControl -Wwarning PerformanceHints + +@warn(ReturnTypeImplicitCopy, as: error) +func foo() -> [Int] { // expected-error {{Performance: 'foo()' returns an array, leading to implicit copies. Consider using an 'inout' parameter instead.}} + return [1,2,3] +} + +func bar() -> [Int] { // expected-warning {{Performance: 'bar()' returns an array, leading to implicit copies. Consider using an 'inout' parameter instead.}} + return [1,2,3] +} diff --git a/test/attr/warn_sites.swift b/test/attr/warn_sites.swift new file mode 100644 index 0000000000000..444bd7ee8d8a8 --- /dev/null +++ b/test/attr/warn_sites.swift @@ -0,0 +1,116 @@ +// REQUIRES: swift_feature_SourceWarningControl +// RUN: %target-typecheck-verify-swift -enable-experimental-feature SourceWarningControl + +@available(*, deprecated) +func dep() -> Bool { return false } + +@available(*, deprecated) +struct depS {} + +// Function +@warn(DeprecatedDeclaration, as: error) +func foo() { + let _: Bool = dep() // expected-error {{'dep()' is deprecated}} + @warn(DeprecatedDeclaration, as: warning) + func bar() { + let _: Bool = dep() // expected-warning {{'dep()' is deprecated}} + @warn(DeprecatedDeclaration, as: ignored) + @warn(PerformanceHints, as: error) + func baz() { + let _: Bool = dep() + } + @warn(DeprecatedDeclaration, as: error) + func qux() { + let _: Bool = dep() // expected-error {{'dep()' is deprecated}} + @warn(DeprecatedDeclaration, as: warning) + func corge() { + let _: Bool = dep() // expected-warning {{'dep()' is deprecated}} + } + } + } +} + +// Nominal types +@warn(DeprecatedDeclaration, as: error) +class Foo { + let x = dep() // expected-error {{'dep()' is deprecated}} +} +@warn(DeprecatedDeclaration, as: ignored) +struct Bar { + let x = dep() +} +@warn(DeprecatedDeclaration, as: warning) +enum Baz { + var x: Bool { dep() } // expected-warning {{'dep()' is deprecated}} +} +@warn(DeprecatedDeclaration, as: error) +actor Qux { + let x = dep() // expected-error {{'dep()' is deprecated}} + @warn(DeprecatedDeclaration, as: ignored) + struct Quux { + let x = dep() + } +} +@warn(DeprecatedDeclaration, as: error) +protocol Proto { + var x: depS { get } // expected-error {{'depS' is deprecated}} +} + +// Initializer +@warn(DeprecatedDeclaration, as: error) +struct Corge : ~Copyable { + let x = dep() // expected-error {{'dep()' is deprecated}} + @warn(DeprecatedDeclaration, as: ignored) + init() { + let _ = dep() + } + @warn(DeprecatedDeclaration, as: warning) + deinit { + let _ = dep() // expected-warning {{'dep()' is deprecated}} + } +} + +// Extension +@warn(DeprecatedDeclaration, as: error) +extension Foo { + var y: Bool { dep() } // expected-error {{'dep()' is deprecated}} +} + +// Import +@warn(PreconcurrencyImport, as: error) +@preconcurrency import Swift // expected-error {{'@preconcurrency' on module 'Swift' has no effect}} +@warn(PreconcurrencyImport, as: ignored) +@preconcurrency import Swift + +// Subscript +struct Hmm { + @warn(DeprecatedDeclaration, as: error) + subscript(index: Int) -> Bool { + return dep() // expected-error {{'dep()' is deprecated}} + } +} + +// Computed property +@warn(DeprecatedDeclaration, as: ignored) +extension Foo { + @warn(DeprecatedDeclaration, as: error) + var property: Bool { + return dep() // expected-error {{'dep()' is deprecated}} + } +} + +// Accessors +@warn(DeprecatedDeclaration, as: ignored) +extension Foo { + var b: Bool { dep() } + var computed_property: Bool { + @warn(DeprecatedDeclaration, as: error) + get { + return dep() // expected-error {{'dep()' is deprecated}} + } + @warn(DeprecatedDeclaration, as: warning) + set { + let _ = dep() // expected-warning {{'dep()' is deprecated}} + } + } +} diff --git a/unittests/AST/DiagnosticGroupsTests.cpp b/unittests/AST/DiagnosticGroupsTests.cpp index d1d0fca77d9c5..eb780472208ff 100644 --- a/unittests/AST/DiagnosticGroupsTests.cpp +++ b/unittests/AST/DiagnosticGroupsTests.cpp @@ -66,9 +66,9 @@ TEST(DiagnosticGroups, TargetAll) { // warnings. testCase( [](DiagnosticEngine &diags) { - const std::vector rules = { - WarningAsErrorRule(WarningAsErrorRule::Action::Enable)}; - diags.setWarningsAsErrorsRules(rules); + llvm::SmallVector rules = { + WarningGroupBehaviorRule(WarningGroupBehavior::AsError)}; + diags.setWarningGroupControlRules(rules); TestDiagnostic diagnostic( diag::warn_unsupported_module_interface_library_evolution.ID, @@ -100,9 +100,10 @@ TEST(DiagnosticGroups, OverrideBehaviorLimitations) { testCase( [&diagnostic](DiagnosticEngine &diags) { - const std::vector rules = {WarningAsErrorRule( - WarningAsErrorRule::Action::Enable, "DeprecatedDeclaration")}; - diags.setWarningsAsErrorsRules(rules); + llvm::SmallVector rules = { + WarningGroupBehaviorRule(WarningGroupBehavior::AsError, + DiagGroupID::DeprecatedDeclaration)}; + diags.setWarningGroupControlRules(rules); diags.diagnose(SourceLoc(), diagnostic); diags.diagnose(SourceLoc(), diagnostic) @@ -133,9 +134,10 @@ TEST(DiagnosticGroups, OverrideBehaviorLimitations) { testCase( [&diagnostic](DiagnosticEngine &diags) { - const std::vector rules = {WarningAsErrorRule( - WarningAsErrorRule::Action::Enable, "DeprecatedDeclaration")}; - diags.setWarningsAsErrorsRules(rules); + llvm::SmallVector rules = { + WarningGroupBehaviorRule(WarningGroupBehavior::AsError, + DiagGroupID::DeprecatedDeclaration)}; + diags.setWarningGroupControlRules(rules); diags.diagnose(SourceLoc(), diagnostic) .limitBehaviorUntilSwiftVersion(DiagnosticBehavior::Warning, 99);