Skip to content

Commit

Permalink
[ClangImporter] Import preprocessor constants with a cast
Browse files Browse the repository at this point in the history
Addresses SR-1509 by adding a heuristic to import numeric literals with a
type cast. Two new cases have been added for macros with 4 or 5 tokens
to cover cases with or without sign tokens.
  • Loading branch information
IngmarStein committed Jun 29, 2016
1 parent 26ade95 commit 4f800f8
Show file tree
Hide file tree
Showing 6 changed files with 237 additions and 140 deletions.
72 changes: 61 additions & 11 deletions lib/ClangImporter/ImportMacro.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ static ValueDecl *importNumericLiteral(ClangImporter::Implementation &Impl,
Identifier name,
const clang::Token *signTok,
const clang::Token &tok,
const clang::MacroInfo *ClangN) {
const clang::MacroInfo *ClangN,
clang::QualType *castType) {
assert(tok.getKind() == clang::tok::numeric_constant &&
"not a numeric token");
{
Expand All @@ -96,12 +97,21 @@ static ValueDecl *importNumericLiteral(ClangImporter::Implementation &Impl,

if (const clang::Expr *parsed = parseNumericLiteral<>(Impl, tok)) {
auto clangTy = parsed->getType();
auto type = Impl.importType(clangTy, ImportTypeKind::Value,
isInSystemModule(DC),
/*isFullyBridgeable*/false);
if (!type)
auto literalType = Impl.importType(clangTy, ImportTypeKind::Value,
isInSystemModule(DC),
/*isFullyBridgeable*/false);
if (!literalType)
return nullptr;

Type constantType;
if (castType) {
constantType = Impl.importType(*castType, ImportTypeKind::Value,
isInSystemModule(DC),
/*isFullyBridgeable*/false);
} else {
constantType = literalType;
}

if (auto *integer = dyn_cast<clang::IntegerLiteral>(parsed)) {
// Determine the value.
llvm::APSInt value{integer->getValue(), clangTy->isUnsignedIntegerType()};
Expand All @@ -117,7 +127,7 @@ static ValueDecl *importNumericLiteral(ClangImporter::Implementation &Impl,
}
}

return Impl.createConstant(name, DC, type, clang::APValue(value),
return Impl.createConstant(name, DC, constantType, clang::APValue(value),
ConstantConvertKind::Coerce,
/*static*/ false, ClangN);
}
Expand All @@ -134,7 +144,7 @@ static ValueDecl *importNumericLiteral(ClangImporter::Implementation &Impl,
value.changeSign();
}

return Impl.createConstant(name, DC, type, clang::APValue(value),
return Impl.createConstant(name, DC, constantType, clang::APValue(value),
ConstantConvertKind::Coerce,
/*static*/ false, ClangN);
}
Expand Down Expand Up @@ -195,7 +205,7 @@ static ValueDecl *importLiteral(ClangImporter::Implementation &Impl,
switch (tok.getKind()) {
case clang::tok::numeric_constant:
return importNumericLiteral(Impl, DC, MI, name, /*signTok*/nullptr, tok,
ClangN);
ClangN, /*castType*/nullptr);

case clang::tok::string_literal:
case clang::tok::utf8_string_literal:
Expand Down Expand Up @@ -250,7 +260,7 @@ static ValueDecl *importMacro(ClangImporter::Implementation &impl,
// Check for a single-token expansion of the form <literal>.
// TODO: or <identifier>.
const clang::Token &tok = *tokenI;

// If it's a literal token, we might be able to translate the literal.
if (tok.isLiteral()) {
return importLiteral(impl, DC, macro, name, tok, ClangN);
Expand Down Expand Up @@ -287,9 +297,10 @@ static ValueDecl *importMacro(ClangImporter::Implementation &impl,
// but are pervasive in C headers anyway.
clang::Token const &first = tokenI[0];
clang::Token const &second = tokenI[1];

if (isSignToken(first) && second.is(clang::tok::numeric_constant))
return importNumericLiteral(impl, DC, macro, name, &first, second, ClangN);
return importNumericLiteral(impl, DC, macro, name, &first, second, ClangN,
/*casttype*/ nullptr);

// We also allow @"string".
if (first.is(clang::tok::at) && isStringToken(second))
Expand Down Expand Up @@ -334,6 +345,29 @@ static ValueDecl *importMacro(ClangImporter::Implementation &impl,
tokenI[3].is(clang::tok::r_paren)) {
return importStringLiteral(impl, DC, macro, name, tokenI[2],
MappedStringLiteralKind::CFString, ClangN);
// Check for literals with a cast, e.g. "(cpu_type_t) 1"
} else if (tokenI[0].is(clang::tok::l_paren) &&
tokenI[1].is(clang::tok::identifier) &&
tokenI[2].is(clang::tok::r_paren) &&
tokenI[3].is(clang::tok::numeric_constant)) {
auto identifierInfo = tokenI[1].getIdentifierInfo();
if (identifierInfo->isStr("id")) {
auto *integerLiteral =
parseNumericLiteral<clang::IntegerLiteral>(impl, tokenI[3]);
if (!integerLiteral || integerLiteral->getValue() != 0)
break;
return importNil(impl, DC, name, ClangN);
}
auto identifierName = identifierInfo->getName();
auto &identifier = impl.getClangASTContext().Idents.get(identifierName);
auto castType = impl.getClangSema().getTypeName(identifier,
clang::SourceLocation(),
/*scope*/nullptr);
if (castType) {
auto clangTy = castType.get();
return importNumericLiteral(impl, DC, macro, name, /*signTok*/nullptr,
tokenI[3], ClangN, &clangTy);
}
}
break;
}
Expand All @@ -350,6 +384,22 @@ static ValueDecl *importMacro(ClangImporter::Implementation &impl,
if (!integerLiteral || integerLiteral->getValue() != 0)
break;
return importNil(impl, DC, name, ClangN);
// Check for literals with a cast, e.g. "(cpu_type_t) -1"
} else if (tokenI[0].is(clang::tok::l_paren) &&
tokenI[1].is(clang::tok::identifier) &&
tokenI[2].is(clang::tok::r_paren) &&
isSignToken(tokenI[3]) &&
tokenI[4].is(clang::tok::numeric_constant)) {
auto identifierName = tokenI[1].getIdentifierInfo()->getName();
auto &identifier = impl.getClangASTContext().Idents.get(identifierName);
auto castType = impl.getClangSema().getTypeName(identifier,
clang::SourceLocation(),
/*scope*/nullptr);
if (castType) {
auto clangTy = castType.get();
return importNumericLiteral(impl, DC, macro, name, &tokenI[3],
tokenI[4], ClangN, &clangTy);
}
}
break;
default:
Expand Down
5 changes: 5 additions & 0 deletions test/IDE/Inputs/mock-sdk/Foo.annotated.txt
Original file line number Diff line number Diff line change
Expand Up @@ -181,11 +181,14 @@ class <loc>FooClassDerived</loc> : <ref:Class>FooClassBase</ref>, <ref:Protocol>
func <loc>fooProtoFuncWithExtraIndentation2()</loc></decl>
<decl:Func>class func <loc>fooProtoClassFunc()</loc></decl>
}</decl>
<decl:TypeAlias>typealias <loc>typedef_int_t</loc> = <ref:Struct>Int32</ref></decl>
<decl:Var>var <loc>FOO_MACRO_1</loc>: <ref:Struct>Int32</ref> { get }</decl>
<decl:Var>var <loc>FOO_MACRO_2</loc>: <ref:Struct>Int32</ref> { get }</decl>
<decl:Var>var <loc>FOO_MACRO_3</loc>: <ref:Struct>Int32</ref> { get }</decl>
<decl:Var>var <loc>FOO_MACRO_4</loc>: <ref:Struct>UInt32</ref> { get }</decl>
<decl:Var>var <loc>FOO_MACRO_5</loc>: <ref:Struct>UInt64</ref> { get }</decl>
<decl:Var>var <loc>FOO_MACRO_6</loc>: <ref:TypeAlias>typedef_int_t</ref> { get }</decl>
<decl:Var>var <loc>FOO_MACRO_7</loc>: <ref:TypeAlias>typedef_int_t</ref> { get }</decl>
<decl:Var>var <loc>FOO_MACRO_REDEF_1</loc>: <ref:Struct>Int32</ref> { get }</decl>
<decl:Var>var <loc>FOO_MACRO_REDEF_2</loc>: <ref:Struct>Int32</ref> { get }</decl>
<decl:Func>func <loc>theLastDeclInFoo()</loc></decl>
Expand Down Expand Up @@ -235,6 +238,8 @@ class <loc>FooClassDerived</loc> : <ref:Class>FooClassBase</ref>, <ref:Protocol>
<decl:Constructor><loc>init!()</loc></decl>
<decl:Constructor>convenience <loc>init!(<decl:Param>float f: <ref:Struct>Float</ref></decl>)</loc></decl>
}</decl>
<decl:Var>@available(*, unavailable, message: "use 'nil' instead of this imported macro")
var <loc>FOO_NIL</loc>: ()</decl>
<decl:Class>class <loc>FooUnavailableMembers</loc> : <ref:Class>FooClassBase</ref> {
<decl:Constructor>convenience <loc>init!(<decl:Param>int i: <ref:Struct>Int32</ref></decl>)</loc></decl>
<decl:Func>@available(*, unavailable, message: "use object construction 'FooUnavailableMembers(int:)'")
Expand Down
3 changes: 3 additions & 0 deletions test/IDE/Inputs/mock-sdk/Foo.framework/Headers/Foo.h
Original file line number Diff line number Diff line change
Expand Up @@ -180,13 +180,16 @@ int fooFuncUsingVararg(int a, ...);// This comment should not show without decl.

@class BarForwardDeclaredClass;
enum BarforwardDeclaredEnum;
typedef int typedef_int_t;

/* FOO_MACRO_1 is the answer */
#define FOO_MACRO_1 0
#define FOO_MACRO_2 1
#define FOO_MACRO_3 (-1) // Don't use FOO_MACRO_3 on Saturdays.
#define FOO_MACRO_4 0xffffffffu
#define FOO_MACRO_5 0xffffffffffffffffull
#define FOO_MACRO_6 ((typedef_int_t) 42)
#define FOO_MACRO_7 ((typedef_int_t) -1)

#define FOO_MACRO_UNDEF_1 0
#undef FOO_MACRO_UNDEF_1
Expand Down
5 changes: 5 additions & 0 deletions test/IDE/Inputs/mock-sdk/Foo.printed.recursive.txt
Original file line number Diff line number Diff line change
Expand Up @@ -181,11 +181,14 @@ class FooClassDerived : FooClassBase, FooProtocolDerived {
func fooProtoFuncWithExtraIndentation2()
class func fooProtoClassFunc()
}
typealias typedef_int_t = Int32
var FOO_MACRO_1: Int32 { get }
var FOO_MACRO_2: Int32 { get }
var FOO_MACRO_3: Int32 { get }
var FOO_MACRO_4: UInt32 { get }
var FOO_MACRO_5: UInt64 { get }
var FOO_MACRO_6: typedef_int_t { get }
var FOO_MACRO_7: typedef_int_t { get }
var FOO_MACRO_REDEF_1: Int32 { get }
var FOO_MACRO_REDEF_2: Int32 { get }
func theLastDeclInFoo()
Expand Down Expand Up @@ -235,6 +238,8 @@ class FooClassWithClassProperties : FooClassBase {
init!()
convenience init!(float f: Float)
}
@available(*, unavailable, message: "use 'nil' instead of this imported macro")
var FOO_NIL: ()
class FooUnavailableMembers : FooClassBase {
convenience init!(int i: Int32)
@available(*, unavailable, message: "use object construction 'FooUnavailableMembers(int:)'")
Expand Down
7 changes: 7 additions & 0 deletions test/IDE/Inputs/mock-sdk/Foo.printed.txt
Original file line number Diff line number Diff line change
Expand Up @@ -217,12 +217,16 @@ class FooClassDerived : FooClassBase, FooProtocolDerived {
class func fooProtoClassFunc()
}

typealias typedef_int_t = Int32

/* FOO_MACRO_1 is the answer */
var FOO_MACRO_1: Int32 { get }
var FOO_MACRO_2: Int32 { get }
var FOO_MACRO_3: Int32 { get } // Don't use FOO_MACRO_3 on Saturdays.
var FOO_MACRO_4: UInt32 { get }
var FOO_MACRO_5: UInt64 { get }
var FOO_MACRO_6: typedef_int_t { get }
var FOO_MACRO_7: typedef_int_t { get }

var FOO_MACRO_REDEF_1: Int32 { get }

Expand Down Expand Up @@ -286,6 +290,9 @@ class FooClassWithClassProperties : FooClassBase {
convenience init!(float f: Float)
}

@available(*, unavailable, message: "use 'nil' instead of this imported macro")
var FOO_NIL: ()

class FooUnavailableMembers : FooClassBase {
convenience init!(int i: Int32)
@available(*, unavailable, message: "use object construction 'FooUnavailableMembers(int:)'")
Expand Down
Loading

0 comments on commit 4f800f8

Please sign in to comment.