Skip to content

Commit df2b971

Browse files
committed
[CodeCompletion] Add "soft deprected" diagnostics for code completion
'available(..., deprecated: <version>)'. If the version is larger than the current active version, the declaration is "soft deprecated". Emit a relevant diagnostics for those items. If the version is equal to or larger than '100000.0', it means the distant future without specifying the version. rdar://76122625
1 parent ed1db1b commit df2b971

File tree

8 files changed

+137
-29
lines changed

8 files changed

+137
-29
lines changed

include/swift/AST/Attr.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2088,6 +2088,10 @@ class DeclAttributes {
20882088
/// a declaration is deprecated on all deployment targets, or null otherwise.
20892089
const AvailableAttr *getDeprecated(const ASTContext &ctx) const;
20902090

2091+
/// Returns the first @available attribute that indicates
2092+
/// a declaration will be deprecated in the future, or null otherwise.
2093+
const AvailableAttr *getSoftDeprecated(const ASTContext &ctx) const;
2094+
20912095
SWIFT_DEBUG_DUMPER(dump(const Decl *D = nullptr));
20922096
void print(ASTPrinter &Printer, const PrintOptions &Options,
20932097
const Decl *D = nullptr) const;

include/swift/AST/DiagnosticsIDE.def

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,19 @@ WARNING(ide_redundant_import, none,
3333
NOTE(ide_redundant_import_indirect, none,
3434
"module %0 is already imported via another module import", (DeclName))
3535

36+
WARNING(ide_availability_softdeprecated, Deprecation,
37+
"%select{getter for |setter for |}0%1 will be deprecated"
38+
" in %select{a future version|%select{a future version of %3|%3 %5}4}2"
39+
"%select{|: %6}6",
40+
(unsigned, DeclName, bool, StringRef, bool, llvm::VersionTuple,
41+
StringRef))
42+
43+
WARNING(ide_availability_softdeprecated_rename, Deprecation,
44+
"%select{getter for |setter for |}0%1 will be deprecated"
45+
" in %select{a future version|%select{a future version of %3|%3 %5}4}2"
46+
": renamed to '%6'",
47+
(unsigned, DeclName, bool, StringRef, bool, llvm::VersionTuple, StringRef))
48+
3649
//===----------------------------------------------------------------------===//
3750

3851
#define UNDEFINE_DIAGNOSTIC_MACROS

include/swift/IDE/CodeCompletion.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,7 @@ class CodeCompletionResult {
617617
RedundantImport,
618618
RedundantImportIndirect,
619619
Deprecated,
620+
SoftDeprecated,
620621
InvalidAsyncContext,
621622
CrossActorReference,
622623
VariableUsedInOwnDefinition,

lib/AST/Attr.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,38 @@ DeclAttributes::getDeprecated(const ASTContext &ctx) const {
326326
return conditional;
327327
}
328328

329+
const AvailableAttr *
330+
DeclAttributes::getSoftDeprecated(const ASTContext &ctx) const {
331+
const AvailableAttr *conditional = nullptr;
332+
const AvailableAttr *bestActive = findMostSpecificActivePlatform(ctx);
333+
for (auto Attr : *this) {
334+
if (auto AvAttr = dyn_cast<AvailableAttr>(Attr)) {
335+
if (AvAttr->isInvalid())
336+
continue;
337+
338+
if (AvAttr->hasPlatform() &&
339+
(!bestActive || AvAttr != bestActive))
340+
continue;
341+
342+
if (!AvAttr->isActivePlatform(ctx) &&
343+
!AvAttr->isLanguageVersionSpecific() &&
344+
!AvAttr->isPackageDescriptionVersionSpecific())
345+
continue;
346+
347+
Optional<llvm::VersionTuple> DeprecatedVersion = AvAttr->Deprecated;
348+
if (!DeprecatedVersion.hasValue())
349+
continue;
350+
351+
llvm::VersionTuple ActiveVersion = AvAttr->getActiveVersion(ctx);
352+
353+
if (DeprecatedVersion.getValue() > ActiveVersion) {
354+
conditional = AvAttr;
355+
}
356+
}
357+
}
358+
return conditional;
359+
}
360+
329361
void DeclAttributes::dump(const Decl *D) const {
330362
StreamPrinter P(llvm::errs());
331363
PrintOptions PO = PrintOptions::printEverything();

lib/IDE/CodeCompletion.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -858,6 +858,8 @@ void CodeCompletionResultBuilder::setAssociatedDecl(const Decl *D) {
858858

859859
if (D->getAttrs().getDeprecated(D->getASTContext()))
860860
setNotRecommended(NotRecommendedReason::Deprecated);
861+
else if (D->getAttrs().getSoftDeprecated(D->getASTContext()))
862+
setNotRecommended(NotRecommendedReason::SoftDeprecated);
861863
}
862864

863865
namespace {

lib/IDE/CodeCompletionDiagnostics.cpp

Lines changed: 55 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,11 @@ CodeCompletionDiagnosticSeverity getSeverity(DiagnosticKind DiagKind) {
4242
}
4343

4444
class CodeCompletionDiagnostics {
45+
ASTContext &Ctx;
4546
DiagnosticEngine &Engine;
4647

4748
public:
48-
CodeCompletionDiagnostics(ASTContext &Ctx) : Engine(Ctx.Diags) {}
49+
CodeCompletionDiagnostics(ASTContext &Ctx) : Ctx(Ctx), Engine(Ctx.Diags) {}
4950

5051
template <typename... ArgTypes>
5152
bool
@@ -75,7 +76,12 @@ bool CodeCompletionDiagnostics::getDiagnostics(
7576
bool CodeCompletionDiagnostics::getDiagnosticForDeprecated(
7677
const ValueDecl *D, CodeCompletionDiagnosticSeverity &severity,
7778
llvm::raw_ostream &Out) {
78-
const AvailableAttr *Attr = D->getAttrs().getDeprecated(D->getASTContext());
79+
bool isSoftDeprecated = false;
80+
const AvailableAttr *Attr = D->getAttrs().getDeprecated(Ctx);
81+
if (!Attr) {
82+
Attr = D->getAttrs().getSoftDeprecated(Ctx);
83+
isSoftDeprecated = true;
84+
}
7985
if (!Attr)
8086
return true;
8187

@@ -91,22 +97,47 @@ bool CodeCompletionDiagnostics::getDiagnosticForDeprecated(
9197
if (Attr->Deprecated)
9298
DeprecatedVersion = Attr->Deprecated.getValue();
9399

94-
if (Attr->Message.empty() && Attr->Rename.empty()) {
95-
getDiagnostics(severity, Out, diag::availability_deprecated,
96-
RawAccessorKind, Name, Attr->hasPlatform(), Platform,
97-
Attr->Deprecated.hasValue(), DeprecatedVersion,
98-
/*message*/ StringRef());
99-
} else if (!Attr->Message.empty()) {
100-
EncodedDiagnosticMessage EncodedMessage(Attr->Message);
101-
getDiagnostics(severity, Out, diag::availability_deprecated,
102-
RawAccessorKind, Name, Attr->hasPlatform(), Platform,
103-
Attr->Deprecated.hasValue(), DeprecatedVersion,
104-
EncodedMessage.Message);
100+
if (!isSoftDeprecated) {
101+
if (Attr->Message.empty() && Attr->Rename.empty()) {
102+
getDiagnostics(severity, Out, diag::availability_deprecated,
103+
RawAccessorKind, Name, Attr->hasPlatform(), Platform,
104+
Attr->Deprecated.hasValue(), DeprecatedVersion,
105+
/*message*/ StringRef());
106+
} else if (!Attr->Message.empty()) {
107+
EncodedDiagnosticMessage EncodedMessage(Attr->Message);
108+
getDiagnostics(severity, Out, diag::availability_deprecated,
109+
RawAccessorKind, Name, Attr->hasPlatform(), Platform,
110+
Attr->Deprecated.hasValue(), DeprecatedVersion,
111+
EncodedMessage.Message);
112+
} else {
113+
getDiagnostics(severity, Out, diag::availability_deprecated_rename,
114+
RawAccessorKind, Name, Attr->hasPlatform(), Platform,
115+
Attr->Deprecated.hasValue(), DeprecatedVersion, false,
116+
/*ReplaceKind*/ 0, Attr->Rename);
117+
}
105118
} else {
106-
getDiagnostics(severity, Out, diag::availability_deprecated_rename,
107-
RawAccessorKind, Name, Attr->hasPlatform(), Platform,
108-
Attr->Deprecated.hasValue(), DeprecatedVersion, false,
109-
/*ReplaceKind*/ 0, Attr->Rename);
119+
// '100000' is used as a version number in API that will be deprecated in an
120+
// upcoming release. This number is to match the 'API_TO_BE_DEPRECATED'
121+
// macro defined in Darwin platforms.
122+
static llvm::VersionTuple DISTANT_FUTURE_VESION(100000);
123+
bool isDistantFuture = DeprecatedVersion >= DISTANT_FUTURE_VESION;
124+
125+
if (Attr->Message.empty() && Attr->Rename.empty()) {
126+
getDiagnostics(severity, Out, diag::ide_availability_softdeprecated,
127+
RawAccessorKind, Name, Attr->hasPlatform(), Platform,
128+
!isDistantFuture, DeprecatedVersion,
129+
/*message*/ StringRef());
130+
} else if (!Attr->Message.empty()) {
131+
EncodedDiagnosticMessage EncodedMessage(Attr->Message);
132+
getDiagnostics(severity, Out, diag::ide_availability_softdeprecated,
133+
RawAccessorKind, Name, Attr->hasPlatform(), Platform,
134+
!isDistantFuture, DeprecatedVersion,
135+
EncodedMessage.Message);
136+
} else {
137+
getDiagnostics(severity, Out, diag::ide_availability_softdeprecated_rename,
138+
RawAccessorKind, Name, Attr->hasPlatform(), Platform,
139+
!isDistantFuture, DeprecatedVersion, Attr->Rename);
140+
}
110141
}
111142
return false;;
112143
}
@@ -123,32 +154,27 @@ bool swift::ide::getCompletionDiagnostics(
123154
CodeCompletionDiagnostics Diag(ctx);
124155
switch (reason) {
125156
case NotRecommendedReason::Deprecated:
157+
case NotRecommendedReason::SoftDeprecated:
126158
return Diag.getDiagnosticForDeprecated(D, severity, Out);
127-
break;
128159
case NotRecommendedReason::InvalidAsyncContext:
129160
// FIXME: Could we use 'diag::async_in_nonasync_function'?
130-
Diag.getDiagnostics(severity, Out, diag::ide_async_in_nonasync_context,
161+
return Diag.getDiagnostics(severity, Out, diag::ide_async_in_nonasync_context,
131162
D->getName());
132-
break;
133163
case NotRecommendedReason::CrossActorReference:
134-
Diag.getDiagnostics(severity, Out, diag::ide_cross_actor_reference_swift5,
164+
return Diag.getDiagnostics(severity, Out, diag::ide_cross_actor_reference_swift5,
135165
D->getName());
136-
break;
137166
case NotRecommendedReason::RedundantImport:
138-
Diag.getDiagnostics(severity, Out, diag::ide_redundant_import,
167+
return Diag.getDiagnostics(severity, Out, diag::ide_redundant_import,
139168
D->getName());
140-
break;
141169
case NotRecommendedReason::RedundantImportIndirect:
142-
Diag.getDiagnostics(severity, Out, diag::ide_redundant_import_indirect,
170+
return Diag.getDiagnostics(severity, Out, diag::ide_redundant_import_indirect,
143171
D->getName());
144-
break;
145172
case NotRecommendedReason::VariableUsedInOwnDefinition:
146-
Diag.getDiagnostics(severity, Out, diag::recursive_accessor_reference,
173+
return Diag.getDiagnostics(severity, Out, diag::recursive_accessor_reference,
147174
D->getName().getBaseIdentifier(), /*"getter"*/ 0);
148-
break;
149175
case NotRecommendedReason::None:
150176
llvm_unreachable("invalid not recommended reason");
151177
}
152-
return false;
178+
return true;
153179
}
154180

lib/IDE/CodeCompletionDiagnostics.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ class ValueDecl;
2121

2222
namespace ide {
2323

24+
/// Populate \p severity and \p Out with the diagnostics for \p D.
25+
/// Returns \c true if it fails to generate the diagnostics.
2426
bool getCompletionDiagnostics(CodeCompletionResult::NotRecommendedReason reason,
2527
const ValueDecl *D,
2628
CodeCompletionDiagnosticSeverity &severity,

test/IDE/complete_diagnostics.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,27 @@ public struct Foo {
4848

4949
@available(swift, deprecated: 3.2)
5050
public var deprecatedVar: Int { 1 }
51+
52+
@available(macOS, deprecated: 987.6)
53+
public func softDeprecatedPlatformVersion() {}
54+
55+
@available(macOS, deprecated: 765.5, message: "this is a \"message\"")
56+
public func softDeprecatedPlatformVersionMessage() {}
57+
58+
@available(swift, deprecated: 1000.1)
59+
public var softDeprecatedVar: Int { 1 }
60+
61+
@available(macOS, deprecated: 654.3, renamed: "renamedName")
62+
public func softDeprecatedPlatformVersionRenamed() {}
63+
64+
@available(macOS, deprecated: 100000.0)
65+
public func futureDeprecatedPlatformVersion() {}
66+
67+
@available(macOS, deprecated: 100000.0, message: "this is a \"message\"")
68+
public func futureDeprecatedPlatformVersionMessage() {}
69+
70+
@available(macOS, deprecated: 100000.0, renamed: "renamedName")
71+
public func futureDeprecatedPlatformVersionRenamed() {}
5172
}
5273

5374
// BEGIN App.swift
@@ -71,6 +92,13 @@ func test(foo: Foo) {
7192
// MEMBER-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: deprecatedPlatformVersionRenamed()[#Void#]; name=deprecatedPlatformVersionRenamed(); diagnostics=warning:'deprecatedPlatformVersionRenamed()' was deprecated in macOS 10.4: renamed to 'renamedName'{{$}}
7293
// MEMBER-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: deprecatedPlatformVersionMessageRenamed()[#Void#]; name=deprecatedPlatformVersionMessageRenamed(); diagnostics=warning:'deprecatedPlatformVersionMessageRenamed()' was deprecated in macOS 10.4: this is a message{{$}}
7394
// MEMBER-DAG: Decl[InstanceVar]/CurrNominal/NotRecommended: deprecatedVar[#Int#]; name=deprecatedVar; diagnostics=warning:'deprecatedVar' is deprecated{{$}}
95+
// MEMBER-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: softDeprecatedPlatformVersion()[#Void#]; name=softDeprecatedPlatformVersion(); diagnostics=warning:'softDeprecatedPlatformVersion()' will be deprecated in macOS 987.6{{$}}
96+
// MEMBER-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: softDeprecatedPlatformVersionMessage()[#Void#]; name=softDeprecatedPlatformVersionMessage(); diagnostics=warning:'softDeprecatedPlatformVersionMessage()' will be deprecated in macOS 765.5: this is a "message"{{$}}
97+
// MEMBER-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: softDeprecatedPlatformVersionRenamed()[#Void#]; name=softDeprecatedPlatformVersionRenamed(); diagnostics=warning:'softDeprecatedPlatformVersionRenamed()' will be deprecated in macOS 654.3: renamed to 'renamedName'{{$}}
98+
// MEMBER-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: futureDeprecatedPlatformVersion()[#Void#]; name=futureDeprecatedPlatformVersion(); diagnostics=warning:'futureDeprecatedPlatformVersion()' will be deprecated in a future version of macOS{{$}}
99+
// MEMBER-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: futureDeprecatedPlatformVersionMessage()[#Void#]; name=futureDeprecatedPlatformVersionMessage(); diagnostics=warning:'futureDeprecatedPlatformVersionMessage()' will be deprecated in a future version of macOS: this is a "message"{{$}}
100+
// MEMBER-DAG: Decl[InstanceMethod]/CurrNominal/NotRecommended: futureDeprecatedPlatformVersionRenamed()[#Void#]; name=futureDeprecatedPlatformVersionRenamed(); diagnostics=warning:'futureDeprecatedPlatformVersionRenamed()' will be deprecated in a future version of macOS: renamed to 'renamedName'{{$}}
101+
// MEMBER-DAG: Decl[InstanceVar]/CurrNominal/NotRecommended: softDeprecatedVar[#Int#]; name=softDeprecatedVar; diagnostics=warning:'softDeprecatedVar' will be deprecated in a future version{{$}}
74102
// MEMBER: End completions
75103
}
76104

0 commit comments

Comments
 (0)