| //===--- ImportMacro.cpp - Import Clang preprocessor macros ---------------===// |
| // |
| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2017 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 |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This file implements support for translating some kinds of C preprocessor |
| // macros into Swift declarations. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "ImporterImpl.h" |
| #include "llvm/ADT/SmallString.h" |
| #include "clang/AST/ASTContext.h" |
| #include "clang/AST/Expr.h" |
| #include "clang/Lex/MacroInfo.h" |
| #include "clang/Lex/Preprocessor.h" |
| #include "clang/Sema/DelayedDiagnostic.h" |
| #include "clang/Sema/Sema.h" |
| #include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" |
| #include "swift/AST/ASTContext.h" |
| #include "swift/AST/Expr.h" |
| #include "swift/AST/Stmt.h" |
| #include "swift/AST/Types.h" |
| #include "swift/Basic/PrettyStackTrace.h" |
| #include "swift/ClangImporter/ClangModule.h" |
| |
| using namespace swift; |
| using namespace importer; |
| |
| template <typename T = clang::Expr> |
| static const T * |
| parseNumericLiteral(ClangImporter::Implementation &impl, |
| const clang::Token &tok) { |
| auto result = impl.getClangSema().ActOnNumericConstant(tok); |
| if (result.isUsable()) |
| return dyn_cast<T>(result.get()); |
| return nullptr; |
| } |
| |
| // FIXME: Duplicated from ImportDecl.cpp. |
| static bool isInSystemModule(DeclContext *D) { |
| return cast<ClangModuleUnit>(D->getModuleScopeContext())->isSystemModule(); |
| } |
| |
| static ValueDecl * |
| createMacroConstant(ClangImporter::Implementation &Impl, |
| const clang::MacroInfo *macro, |
| Identifier name, |
| DeclContext *dc, |
| Type type, |
| const clang::APValue &value, |
| ConstantConvertKind convertKind, |
| bool isStatic, |
| ClangNode ClangN) { |
| Impl.ImportedMacroConstants[macro] = {value, type}; |
| return Impl.createConstant(name, dc, type, value, convertKind, isStatic, |
| ClangN); |
| } |
| |
| static ValueDecl *importNumericLiteral(ClangImporter::Implementation &Impl, |
| DeclContext *DC, |
| const clang::MacroInfo *MI, |
| Identifier name, |
| const clang::Token *signTok, |
| const clang::Token &tok, |
| ClangNode ClangN, |
| clang::QualType castType) { |
| assert(tok.getKind() == clang::tok::numeric_constant && |
| "not a numeric token"); |
| { |
| // Temporary hack to reject literals with ud-suffix. |
| // FIXME: remove this when the following radar is implemented: |
| // <rdar://problem/16445608> Swift should set up a DiagnosticConsumer for |
| // Clang |
| llvm::SmallString<32> SpellingBuffer; |
| bool Invalid = false; |
| StringRef TokSpelling = |
| Impl.getClangPreprocessor().getSpelling(tok, SpellingBuffer, &Invalid); |
| if (Invalid) |
| return nullptr; |
| if (TokSpelling.find('_') != StringRef::npos) |
| return nullptr; |
| } |
| |
| if (const clang::Expr *parsed = parseNumericLiteral<>(Impl, tok)) { |
| auto clangTy = parsed->getType(); |
| auto literalType = Impl.importTypeIgnoreIUO( |
| clangTy, ImportTypeKind::Value, isInSystemModule(DC), |
| Bridgeability::None); |
| if (!literalType) |
| return nullptr; |
| |
| Type constantType; |
| if (castType.isNull()) { |
| constantType = literalType; |
| } else { |
| constantType = Impl.importTypeIgnoreIUO( |
| castType, ImportTypeKind::Value, isInSystemModule(DC), |
| Bridgeability::None); |
| if (!constantType) |
| return nullptr; |
| } |
| |
| if (auto *integer = dyn_cast<clang::IntegerLiteral>(parsed)) { |
| // Determine the value. |
| llvm::APSInt value{integer->getValue(), clangTy->isUnsignedIntegerType()}; |
| |
| // If there was a - sign, negate the value. |
| // If there was a ~, flip all bits. |
| if (signTok) { |
| if (signTok->is(clang::tok::minus)) { |
| if (!value.isMinSignedValue()) |
| value = -value; |
| } else if (signTok->is(clang::tok::tilde)) { |
| value.flipAllBits(); |
| } |
| } |
| |
| return createMacroConstant(Impl, MI, name, DC, constantType, |
| clang::APValue(value), |
| ConstantConvertKind::Coerce, |
| /*static*/ false, ClangN); |
| } |
| |
| if (auto *floating = dyn_cast<clang::FloatingLiteral>(parsed)) { |
| // ~ doesn't make sense with floating-point literals. |
| if (signTok && signTok->is(clang::tok::tilde)) |
| return nullptr; |
| |
| llvm::APFloat value = floating->getValue(); |
| |
| // If there was a - sign, negate the value. |
| if (signTok && signTok->is(clang::tok::minus)) { |
| value.changeSign(); |
| } |
| |
| return createMacroConstant(Impl, MI, name, DC, constantType, |
| clang::APValue(value), |
| ConstantConvertKind::Coerce, |
| /*static*/ false, ClangN); |
| } |
| // TODO: Other numeric literals (complex, imaginary, etc.) |
| } |
| return nullptr; |
| } |
| |
| static bool isStringToken(const clang::Token &tok) { |
| return tok.is(clang::tok::string_literal) || |
| tok.is(clang::tok::utf8_string_literal); |
| } |
| |
| // Describes the kind of string literal we're importing. |
| enum class MappedStringLiteralKind { |
| CString, // "string" |
| NSString, // @"string" |
| CFString // CFSTR("string") |
| }; |
| |
| static ValueDecl *importStringLiteral(ClangImporter::Implementation &Impl, |
| DeclContext *DC, |
| const clang::MacroInfo *MI, |
| Identifier name, |
| const clang::Token &tok, |
| MappedStringLiteralKind kind, |
| ClangNode ClangN) { |
| assert(isStringToken(tok)); |
| |
| clang::ActionResult<clang::Expr*> result = |
| Impl.getClangSema().ActOnStringLiteral(tok); |
| if (!result.isUsable()) |
| return nullptr; |
| |
| auto parsed = dyn_cast<clang::StringLiteral>(result.get()); |
| if (!parsed) |
| return nullptr; |
| |
| Type importTy = Impl.getNamedSwiftType(Impl.getStdlibModule(), "String"); |
| if (!importTy) |
| return nullptr; |
| |
| return Impl.createConstant(name, DC, importTy, parsed->getString(), |
| ConstantConvertKind::Coerce, /*static*/ false, |
| ClangN); |
| } |
| |
| static ValueDecl *importLiteral(ClangImporter::Implementation &Impl, |
| DeclContext *DC, |
| const clang::MacroInfo *MI, |
| Identifier name, |
| const clang::Token &tok, |
| ClangNode ClangN, |
| clang::QualType castType) { |
| switch (tok.getKind()) { |
| case clang::tok::numeric_constant: |
| return importNumericLiteral(Impl, DC, MI, name, /*signTok*/nullptr, tok, |
| ClangN, castType); |
| |
| case clang::tok::string_literal: |
| case clang::tok::utf8_string_literal: |
| return importStringLiteral(Impl, DC, MI, name, tok, |
| MappedStringLiteralKind::CString, ClangN); |
| |
| // TODO: char literals. |
| default: |
| return nullptr; |
| } |
| } |
| |
| static ValueDecl *importNil(ClangImporter::Implementation &Impl, |
| DeclContext *DC, Identifier name, |
| ClangNode clangN) { |
| // We use a dummy type since we don't have a convenient type for 'nil'. Any |
| // use of this will be an error anyway. |
| auto type = TupleType::getEmpty(Impl.SwiftContext); |
| return Impl.createUnavailableDecl(name, DC, type, |
| "use 'nil' instead of this imported macro", |
| /*isStatic=*/false, clangN); |
| } |
| |
| static bool isSignToken(const clang::Token &tok) { |
| return tok.is(clang::tok::plus) || tok.is(clang::tok::minus) || |
| tok.is(clang::tok::tilde); |
| } |
| |
| static Optional<clang::QualType> builtinTypeForToken(const clang::Token &tok, |
| const clang::ASTContext &context) { |
| switch (tok.getKind()) { |
| case clang::tok::kw_short: |
| return clang::QualType(context.ShortTy); |
| case clang::tok::kw_long: |
| return clang::QualType(context.LongTy); |
| case clang::tok::kw___int64: |
| return clang::QualType(context.LongLongTy); |
| case clang::tok::kw___int128: |
| return clang::QualType(context.Int128Ty); |
| case clang::tok::kw_signed: |
| return clang::QualType(context.IntTy); |
| case clang::tok::kw_unsigned: |
| return clang::QualType(context.UnsignedIntTy); |
| case clang::tok::kw_void: |
| return clang::QualType(context.VoidTy); |
| case clang::tok::kw_char: |
| return clang::QualType(context.CharTy); |
| case clang::tok::kw_int: |
| return clang::QualType(context.IntTy); |
| case clang::tok::kw_float: |
| return clang::QualType(context.FloatTy); |
| case clang::tok::kw_double: |
| return clang::QualType(context.DoubleTy); |
| case clang::tok::kw_wchar_t: |
| return clang::QualType(context.WCharTy); |
| case clang::tok::kw_bool: |
| return clang::QualType(context.BoolTy); |
| case clang::tok::kw_char16_t: |
| return clang::QualType(context.Char16Ty); |
| case clang::tok::kw_char32_t: |
| return clang::QualType(context.Char32Ty); |
| default: |
| return llvm::None; |
| } |
| } |
| |
| static Optional<std::pair<llvm::APSInt, Type>> |
| getIntegerConstantForMacroToken(ClangImporter::Implementation &impl, |
| DeclContext *DC, |
| const clang::Token &token) { |
| |
| // Integer literal. |
| if (token.is(clang::tok::numeric_constant)) { |
| if (auto literal = parseNumericLiteral<clang::IntegerLiteral>(impl,token)) { |
| auto value = llvm::APSInt { literal->getValue(), |
| literal->getType()->isUnsignedIntegerType() }; |
| auto type = impl.importTypeIgnoreIUO( |
| literal->getType(), ImportTypeKind::Value, isInSystemModule(DC), |
| Bridgeability::None); |
| return {{ value, type }}; |
| } |
| |
| // Macro identifier. |
| } else if (token.is(clang::tok::identifier) && |
| token.getIdentifierInfo()->hasMacroDefinition()) { |
| |
| auto rawID = token.getIdentifierInfo(); |
| auto definition = impl.getClangPreprocessor().getMacroDefinition(rawID); |
| if (!definition) |
| return None; |
| |
| ClangNode macroNode; |
| const clang::MacroInfo *macroInfo; |
| if (definition.getModuleMacros().empty()) { |
| macroInfo = definition.getMacroInfo(); |
| macroNode = macroInfo; |
| } else { |
| // Follow MacroDefinition::getMacroInfo in preferring the last ModuleMacro |
| // rather than the first. |
| const clang::ModuleMacro *moduleMacro = |
| definition.getModuleMacros().back(); |
| macroInfo = moduleMacro->getMacroInfo(); |
| macroNode = moduleMacro; |
| } |
| auto importedID = impl.getNameImporter().importMacroName(rawID, macroInfo); |
| (void)impl.importMacro(importedID, macroNode); |
| |
| auto searcher = impl.ImportedMacroConstants.find(macroInfo); |
| if (searcher == impl.ImportedMacroConstants.end()) { |
| return None; |
| } |
| auto importedConstant = searcher->second; |
| if (!importedConstant.first.isInt()) { |
| return None; |
| } |
| return {{ importedConstant.first.getInt(), importedConstant.second }}; |
| } |
| |
| return None; |
| } |
| |
| |
| static ValueDecl *importMacro(ClangImporter::Implementation &impl, |
| DeclContext *DC, |
| Identifier name, |
| const clang::MacroInfo *macro, |
| ClangNode ClangN, |
| clang::QualType castType) { |
| if (name.empty()) return nullptr; |
| |
| auto numTokens = macro->getNumTokens(); |
| auto tokenI = macro->tokens_begin(), tokenE = macro->tokens_end(); |
| |
| // Drop one layer of parentheses. |
| if (numTokens > 2 && |
| tokenI[0].is(clang::tok::l_paren) && |
| tokenE[-1].is(clang::tok::r_paren)) { |
| ++tokenI; |
| --tokenE; |
| numTokens -= 2; |
| } |
| |
| // Handle tokens starting with a type cast |
| bool castTypeIsId = false; |
| if (numTokens > 3 && |
| tokenI[0].is(clang::tok::l_paren) && |
| (tokenI[1].is(clang::tok::identifier) || |
| impl.getClangSema().isSimpleTypeSpecifier(tokenI[1].getKind())) && |
| tokenI[2].is(clang::tok::r_paren)) { |
| if (!castType.isNull()) { |
| // this is a nested cast |
| return nullptr; |
| } |
| |
| if (tokenI[1].is(clang::tok::identifier)) { |
| auto identifierInfo = tokenI[1].getIdentifierInfo(); |
| if (identifierInfo->isStr("id")) { |
| castTypeIsId = true; |
| } |
| auto identifierName = identifierInfo->getName(); |
| auto &identifier = impl.getClangASTContext().Idents.get(identifierName); |
| |
| clang::sema::DelayedDiagnosticPool diagPool{ |
| impl.getClangSema().DelayedDiagnostics.getCurrentPool()}; |
| auto diagState = impl.getClangSema().DelayedDiagnostics.push(diagPool); |
| auto parsedType = impl.getClangSema().getTypeName(identifier, |
| clang::SourceLocation(), |
| /*scope*/nullptr); |
| impl.getClangSema().DelayedDiagnostics.popWithoutEmitting(diagState); |
| |
| if (parsedType && diagPool.empty()) { |
| castType = parsedType.get(); |
| } else { |
| return nullptr; |
| } |
| if (!castType->isBuiltinType() && !castTypeIsId) { |
| return nullptr; |
| } |
| } else { |
| auto builtinType = builtinTypeForToken(tokenI[1], |
| impl.getClangASTContext()); |
| if (builtinType) { |
| castType = builtinType.getValue(); |
| } else { |
| return nullptr; |
| } |
| } |
| tokenI += 3; |
| numTokens -= 3; |
| } |
| |
| // FIXME: Ask Clang to try to parse and evaluate the expansion as a constant |
| // expression instead of doing these special-case pattern matches. |
| switch (numTokens) { |
| case 1: { |
| // Check for a single-token expansion of the form <literal>. |
| // TODO: or <identifier>. |
| const clang::Token &tok = *tokenI; |
| |
| if (castTypeIsId && tok.is(clang::tok::numeric_constant)) { |
| auto *integerLiteral = |
| parseNumericLiteral<clang::IntegerLiteral>(impl, tok); |
| if (integerLiteral && integerLiteral->getValue() == 0) |
| return importNil(impl, DC, name, ClangN); |
| } |
| |
| // 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, castType); |
| } |
| |
| if (tok.is(clang::tok::identifier)) { |
| auto clangID = tok.getIdentifierInfo(); |
| |
| // If it's an identifier that is itself a macro, look into that macro. |
| if (clangID->hasMacroDefinition()) { |
| auto isNilMacro = |
| llvm::StringSwitch<bool>(clangID->getName()) |
| #define NIL_MACRO(NAME) .Case(#NAME, true) |
| #include "MacroTable.def" |
| .Default(false); |
| if (isNilMacro) |
| return importNil(impl, DC, name, ClangN); |
| |
| auto macroID = impl.getClangPreprocessor().getMacroInfo(clangID); |
| if (macroID && macroID != macro) { |
| // FIXME: This was clearly intended to pass the cast type down, but |
| // doing so would be a behavior change. |
| return importMacro(impl, DC, name, macroID, ClangN, /*castType*/{}); |
| } |
| } |
| |
| // FIXME: If the identifier refers to a declaration, alias it? |
| } |
| return nullptr; |
| } |
| case 2: { |
| // Check for a two-token expansion of the form +<number> or -<number>. |
| // These are technically subtly wrong without parentheses because they |
| // allow things like: |
| // #define EOF -1 |
| // int pred(int x) { return x EOF; } |
| // 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, |
| castType); |
| |
| // We also allow @"string". |
| if (first.is(clang::tok::at) && isStringToken(second)) |
| return importStringLiteral(impl, DC, macro, name, second, |
| MappedStringLiteralKind::NSString, ClangN); |
| break; |
| } |
| case 3: { |
| // Check for infix operations between two integer constants. |
| // Import the result as another integer constant: |
| // #define INT3 (INT1 <op> INT2) |
| // Doesn't allow inner parentheses. |
| |
| // Parse INT1. |
| llvm::APSInt firstValue; |
| Type firstSwiftType = nullptr; |
| if (auto firstInt = getIntegerConstantForMacroToken(impl, DC, tokenI[0])) { |
| firstValue = firstInt->first; |
| firstSwiftType = firstInt->second; |
| } else { |
| return nullptr; |
| } |
| |
| // Parse INT2. |
| llvm::APSInt secondValue; |
| Type secondSwiftType = nullptr; |
| if (auto secondInt = getIntegerConstantForMacroToken(impl, DC, tokenI[2])) { |
| secondValue = secondInt->first; |
| secondSwiftType = secondInt->second; |
| } else { |
| return nullptr; |
| } |
| |
| llvm::APSInt resultValue; |
| Type resultSwiftType = nullptr; |
| |
| // Resolve width and signedness differences and find the type of the result. |
| auto firstIntSpec = clang::ento::APSIntType(firstValue); |
| auto secondIntSpec = clang::ento::APSIntType(secondValue); |
| if (firstIntSpec == std::max(firstIntSpec, secondIntSpec)) { |
| firstIntSpec.apply(secondValue); |
| resultSwiftType = firstSwiftType; |
| } else { |
| secondIntSpec.apply(firstValue); |
| resultSwiftType = secondSwiftType; |
| } |
| |
| // Addition. |
| if (tokenI[1].is(clang::tok::plus)) { |
| resultValue = firstValue + secondValue; |
| |
| // Subtraction. |
| } else if (tokenI[1].is(clang::tok::minus)) { |
| resultValue = firstValue - secondValue; |
| |
| // Multiplication. |
| } else if (tokenI[1].is(clang::tok::star)) { |
| resultValue = firstValue * secondValue; |
| |
| // Division. |
| } else if (tokenI[1].is(clang::tok::slash)) { |
| if (secondValue == 0) { return nullptr; } |
| resultValue = firstValue / secondValue; |
| |
| // Left-shift. |
| } else if (tokenI[1].is(clang::tok::lessless)) { |
| // Shift by a negative number is UB in C. Don't import. |
| if (secondValue.isNegative()) { return nullptr; } |
| resultValue = llvm::APSInt { firstValue.shl(secondValue), |
| firstValue.isUnsigned() }; |
| |
| // Right-shift. |
| } else if (tokenI[1].is(clang::tok::greatergreater)) { |
| // Shift by a negative number is UB in C. Don't import. |
| if (secondValue.isNegative()) { return nullptr; } |
| if (firstValue.isUnsigned()) { |
| resultValue = llvm::APSInt { firstValue.lshr(secondValue), |
| /*isUnsigned*/ true }; |
| } else { |
| resultValue = llvm::APSInt { firstValue.ashr(secondValue), |
| /*isUnsigned*/ false }; |
| } |
| |
| // Bitwise OR. |
| } else if (tokenI[1].is(clang::tok::pipe)) { |
| firstValue.setIsUnsigned(true); |
| secondValue.setIsUnsigned(true); |
| resultValue = llvm::APSInt { firstValue | secondValue, |
| /*isUnsigned*/ true }; |
| |
| // Bitwise AND. |
| } else if (tokenI[1].is(clang::tok::amp)) { |
| firstValue.setIsUnsigned(true); |
| secondValue.setIsUnsigned(true); |
| resultValue = llvm::APSInt { firstValue & secondValue, |
| /*isUnsigned*/ true }; |
| |
| // XOR. |
| } else if (tokenI[1].is(clang::tok::caret)) { |
| firstValue.setIsUnsigned(true); |
| secondValue.setIsUnsigned(true); |
| resultValue = llvm::APSInt { firstValue ^ secondValue, |
| /*isUnsigned*/ true }; |
| |
| // Logical OR. |
| } else if (tokenI[1].is(clang::tok::pipepipe)) { |
| bool result = firstValue.getBoolValue() || secondValue.getBoolValue(); |
| resultValue = llvm::APSInt::get(result); |
| resultSwiftType = impl.SwiftContext.getBoolDecl()->getDeclaredType(); |
| |
| // Logical AND. |
| } else if (tokenI[1].is(clang::tok::ampamp)) { |
| bool result = firstValue.getBoolValue() && secondValue.getBoolValue(); |
| resultValue = llvm::APSInt::get(result); |
| resultSwiftType = impl.SwiftContext.getBoolDecl()->getDeclaredType(); |
| |
| // Equality. |
| } else if (tokenI[1].is(clang::tok::equalequal)) { |
| resultValue = llvm::APSInt::get(firstValue == secondValue); |
| resultSwiftType = impl.SwiftContext.getBoolDecl()->getDeclaredType(); |
| |
| // Less than. |
| } else if (tokenI[1].is(clang::tok::less)) { |
| resultValue = llvm::APSInt::get(firstValue < secondValue); |
| resultSwiftType = impl.SwiftContext.getBoolDecl()->getDeclaredType(); |
| |
| // Less than or equal. |
| } else if (tokenI[1].is(clang::tok::lessequal)) { |
| resultValue = llvm::APSInt::get(firstValue <= secondValue); |
| resultSwiftType = impl.SwiftContext.getBoolDecl()->getDeclaredType(); |
| |
| // Greater than. |
| } else if (tokenI[1].is(clang::tok::greater)) { |
| resultValue = llvm::APSInt::get(firstValue > secondValue); |
| resultSwiftType = impl.SwiftContext.getBoolDecl()->getDeclaredType(); |
| |
| // Greater than or equal. |
| } else if (tokenI[1].is(clang::tok::greaterequal)) { |
| resultValue = llvm::APSInt::get(firstValue >= secondValue); |
| resultSwiftType = impl.SwiftContext.getBoolDecl()->getDeclaredType(); |
| |
| // Unhandled operators. |
| } else { |
| return nullptr; |
| } |
| |
| return createMacroConstant(impl, macro, name, DC, resultSwiftType, |
| clang::APValue(resultValue), |
| ConstantConvertKind::Coerce, |
| /*isStatic=*/false, ClangN); |
| } |
| case 4: { |
| // Check for a CFString literal of the form CFSTR("string"). |
| if (tokenI[0].is(clang::tok::identifier) && |
| tokenI[0].getIdentifierInfo()->isStr("CFSTR") && |
| tokenI[1].is(clang::tok::l_paren) && |
| isStringToken(tokenI[2]) && |
| tokenI[3].is(clang::tok::r_paren)) { |
| return importStringLiteral(impl, DC, macro, name, tokenI[2], |
| MappedStringLiteralKind::CFString, ClangN); |
| } |
| // FIXME: Handle BIT_MASK(pos) helper macros which expand to a constant? |
| break; |
| } |
| case 5: |
| // Check for the literal series of tokens (void*)0. (We've already stripped |
| // one layer of parentheses.) |
| if (tokenI[0].is(clang::tok::l_paren) && |
| tokenI[1].is(clang::tok::kw_void) && |
| tokenI[2].is(clang::tok::star) && |
| tokenI[3].is(clang::tok::r_paren) && |
| tokenI[4].is(clang::tok::numeric_constant)) { |
| auto *integerLiteral = |
| parseNumericLiteral<clang::IntegerLiteral>(impl, tokenI[4]); |
| if (!integerLiteral || integerLiteral->getValue() != 0) |
| break; |
| return importNil(impl, DC, name, ClangN); |
| } |
| break; |
| default: |
| break; |
| } |
| |
| return nullptr; |
| } |
| |
| ValueDecl *ClangImporter::Implementation::importMacro(Identifier name, |
| ClangNode macroNode) { |
| const clang::MacroInfo *macro = macroNode.getAsMacro(); |
| if (!macro) |
| return nullptr; |
| |
| PrettyStackTraceStringAction stackRAII{"importing macro", name.str()}; |
| |
| // Look for macros imported with the same name. |
| auto known = ImportedMacros.find(name); |
| if (known == ImportedMacros.end()) { |
| // Push in a placeholder to break circularity. |
| ImportedMacros[name].push_back({macro, nullptr}); |
| } else { |
| // Check whether this macro has already been imported. |
| for (const auto &entry : known->second) { |
| if (entry.first == macro) return entry.second; |
| } |
| |
| // Otherwise, check whether this macro is identical to a macro that has |
| // already been imported. |
| auto &clangPP = getClangPreprocessor(); |
| for (const auto &entry : known->second) { |
| // If the macro is equal to an existing macro, map down to the same |
| // declaration. |
| if (macro->isIdenticalTo(*entry.first, clangPP, true)) { |
| ValueDecl *result = entry.second; |
| known->second.push_back({macro, result}); |
| return result; |
| } |
| } |
| |
| // If not, push in a placeholder to break circularity. |
| known->second.push_back({macro, nullptr}); |
| } |
| |
| startedImportingEntity(); |
| |
| // We haven't tried to import this macro yet. Do so now, and cache the |
| // result. |
| |
| DeclContext *DC; |
| if (const clang::Module *module = getClangOwningModule(macroNode)) { |
| // Get the parent module because currently we don't model Clang submodules |
| // in Swift. |
| DC = getWrapperForModule(module->getTopLevelModule()); |
| } else { |
| DC = ImportedHeaderUnit; |
| } |
| |
| auto valueDecl = ::importMacro(*this, DC, name, macro, macroNode, |
| /*castType*/{}); |
| |
| // Update the entry for the value we just imported. |
| // It's /probably/ the last entry in ImportedMacros[name], but there's an |
| // outside chance more macros with the same name have been imported |
| // re-entrantly since this method started. |
| if (valueDecl) { |
| auto entryIter = llvm::find_if(llvm::reverse(ImportedMacros[name]), |
| [macro](std::pair<const clang::MacroInfo *, ValueDecl *> entry) { |
| return entry.first == macro; |
| }); |
| assert(entryIter != llvm::reverse(ImportedMacros[name]).end() && |
| "placeholder not found"); |
| entryIter->second = valueDecl; |
| } |
| |
| return valueDecl; |
| } |