Skip to content

Commit 7e7d1db

Browse files
authored
Merge pull request #2538 from IngmarStein/master
2 parents 4ba74a2 + 53d262b commit 7e7d1db

File tree

8 files changed

+351
-145
lines changed

8 files changed

+351
-145
lines changed

lib/ClangImporter/ImportMacro.cpp

Lines changed: 157 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,29 @@ static bool isInSystemModule(DeclContext *D) {
7070
return false;
7171
}
7272

73+
static ValueDecl *
74+
createMacroConstant(ClangImporter::Implementation &Impl,
75+
const clang::MacroInfo *macro,
76+
Identifier name,
77+
DeclContext *dc,
78+
Type type,
79+
const clang::APValue &value,
80+
ConstantConvertKind convertKind,
81+
bool isStatic,
82+
ClangNode ClangN) {
83+
Impl.ImportedMacroConstants[macro] = {value, type};
84+
return Impl.createConstant(name, dc, type, value, convertKind, isStatic,
85+
ClangN);
86+
}
87+
7388
static ValueDecl *importNumericLiteral(ClangImporter::Implementation &Impl,
7489
DeclContext *DC,
7590
const clang::MacroInfo *MI,
7691
Identifier name,
7792
const clang::Token *signTok,
7893
const clang::Token &tok,
79-
const clang::MacroInfo *ClangN) {
94+
const clang::MacroInfo *ClangN,
95+
const clang::QualType *castType) {
8096
assert(tok.getKind() == clang::tok::numeric_constant &&
8197
"not a numeric token");
8298
{
@@ -96,12 +112,21 @@ static ValueDecl *importNumericLiteral(ClangImporter::Implementation &Impl,
96112

97113
if (const clang::Expr *parsed = parseNumericLiteral<>(Impl, tok)) {
98114
auto clangTy = parsed->getType();
99-
auto type = Impl.importType(clangTy, ImportTypeKind::Value,
100-
isInSystemModule(DC),
101-
/*isFullyBridgeable*/false);
102-
if (!type)
115+
auto literalType = Impl.importType(clangTy, ImportTypeKind::Value,
116+
isInSystemModule(DC),
117+
/*isFullyBridgeable*/false);
118+
if (!literalType)
103119
return nullptr;
104120

121+
Type constantType;
122+
if (castType) {
123+
constantType = Impl.importType(*castType, ImportTypeKind::Value,
124+
isInSystemModule(DC),
125+
/*isFullyBridgeable*/false);
126+
} else {
127+
constantType = literalType;
128+
}
129+
105130
if (auto *integer = dyn_cast<clang::IntegerLiteral>(parsed)) {
106131
// Determine the value.
107132
llvm::APSInt value{integer->getValue(), clangTy->isUnsignedIntegerType()};
@@ -117,7 +142,8 @@ static ValueDecl *importNumericLiteral(ClangImporter::Implementation &Impl,
117142
}
118143
}
119144

120-
return Impl.createConstant(name, DC, type, clang::APValue(value),
145+
return createMacroConstant(Impl, MI, name, DC, constantType,
146+
clang::APValue(value),
121147
ConstantConvertKind::Coerce,
122148
/*static*/ false, ClangN);
123149
}
@@ -134,7 +160,8 @@ static ValueDecl *importNumericLiteral(ClangImporter::Implementation &Impl,
134160
value.changeSign();
135161
}
136162

137-
return Impl.createConstant(name, DC, type, clang::APValue(value),
163+
return createMacroConstant(Impl, MI, name, DC, constantType,
164+
clang::APValue(value),
138165
ConstantConvertKind::Coerce,
139166
/*static*/ false, ClangN);
140167
}
@@ -148,6 +175,13 @@ static bool isStringToken(const clang::Token &tok) {
148175
tok.is(clang::tok::utf8_string_literal);
149176
}
150177

178+
static bool isBinaryOperator(const clang::Token &tok) {
179+
return tok.is(clang::tok::amp) ||
180+
tok.is(clang::tok::pipe) ||
181+
tok.is(clang::tok::ampamp) ||
182+
tok.is(clang::tok::pipepipe);
183+
}
184+
151185
// Describes the kind of string literal we're importing.
152186
enum class MappedStringLiteralKind {
153187
CString, // "string"
@@ -191,11 +225,12 @@ static ValueDecl *importLiteral(ClangImporter::Implementation &Impl,
191225
const clang::MacroInfo *MI,
192226
Identifier name,
193227
const clang::Token &tok,
194-
const clang::MacroInfo *ClangN) {
228+
const clang::MacroInfo *ClangN,
229+
const clang::QualType *castType = nullptr) {
195230
switch (tok.getKind()) {
196231
case clang::tok::numeric_constant:
197232
return importNumericLiteral(Impl, DC, MI, name, /*signTok*/nullptr, tok,
198-
ClangN);
233+
ClangN, castType);
199234

200235
case clang::tok::string_literal:
201236
case clang::tok::utf8_string_literal:
@@ -228,7 +263,8 @@ static ValueDecl *importMacro(ClangImporter::Implementation &impl,
228263
DeclContext *DC,
229264
Identifier name,
230265
const clang::MacroInfo *macro,
231-
const clang::MacroInfo *ClangN) {
266+
const clang::MacroInfo *ClangN,
267+
clang::QualType *castType = nullptr) {
232268
if (name.empty()) return nullptr;
233269

234270
auto numTokens = macro->getNumTokens();
@@ -243,17 +279,55 @@ static ValueDecl *importMacro(ClangImporter::Implementation &impl,
243279
numTokens -= 2;
244280
}
245281

282+
// Handle tokens starting with a type cast
283+
bool castTypeIsId = false;
284+
clang::QualType castClangType;
285+
if (numTokens > 3 &&
286+
tokenI[0].is(clang::tok::l_paren) &&
287+
tokenI[1].is(clang::tok::identifier) &&
288+
tokenI[2].is(clang::tok::r_paren)) {
289+
if (castType) {
290+
// this is a nested cast
291+
return nullptr;
292+
}
293+
294+
auto identifierInfo = tokenI[1].getIdentifierInfo();
295+
if (identifierInfo->isStr("id")) {
296+
castTypeIsId = true;
297+
}
298+
auto identifierName = identifierInfo->getName();
299+
auto &identifier = impl.getClangASTContext().Idents.get(identifierName);
300+
auto parsedType = impl.getClangSema().getTypeName(identifier,
301+
clang::SourceLocation(),
302+
/*scope*/nullptr);
303+
if (parsedType) {
304+
castClangType = parsedType.get();
305+
castType = &castClangType;
306+
} else {
307+
return nullptr;
308+
}
309+
tokenI += 3;
310+
numTokens -= 3;
311+
}
312+
246313
// FIXME: Ask Clang to try to parse and evaluate the expansion as a constant
247314
// expression instead of doing these special-case pattern matches.
248315
switch (numTokens) {
249316
case 1: {
250317
// Check for a single-token expansion of the form <literal>.
251318
// TODO: or <identifier>.
252319
const clang::Token &tok = *tokenI;
253-
320+
321+
if (castTypeIsId && tok.is(clang::tok::numeric_constant)) {
322+
auto *integerLiteral =
323+
parseNumericLiteral<clang::IntegerLiteral>(impl, tok);
324+
if (integerLiteral && integerLiteral->getValue() == 0)
325+
return importNil(impl, DC, name, ClangN);
326+
}
327+
254328
// If it's a literal token, we might be able to translate the literal.
255329
if (tok.isLiteral()) {
256-
return importLiteral(impl, DC, macro, name, tok, ClangN);
330+
return importLiteral(impl, DC, macro, name, tok, ClangN, castType);
257331
}
258332

259333
if (tok.is(clang::tok::identifier)) {
@@ -287,9 +361,10 @@ static ValueDecl *importMacro(ClangImporter::Implementation &impl,
287361
// but are pervasive in C headers anyway.
288362
clang::Token const &first = tokenI[0];
289363
clang::Token const &second = tokenI[1];
290-
364+
291365
if (isSignToken(first) && second.is(clang::tok::numeric_constant))
292-
return importNumericLiteral(impl, DC, macro, name, &first, second, ClangN);
366+
return importNumericLiteral(impl, DC, macro, name, &first, second, ClangN,
367+
castType);
293368

294369
// We also allow @"string".
295370
if (first.is(clang::tok::at) && isStringToken(second))
@@ -319,9 +394,76 @@ static ValueDecl *importMacro(ClangImporter::Implementation &impl,
319394

320395
llvm::APSInt value{ base->getValue() << shift->getValue(),
321396
clangTy->isUnsignedIntegerType() };
322-
return impl.createConstant(name, DC, type, clang::APValue(value),
397+
return createMacroConstant(impl, macro, name, DC, type,
398+
clang::APValue(value),
323399
ConstantConvertKind::Coerce, /*static=*/false,
324400
ClangN);
401+
// Check for a expression of the form (FLAG1 | FLAG2), (FLAG1 & FLAG2),
402+
// (FLAG1 || FLAG2), or (FLAG1 || FLAG2)
403+
} else if (tokenI[0].is(clang::tok::identifier) &&
404+
isBinaryOperator(tokenI[1]) &&
405+
tokenI[2].is(clang::tok::identifier)) {
406+
auto firstID = tokenI[0].getIdentifierInfo();
407+
auto secondID = tokenI[2].getIdentifierInfo();
408+
409+
if (firstID->hasMacroDefinition() && secondID->hasMacroDefinition()) {
410+
auto firstMacroInfo = impl.getClangPreprocessor().getMacroInfo(firstID);
411+
auto secondMacroInfo = impl.getClangPreprocessor().getMacroInfo(
412+
secondID);
413+
auto firstIterator = impl.ImportedMacroConstants.find(firstMacroInfo);
414+
if (firstIterator == impl.ImportedMacroConstants.end()) {
415+
return nullptr;
416+
}
417+
auto secondIterator = impl.ImportedMacroConstants.find(secondMacroInfo);
418+
if (secondIterator == impl.ImportedMacroConstants.end()) {
419+
return nullptr;
420+
}
421+
422+
auto firstConstant = firstIterator->second;
423+
auto secondConstant = secondIterator->second;
424+
auto firstValue = firstConstant.first;
425+
auto secondValue = secondConstant.first;
426+
if (!firstValue.isInt() || !secondValue.isInt()) {
427+
return nullptr;
428+
}
429+
430+
auto firstInteger = firstValue.getInt();
431+
auto secondInteger = secondValue.getInt();
432+
auto type = firstConstant.second;
433+
434+
clang::APValue value;
435+
if (tokenI[1].is(clang::tok::pipe)) {
436+
if (firstInteger.getBitWidth() == secondInteger.getBitWidth()) {
437+
value = clang::APValue(firstInteger | secondInteger);
438+
} else {
439+
return nullptr;
440+
}
441+
} else if (tokenI[1].is(clang::tok::amp)) {
442+
if (firstInteger.getBitWidth() == secondInteger.getBitWidth()) {
443+
value = clang::APValue(firstInteger & secondInteger);
444+
} else {
445+
return nullptr;
446+
}
447+
} else if (tokenI[1].is(clang::tok::pipepipe)) {
448+
auto firstBool = firstInteger.getBoolValue();
449+
auto secondBool = firstInteger.getBoolValue();
450+
auto result = firstBool || secondBool;
451+
value = clang::APValue(result ?
452+
llvm::APSInt::get(1) : llvm::APSInt::get(0));
453+
} else if (tokenI[1].is(clang::tok::ampamp)) {
454+
auto firstBool = firstInteger.getBoolValue();
455+
auto secondBool = firstInteger.getBoolValue();
456+
auto result = firstBool && secondBool;
457+
value = clang::APValue(result ?
458+
llvm::APSInt::get(1) : llvm::APSInt::get(0));
459+
} else {
460+
return nullptr;
461+
}
462+
return createMacroConstant(impl, macro, name, DC, type,
463+
value,
464+
ConstantConvertKind::Coerce,
465+
/*static=*/false, ClangN);
466+
}
325467
}
326468
break;
327469
}

lib/ClangImporter/ImporterImpl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,10 @@ class LLVM_LIBRARY_VISIBILITY ClangImporter::Implementation
386386
SmallVector<std::pair<clang::MacroInfo *, ValueDecl *>, 2>>
387387
ImportedMacros;
388388

389+
// Mapping from macro to value for macros that expand to constant values.
390+
llvm::DenseMap<const clang::MacroInfo *, std::pair<clang::APValue, Type>>
391+
ImportedMacroConstants;
392+
389393
/// Keeps track of active selector-based lookups, so that we don't infinitely
390394
/// recurse when checking whether a method with a given selector has already
391395
/// been imported.

test/IDE/Inputs/mock-sdk/Foo.annotated.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,11 +181,16 @@ class <loc>FooClassDerived</loc> : <ref:Class>FooClassBase</ref>, <ref:Protocol>
181181
func <loc>fooProtoFuncWithExtraIndentation2()</loc></decl>
182182
<decl:Func>class func <loc>fooProtoClassFunc()</loc></decl>
183183
}</decl>
184+
<decl:TypeAlias>typealias <loc>typedef_int_t</loc> = <ref:Struct>Int32</ref></decl>
184185
<decl:Var>var <loc>FOO_MACRO_1</loc>: <ref:Struct>Int32</ref> { get }</decl>
185186
<decl:Var>var <loc>FOO_MACRO_2</loc>: <ref:Struct>Int32</ref> { get }</decl>
186187
<decl:Var>var <loc>FOO_MACRO_3</loc>: <ref:Struct>Int32</ref> { get }</decl>
187188
<decl:Var>var <loc>FOO_MACRO_4</loc>: <ref:Struct>UInt32</ref> { get }</decl>
188189
<decl:Var>var <loc>FOO_MACRO_5</loc>: <ref:Struct>UInt64</ref> { get }</decl>
190+
<decl:Var>var <loc>FOO_MACRO_6</loc>: <ref:TypeAlias>typedef_int_t</ref> { get }</decl>
191+
<decl:Var>var <loc>FOO_MACRO_7</loc>: <ref:TypeAlias>typedef_int_t</ref> { get }</decl>
192+
<decl:Var>var <loc>FOO_MACRO_OR</loc>: <ref:Struct>Int32</ref> { get }</decl>
193+
<decl:Var>var <loc>FOO_MACRO_AND</loc>: <ref:Struct>Int32</ref> { get }</decl>
189194
<decl:Var>var <loc>FOO_MACRO_REDEF_1</loc>: <ref:Struct>Int32</ref> { get }</decl>
190195
<decl:Var>var <loc>FOO_MACRO_REDEF_2</loc>: <ref:Struct>Int32</ref> { get }</decl>
191196
<decl:Func>func <loc>theLastDeclInFoo()</loc></decl>
@@ -235,6 +240,8 @@ class <loc>FooClassDerived</loc> : <ref:Class>FooClassBase</ref>, <ref:Protocol>
235240
<decl:Constructor><loc>init!()</loc></decl>
236241
<decl:Constructor>convenience <loc>init!(<decl:Param>float f: <ref:Struct>Float</ref></decl>)</loc></decl>
237242
}</decl>
243+
<decl:Var>@available(*, unavailable, message: "use 'nil' instead of this imported macro")
244+
var <loc>FOO_NIL</loc>: ()</decl>
238245
<decl:Class>class <loc>FooUnavailableMembers</loc> : <ref:Class>FooClassBase</ref> {
239246
<decl:Constructor>convenience <loc>init!(<decl:Param>int i: <ref:Struct>Int32</ref></decl>)</loc></decl>
240247
<decl:Func>@available(*, unavailable, message: "use object construction 'FooUnavailableMembers(int:)'")

test/IDE/Inputs/mock-sdk/Foo.framework/Headers/Foo.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,13 +180,19 @@ int fooFuncUsingVararg(int a, ...);// This comment should not show without decl.
180180

181181
@class BarForwardDeclaredClass;
182182
enum BarforwardDeclaredEnum;
183+
typedef int typedef_int_t;
183184

184185
/* FOO_MACRO_1 is the answer */
185186
#define FOO_MACRO_1 0
186187
#define FOO_MACRO_2 1
187188
#define FOO_MACRO_3 (-1) // Don't use FOO_MACRO_3 on Saturdays.
188189
#define FOO_MACRO_4 0xffffffffu
189190
#define FOO_MACRO_5 0xffffffffffffffffull
191+
#define FOO_MACRO_6 ((typedef_int_t) 42)
192+
#define FOO_MACRO_7 ((typedef_int_t) -1)
193+
#define FOO_MACRO_OR (FOO_MACRO_2 | FOO_MACRO_6)
194+
#define FOO_MACRO_AND (FOO_MACRO_2 & FOO_MACRO_6)
195+
#define FOO_MACRO_BITWIDTH (FOO_MACRO_4 & FOO_MACRO_5)
190196

191197
#define FOO_MACRO_UNDEF_1 0
192198
#undef FOO_MACRO_UNDEF_1

test/IDE/Inputs/mock-sdk/Foo.printed.recursive.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,11 +181,16 @@ class FooClassDerived : FooClassBase, FooProtocolDerived {
181181
func fooProtoFuncWithExtraIndentation2()
182182
class func fooProtoClassFunc()
183183
}
184+
typealias typedef_int_t = Int32
184185
var FOO_MACRO_1: Int32 { get }
185186
var FOO_MACRO_2: Int32 { get }
186187
var FOO_MACRO_3: Int32 { get }
187188
var FOO_MACRO_4: UInt32 { get }
188189
var FOO_MACRO_5: UInt64 { get }
190+
var FOO_MACRO_6: typedef_int_t { get }
191+
var FOO_MACRO_7: typedef_int_t { get }
192+
var FOO_MACRO_OR: Int32 { get }
193+
var FOO_MACRO_AND: Int32 { get }
189194
var FOO_MACRO_REDEF_1: Int32 { get }
190195
var FOO_MACRO_REDEF_2: Int32 { get }
191196
func theLastDeclInFoo()
@@ -235,6 +240,8 @@ class FooClassWithClassProperties : FooClassBase {
235240
init!()
236241
convenience init!(float f: Float)
237242
}
243+
@available(*, unavailable, message: "use 'nil' instead of this imported macro")
244+
var FOO_NIL: ()
238245
class FooUnavailableMembers : FooClassBase {
239246
convenience init!(int i: Int32)
240247
@available(*, unavailable, message: "use object construction 'FooUnavailableMembers(int:)'")

test/IDE/Inputs/mock-sdk/Foo.printed.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,12 +217,18 @@ class FooClassDerived : FooClassBase, FooProtocolDerived {
217217
class func fooProtoClassFunc()
218218
}
219219

220+
typealias typedef_int_t = Int32
221+
220222
/* FOO_MACRO_1 is the answer */
221223
var FOO_MACRO_1: Int32 { get }
222224
var FOO_MACRO_2: Int32 { get }
223225
var FOO_MACRO_3: Int32 { get } // Don't use FOO_MACRO_3 on Saturdays.
224226
var FOO_MACRO_4: UInt32 { get }
225227
var FOO_MACRO_5: UInt64 { get }
228+
var FOO_MACRO_6: typedef_int_t { get }
229+
var FOO_MACRO_7: typedef_int_t { get }
230+
var FOO_MACRO_OR: Int32 { get }
231+
var FOO_MACRO_AND: Int32 { get }
226232

227233
var FOO_MACRO_REDEF_1: Int32 { get }
228234

@@ -286,6 +292,9 @@ class FooClassWithClassProperties : FooClassBase {
286292
convenience init!(float f: Float)
287293
}
288294

295+
@available(*, unavailable, message: "use 'nil' instead of this imported macro")
296+
var FOO_NIL: ()
297+
289298
class FooUnavailableMembers : FooClassBase {
290299
convenience init!(int i: Int32)
291300
@available(*, unavailable, message: "use object construction 'FooUnavailableMembers(int:)'")

test/IDE/complete_from_clang_framework.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ func testCompleteModuleQualifiedFoo2() {
146146
Foo#^CLANG_QUAL_FOO_2^#
147147
// If the number of results below changes, then you need to add a result to the
148148
// list below.
149-
// CLANG_QUAL_FOO_2: Begin completions, 64 items
149+
// CLANG_QUAL_FOO_2: Begin completions, 69 items
150150
// CLANG_QUAL_FOO_2-DAG: Decl[Class]/OtherModule[Foo]: .FooClassBase[#FooClassBase#]{{; name=.+$}}
151151
// CLANG_QUAL_FOO_2-DAG: Decl[Class]/OtherModule[Foo]: .FooClassDerived[#FooClassDerived#]{{; name=.+$}}
152152
// CLANG_QUAL_FOO_2-DAG: Decl[Class]/OtherModule[Foo]: .ClassWithInternalProt[#ClassWithInternalProt#]{{; name=.+$}}
@@ -176,6 +176,10 @@ func testCompleteModuleQualifiedFoo2() {
176176
// CLANG_QUAL_FOO_2-DAG: Decl[GlobalVar]/OtherModule[Foo]: .FOO_MACRO_3[#Int32#]{{; name=.+$}}
177177
// CLANG_QUAL_FOO_2-DAG: Decl[GlobalVar]/OtherModule[Foo]: .FOO_MACRO_4[#UInt32#]{{; name=.+$}}
178178
// CLANG_QUAL_FOO_2-DAG: Decl[GlobalVar]/OtherModule[Foo]: .FOO_MACRO_5[#UInt64#]{{; name=.+$}}
179+
// CLANG_QUAL_FOO_2-DAG: Decl[GlobalVar]/OtherModule[Foo]: .FOO_MACRO_6[#typedef_int_t#]{{; name=.+$}}
180+
// CLANG_QUAL_FOO_2-DAG: Decl[GlobalVar]/OtherModule[Foo]: .FOO_MACRO_7[#typedef_int_t#]{{; name=.+$}}
181+
// CLANG_QUAL_FOO_2-DAG: Decl[GlobalVar]/OtherModule[Foo]: .FOO_MACRO_OR[#Int32#]{{; name=.+$}}
182+
// CLANG_QUAL_FOO_2-DAG: Decl[GlobalVar]/OtherModule[Foo]: .FOO_MACRO_AND[#Int32#]{{; name=.+$}}
179183
// CLANG_QUAL_FOO_2-DAG: Decl[GlobalVar]/OtherModule[Foo]: .FOO_MACRO_REDEF_1[#Int32#]{{; name=.+$}}
180184
// CLANG_QUAL_FOO_2-DAG: Decl[GlobalVar]/OtherModule[Foo]: .FOO_MACRO_REDEF_2[#Int32#]{{; name=.+$}}
181185
// CLANG_QUAL_FOO_2-DAG: Decl[GlobalVar]/OtherModule[Foo]: .FooEnum1X[#FooEnum1#]{{; name=.+$}}

0 commit comments

Comments
 (0)