Skip to content

Commit 3a5fe80

Browse files
committed
Parse: Fix parsing of canImport() version arguments with more than two components.
Resolves: rdar://97990159
1 parent 1a611e9 commit 3a5fe80

File tree

4 files changed

+192
-39
lines changed

4 files changed

+192
-39
lines changed

include/swift/AST/DiagnosticsParse.def

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1851,11 +1851,20 @@ WARNING(likely_simulator_platform_condition,none,
18511851
"use 'targetEnvironment(simulator)' instead",
18521852
())
18531853

1854-
ERROR(canimport_two_parameters, none, "canImport can take only two parameters", ())
1855-
1856-
ERROR(canimport_label, none, "2nd parameter of canImport should be labeled as _version or _underlyingVersion", ())
1857-
1858-
ERROR(canimport_version, none, "cannot parse module version: %0", (StringRef))
1854+
ERROR(canimport_two_parameters,none,
1855+
"canImport can take only two parameters", ())
1856+
ERROR(canimport_label,none,
1857+
"2nd parameter of canImport should be labeled as "
1858+
"_version or _underlyingVersion", ())
1859+
ERROR(canimport_invalid_version,none,
1860+
"cannot parse module version '%0'",
1861+
(StringRef))
1862+
ERROR(canimport_empty_version,none,
1863+
"%0 argument cannot be empty",
1864+
(StringRef))
1865+
WARNING(canimport_version_too_many_components,none,
1866+
"trailing components of version '%0' are ignored",
1867+
(StringRef))
18591868

18601869
//------------------------------------------------------------------------------
18611870
// MARK: Availability query parsing diagnostics

lib/Parse/ParseIfConfig.cpp

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ static bool isValidVersion(const version::Version &Version,
7777
}
7878

7979
static llvm::VersionTuple getCanImportVersion(ArgumentList *args,
80+
SourceManager &SM,
8081
DiagnosticEngine *D,
8182
bool &underlyingVersion) {
8283
llvm::VersionTuple result;
@@ -99,16 +100,37 @@ static llvm::VersionTuple getCanImportVersion(ArgumentList *args,
99100
return result;
100101
}
101102
StringRef verText;
102-
if (auto *nle = dyn_cast<NumberLiteralExpr>(subE)) {
103-
verText = nle->getDigitsText();
104-
} else if (auto *sle= dyn_cast<StringLiteralExpr>(subE)) {
103+
if (auto *sle = dyn_cast<StringLiteralExpr>(subE)) {
105104
verText = sle->getValue();
105+
} else {
106+
// Use the raw text for every non-string-literal expression. Versions with
107+
// just two components are parsed as number literals, but versions with more
108+
// components are parsed as unresolved dot expressions.
109+
verText = extractExprSource(SM, subE);
106110
}
107-
if (verText.empty())
111+
112+
if (verText.empty()) {
113+
if (D) {
114+
D->diagnose(subE->getLoc(), diag::canimport_empty_version, label.str());
115+
}
108116
return result;
117+
}
118+
119+
// VersionTuple supports a maximum of 4 components.
120+
ssize_t excessComponents = verText.count('.') - 3;
121+
if (excessComponents > 0) {
122+
do {
123+
verText = verText.rsplit('.').first;
124+
} while (--excessComponents > 0);
125+
if (D) {
126+
D->diagnose(subE->getLoc(), diag::canimport_version_too_many_components,
127+
verText);
128+
}
129+
}
130+
109131
if (result.tryParse(verText)) {
110132
if (D) {
111-
D->diagnose(subE->getLoc(), diag::canimport_version, verText);
133+
D->diagnose(subE->getLoc(), diag::canimport_invalid_version, verText);
112134
}
113135
}
114136
return result;
@@ -122,12 +144,8 @@ static Expr *getSingleSubExp(ArgumentList *args, StringRef kindName,
122144
if (auto *unary = args->getUnlabeledUnaryExpr())
123145
return unary;
124146

147+
// canImport() has an optional second parameter.
125148
if (kindName == "canImport") {
126-
bool underlyingVersion;
127-
if (D) {
128-
// Diagnose canImport syntax
129-
(void)getCanImportVersion(args, D, underlyingVersion);
130-
}
131149
return args->getExpr(0);
132150
}
133151
return nullptr;
@@ -328,6 +346,13 @@ class ValidateIfConfigCondition :
328346
}
329347

330348
if (*KindName == "canImport") {
349+
if (!E->getArgs()->isUnary()) {
350+
bool underlyingVersion;
351+
// Diagnose canImport(_:_version:) syntax.
352+
(void)getCanImportVersion(E->getArgs(), Ctx.SourceMgr, &D,
353+
underlyingVersion);
354+
}
355+
331356
if (!isModulePath(Arg)) {
332357
D.diagnose(E->getLoc(), diag::unsupported_platform_condition_argument,
333358
"module name");
@@ -566,7 +591,8 @@ class EvaluateIfConfigCondition :
566591
bool underlyingModule = false;
567592
llvm::VersionTuple version;
568593
if (!E->getArgs()->isUnlabeledUnary()) {
569-
version = getCanImportVersion(E->getArgs(), nullptr, underlyingModule);
594+
version = getCanImportVersion(E->getArgs(), Ctx.SourceMgr, nullptr,
595+
underlyingModule);
570596
}
571597
ImportPath::Module::Builder builder(Ctx, Str, /*separator=*/'.',
572598
Arg->getStartLoc());

test/Parse/ifconfig_expr.swift

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,18 @@ func canImportVersioned() {
134134
#if canImport(A, _version: 2.2)
135135
let a = 1
136136
#endif
137+
138+
#if canImport(A, _version: 2.2.2)
139+
let a = 1
140+
#endif
141+
142+
#if canImport(A, _version: 2.2.2.2)
143+
let a = 1
144+
#endif
145+
146+
#if canImport(A, _version: 2.2.2.2.2) // expected-warning {{trailing components of version '2.2.2.2' are ignored}}
147+
let a = 1
148+
#endif
137149

138150
#if canImport(A, _underlyingVersion: 4)
139151
let a = 1
@@ -142,16 +154,48 @@ func canImportVersioned() {
142154
#if canImport(A, _underlyingVersion: 2.200)
143155
let a = 1
144156
#endif
157+
158+
#if canImport(A, _underlyingVersion: 2.200.1)
159+
let a = 1
160+
#endif
161+
162+
#if canImport(A, _underlyingVersion: 2.200.1.3)
163+
let a = 1
164+
#endif
145165

146166
#if canImport(A, unknown: 2.2) // expected-error {{2nd parameter of canImport should be labeled as _version or _underlyingVersion}}
147167
let a = 1
148168
#endif
149169

170+
#if canImport(A,) // expected-error {{unexpected ',' separator}}
171+
let a = 1
172+
#endif
173+
150174
#if canImport(A, 2.2) // expected-error {{2nd parameter of canImport should be labeled as _version or _underlyingVersion}}
151175
let a = 1
152176
#endif
153177

154178
#if canImport(A, 2.2, 1.1) // expected-error {{canImport can take only two parameters}}
155179
let a = 1
156180
#endif
181+
182+
#if canImport(A, _version:) // expected-error {{expected expression in list of expressions}}
183+
let a = 1
184+
#endif
185+
186+
#if canImport(A, _version: "") // expected-error {{_version argument cannot be empty}}
187+
let a = 1
188+
#endif
189+
190+
#if canImport(A, _version: >=2.2) // expected-error {{cannot parse module version '>=2.2'}}
191+
let a = 1
192+
#endif
193+
194+
#if canImport(A, _version: 20A301) // expected-error {{'A' is not a valid digit in integer literal}}
195+
let a = 1
196+
#endif
197+
198+
#if canImport(A, _version: "20A301") // expected-error {{cannot parse module version '20A301'}}
199+
let a = 1
200+
#endif
157201
}

test/Parse/versioned_canimport.swift

Lines changed: 97 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -20,57 +20,131 @@ import Bar
2020
#endif
2121

2222
func canImportVersioned() {
23-
#if canImport(Foo, _version: 113.331)
24-
let a = 1
23+
#if canImport(Foo, _version: 0)
24+
let majorZero = 1 // expected-warning {{initialization of immutable value 'majorZero' was never used; consider replacing with assignment to '_' or removing it}}
2525
#endif
2626

27-
#if canImport(Foo, _version: 113.3000)
28-
let b = 1
27+
#if canImport(Foo, _version: 112)
28+
let majorSmaller = 1 // expected-warning {{initialization of immutable value 'majorSmaller' was never used; consider replacing with assignment to '_' or removing it}}
29+
#endif
30+
31+
#if canImport(Foo, _version: 113)
32+
let majorEqual = 1 // expected-warning {{initialization of immutable value 'majorEqual' was never used; consider replacing with assignment to '_' or removing it}}
2933
#endif
3034

3135
#if canImport(Foo, _version: 114)
32-
let c = 1
36+
let majorLarger = 1
37+
#endif
38+
39+
#if canImport(Foo, _version: 113.329)
40+
let minorSmaller = 1 // expected-warning {{initialization of immutable value 'minorSmaller' was never used; consider replacing with assignment to '_' or removing it}}
3341
#endif
3442

35-
#if canImport(Foo, _version: 4)
36-
let d = 1 // expected-warning {{initialization of immutable value 'd' was never used; consider replacing with assignment to '_' or removing it}}
43+
#if canImport(Foo, _version: 113.330)
44+
let minorEqual = 1 // expected-warning {{initialization of immutable value 'minorEqual' was never used; consider replacing with assignment to '_' or removing it}}
3745
#endif
3846

39-
#if canImport(Foo, _version: 113.33)
40-
let e = 1 // expected-warning {{initialization of immutable value 'e' was never used; consider replacing with assignment to '_' or removing it}}
47+
#if canImport(Foo, _version: 113.331)
48+
let minorLarger = 1
49+
#endif
50+
51+
#if canImport(Foo, _version: 113.330.0)
52+
let patchSmaller = 1 // expected-warning {{initialization of immutable value 'patchSmaller' was never used; consider replacing with assignment to '_' or removing it}}
4153
#endif
4254

43-
#if canImport(Foo, _underlyingVersion: 113.33)
44-
let ee = 1
55+
#if canImport(Foo, _version: 113.330.1)
56+
let patchEqual = 1 // expected-warning {{initialization of immutable value 'patchEqual' was never used; consider replacing with assignment to '_' or removing it}}
57+
#endif
58+
59+
#if canImport(Foo, _version: 113.330.2)
60+
let patchLarger = 1
4561
#endif
4662

47-
#if canImport(Foo, _version: 113.329)
48-
let f = 1 // expected-warning {{initialization of immutable value 'f' was never used; consider replacing with assignment to '_' or removing it}}
63+
#if canImport(Foo, _version: 113.330.1.1)
64+
let buildSmaller = 1 // expected-warning {{initialization of immutable value 'buildSmaller' was never used; consider replacing with assignment to '_' or removing it}}
4965
#endif
5066

51-
#if canImport(Foo, _version: 113.330)
52-
let g = 1 // expected-warning {{initialization of immutable value 'g' was never used; consider replacing with assignment to '_' or removing it}}
67+
#if canImport(Foo, _version: 113.330.1.2)
68+
let buildEqual = 1 // expected-warning {{initialization of immutable value 'buildEqual' was never used; consider replacing with assignment to '_' or removing it}}
69+
#endif
70+
71+
#if canImport(Foo, _version: 113.330.1.3)
72+
let buildLarger = 1
73+
#endif
74+
75+
#if canImport(Foo, _version: 113.330.1.2.0) // expected-warning {{trailing components of version '113.330.1.2' are ignored}}
76+
let extraComponent = 1 // expected-warning {{initialization of immutable value 'extraComponent' was never used; consider replacing with assignment to '_' or removing it}}
77+
#endif
78+
79+
#if canImport(Foo, _underlyingVersion: 113.33)
80+
// Foo is a Swift module with no underlying clang module.
81+
let underlyingMinorSmaller = 1
5382
#endif
5483

5584
#if canImport(Foo)
56-
let h = 1 // expected-warning {{initialization of immutable value 'h' was never used; consider replacing with assignment to '_' or removing it}}
85+
let noVersion = 1 // expected-warning {{initialization of immutable value 'noVersion' was never used; consider replacing with assignment to '_' or removing it}}
5786
#endif
5887
}
5988

6089
func canImportVersionedString() {
90+
#if canImport(Foo, _version: "0")
91+
let majorZero = 1 // expected-warning {{initialization of immutable value 'majorZero' was never used; consider replacing with assignment to '_' or removing it}}
92+
#endif
93+
94+
#if canImport(Foo, _version: "112")
95+
let majorSmaller = 1 // expected-warning {{initialization of immutable value 'majorSmaller' was never used; consider replacing with assignment to '_' or removing it}}
96+
#endif
97+
98+
#if canImport(Foo, _version: "113")
99+
let majorEqual = 1 // expected-warning {{initialization of immutable value 'majorEqual' was never used; consider replacing with assignment to '_' or removing it}}
100+
#endif
101+
102+
#if canImport(Foo, _version: "114")
103+
let majorLarger = 1
104+
#endif
105+
106+
#if canImport(Foo, _version: "113.329")
107+
let minorSmaller = 1 // expected-warning {{initialization of immutable value 'minorSmaller' was never used; consider replacing with assignment to '_' or removing it}}
108+
#endif
109+
110+
#if canImport(Foo, _version: "113.330")
111+
let minorEqual = 1 // expected-warning {{initialization of immutable value 'minorEqual' was never used; consider replacing with assignment to '_' or removing it}}
112+
#endif
113+
61114
#if canImport(Foo, _version: "113.331")
62-
let a = 1
115+
let minorLarger = 1
116+
#endif
117+
118+
#if canImport(Foo, _version: "113.330.0")
119+
let patchSmaller = 1 // expected-warning {{initialization of immutable value 'patchSmaller' was never used; consider replacing with assignment to '_' or removing it}}
120+
#endif
121+
122+
#if canImport(Foo, _version: "113.330.1")
123+
let patchEqual = 1 // expected-warning {{initialization of immutable value 'patchEqual' was never used; consider replacing with assignment to '_' or removing it}}
124+
#endif
125+
126+
#if canImport(Foo, _version: "113.330.2")
127+
let patchLarger = 1
63128
#endif
64129

65-
#if canImport(Foo, _version: "113.3000")
66-
let b = 1
130+
#if canImport(Foo, _version: "113.330.1.1")
131+
let buildSmaller = 1 // expected-warning {{initialization of immutable value 'buildSmaller' was never used; consider replacing with assignment to '_' or removing it}}
67132
#endif
68133

69-
#if canImport(Foo, _version: "4")
70-
let d = 1 // expected-warning {{initialization of immutable value 'd' was never used; consider replacing with assignment to '_' or removing it}}
134+
#if canImport(Foo, _version: "113.330.1.2")
135+
let buildEqual = 1 // expected-warning {{initialization of immutable value 'buildEqual' was never used; consider replacing with assignment to '_' or removing it}}
136+
#endif
137+
138+
#if canImport(Foo, _version: "113.330.1.3")
139+
let buildLarger = 1
140+
#endif
141+
142+
#if canImport(Foo, _version: "113.330.1.2.0") // expected-warning {{trailing components of version '113.330.1.2' are ignored}}
143+
let extraComponent = 1 // expected-warning {{initialization of immutable value 'extraComponent' was never used; consider replacing with assignment to '_' or removing it}}
71144
#endif
72145

73-
#if canImport(Foo, _version: "113.33")
74-
let e = 1 // expected-warning {{initialization of immutable value 'e' was never used; consider replacing with assignment to '_' or removing it}}
146+
#if canImport(Foo, _underlyingVersion: 113.33)
147+
// Foo is a Swift module with no underlying clang module.
148+
let underlyingMinorSmaller = 1
75149
#endif
76150
}

0 commit comments

Comments
 (0)