moc: support __has_include

__has_include is part of C++17, but moc did not handle it so far.
This commit fixes moc to correctly support it.

It should be noted that support for __has_include relies on all
necessary include paths being passed to moc.

Pick-to: 6.10 6.9 6.8
Fixes: QTBUG-136097
Change-Id: I7284e97dea12d1637b38349d32e090c0102124e7
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Fabian Kosmale 2025-04-30 14:41:45 +02:00
parent 246b43d581
commit f2ef861688
5 changed files with 151 additions and 75 deletions

View File

@ -5,18 +5,18 @@
// DO NOT EDIT.
static const short pp_keyword_trans[][128] = {
{0,0,0,0,0,0,0,0,0,98,12,0,0,0,0,0,
{0,0,0,0,0,0,0,0,0,111,13,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
98,76,96,13,1,60,62,97,9,10,58,56,11,57,102,59,
6,6,6,6,6,6,6,6,6,6,92,0,7,81,8,91,
111,89,109,14,1,73,75,110,10,11,71,69,12,70,115,72,
7,7,7,7,7,7,7,7,7,7,105,0,8,94,9,104,
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,0,101,0,61,1,
1,1,1,1,1,1,1,1,1,1,1,0,114,0,74,6,
0,1,2,3,4,1,1,1,1,1,1,1,1,1,5,1,
1,1,1,1,1,1,1,1,1,1,1,0,68,0,71,0},
1,1,1,1,1,1,1,1,1,1,1,0,81,0,84,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,79,87,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,92,100,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
@ -24,26 +24,26 @@ static const short pp_keyword_trans[][128] = {
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,88,80,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,101,93,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,93,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,106,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,14,34,0,0,0,20,0,0,0,0,0,0,
0,0,0,0,0,22,0,0,0,0,0,0,0,0,0,0},
0,0,0,0,15,35,0,0,0,21,0,0,0,0,0,0,
0,0,0,0,0,23,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,21,0,0,0,0,0,0,0,44,0,
0,0,0,0,0,0,22,0,0,0,0,0,0,0,45,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
@ -51,7 +51,7 @@ static const short pp_keyword_trans[][128] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,27,0,0,0,0,0,0,0,0,0,30,0,
0,0,0,0,28,0,0,0,0,0,0,0,0,0,31,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
@ -59,7 +59,7 @@ static const short pp_keyword_trans[][128] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,35,0,40,0,
0,0,0,0,0,0,0,0,0,0,0,0,36,0,41,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
@ -67,11 +67,11 @@ static const short pp_keyword_trans[][128] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,36,0,0,0,0,0,0,
0,0,0,38,0,0,0,0,0,0,0,0,0,0,0,0},
0,0,0,0,0,0,0,0,0,37,0,0,0,0,0,0,
0,0,0,39,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,100,0,0,0,0,99,
0,0,0,0,0,0,0,0,0,0,113,0,0,0,0,112,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
@ -83,12 +83,12 @@ static const short pp_keyword_trans[][128] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,65,0,0,0,0,0,0,0,0,0,0,0,0,0,69,
0,78,0,0,0,0,0,0,0,0,0,0,0,0,0,82,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
103,103,103,103,103,103,103,103,103,103,0,0,0,0,0,0,
116,116,116,116,116,116,116,116,116,116,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
@ -105,10 +105,11 @@ static const struct
} pp_keywords[] = {
{PP_NOTOKEN, 0, 0, 0, PP_NOTOKEN},
{PP_CHARACTER, 0, 0, 0, PP_NOTOKEN},
{PP_CHARACTER, 0, 105, 63, PP_NOTOKEN},
{PP_CHARACTER, 0, 111, 72, PP_NOTOKEN},
{PP_CHARACTER, 0, 101, 50, PP_NOTOKEN},
{PP_CHARACTER, 0, 111, 77, PP_NOTOKEN},
{PP_CHARACTER, 0, 105, 76, PP_NOTOKEN},
{PP_CHARACTER, 0, 111, 85, PP_NOTOKEN},
{PP_CHARACTER, 0, 101, 63, PP_NOTOKEN},
{PP_CHARACTER, 0, 111, 90, PP_NOTOKEN},
{PP_CHARACTER, 0, 95, 51, PP_NOTOKEN},
{PP_DIGIT, 0, 0, 0, PP_NOTOKEN},
{PP_LANGLE, 1, 0, 0, PP_NOTOKEN},
{PP_RANGLE, 2, 0, 0, PP_NOTOKEN},
@ -117,78 +118,90 @@ static const struct
{PP_COMMA, 0, 0, 0, PP_NOTOKEN},
{PP_NEWLINE, 0, 0, 0, PP_NOTOKEN},
{PP_HASH, 3, 0, 0, PP_HASH},
{PP_HASH, 0, 101, 15, PP_HASH},
{PP_HASH, 0, 102, 16, PP_HASH},
{PP_HASH, 0, 105, 17, PP_HASH},
{PP_HASH, 0, 110, 18, PP_HASH},
{PP_HASH, 0, 101, 19, PP_HASH},
{PP_HASH, 0, 101, 16, PP_HASH},
{PP_HASH, 0, 102, 17, PP_HASH},
{PP_HASH, 0, 105, 18, PP_HASH},
{PP_HASH, 0, 110, 19, PP_HASH},
{PP_HASH, 0, 101, 20, PP_HASH},
{PP_DEFINE, 0, 0, 0, PP_HASH},
{PP_HASH, 4, 0, 0, PP_HASH},
{PP_IF, 5, 0, 0, PP_HASH},
{PP_HASH, 0, 110, 23, PP_HASH},
{PP_HASH, 0, 100, 24, PP_HASH},
{PP_HASH, 0, 101, 25, PP_HASH},
{PP_HASH, 0, 102, 26, PP_HASH},
{PP_HASH, 0, 110, 24, PP_HASH},
{PP_HASH, 0, 100, 25, PP_HASH},
{PP_HASH, 0, 101, 26, PP_HASH},
{PP_HASH, 0, 102, 27, PP_HASH},
{PP_UNDEF, 0, 0, 0, PP_HASH},
{PP_HASH, 0, 101, 28, PP_HASH},
{PP_HASH, 0, 102, 29, PP_HASH},
{PP_HASH, 0, 101, 29, PP_HASH},
{PP_HASH, 0, 102, 30, PP_HASH},
{PP_IFDEF, 0, 0, 0, PP_HASH},
{PP_HASH, 0, 100, 31, PP_HASH},
{PP_HASH, 0, 101, 32, PP_HASH},
{PP_HASH, 0, 102, 33, PP_HASH},
{PP_HASH, 0, 100, 32, PP_HASH},
{PP_HASH, 0, 101, 33, PP_HASH},
{PP_HASH, 0, 102, 34, PP_HASH},
{PP_IFNDEF, 0, 0, 0, PP_HASH},
{PP_HASH, 6, 0, 0, PP_HASH},
{PP_HASH, 7, 0, 0, PP_HASH},
{PP_HASH, 0, 102, 37, PP_HASH},
{PP_HASH, 0, 102, 38, PP_HASH},
{PP_ELIF, 0, 0, 0, PP_HASH},
{PP_HASH, 0, 101, 39, PP_HASH},
{PP_HASH, 0, 101, 40, PP_HASH},
{PP_ELSE, 0, 0, 0, PP_HASH},
{PP_HASH, 0, 100, 41, PP_HASH},
{PP_HASH, 0, 105, 42, PP_HASH},
{PP_HASH, 0, 102, 43, PP_HASH},
{PP_HASH, 0, 100, 42, PP_HASH},
{PP_HASH, 0, 105, 43, PP_HASH},
{PP_HASH, 0, 102, 44, PP_HASH},
{PP_ENDIF, 0, 0, 0, PP_HASH},
{PP_HASH, 0, 99, 45, PP_HASH},
{PP_HASH, 0, 108, 46, PP_HASH},
{PP_HASH, 0, 117, 47, PP_HASH},
{PP_HASH, 0, 100, 48, PP_HASH},
{PP_HASH, 0, 101, 49, PP_HASH},
{PP_HASH, 0, 99, 46, PP_HASH},
{PP_HASH, 0, 108, 47, PP_HASH},
{PP_HASH, 0, 117, 48, PP_HASH},
{PP_HASH, 0, 100, 49, PP_HASH},
{PP_HASH, 0, 101, 50, PP_HASH},
{PP_INCLUDE, 0, 0, 0, PP_HASH},
{PP_CHARACTER, 0, 102, 51, PP_CHARACTER},
{PP_CHARACTER, 0, 105, 52, PP_CHARACTER},
{PP_CHARACTER, 0, 110, 53, PP_CHARACTER},
{PP_CHARACTER, 0, 101, 54, PP_CHARACTER},
{PP_CHARACTER, 0, 100, 55, PP_CHARACTER},
{PP_CHARACTER, 0, 104, 52, PP_CHARACTER},
{PP_CHARACTER, 0, 97, 53, PP_CHARACTER},
{PP_CHARACTER, 0, 115, 54, PP_CHARACTER},
{PP_CHARACTER, 0, 95, 55, PP_CHARACTER},
{PP_CHARACTER, 0, 105, 56, PP_CHARACTER},
{PP_CHARACTER, 0, 110, 57, PP_CHARACTER},
{PP_CHARACTER, 0, 99, 58, PP_CHARACTER},
{PP_CHARACTER, 0, 108, 59, PP_CHARACTER},
{PP_CHARACTER, 0, 117, 60, PP_CHARACTER},
{PP_CHARACTER, 0, 100, 61, PP_CHARACTER},
{PP_CHARACTER, 0, 101, 62, PP_CHARACTER},
{PP_HAS_INCLUDE, 0, 0, 0, PP_CHARACTER},
{PP_CHARACTER, 0, 102, 64, PP_CHARACTER},
{PP_CHARACTER, 0, 105, 65, PP_CHARACTER},
{PP_CHARACTER, 0, 110, 66, PP_CHARACTER},
{PP_CHARACTER, 0, 101, 67, PP_CHARACTER},
{PP_CHARACTER, 0, 100, 68, PP_CHARACTER},
{PP_DEFINED, 0, 0, 0, PP_CHARACTER},
{PP_PLUS, 0, 0, 0, PP_NOTOKEN},
{PP_MINUS, 0, 0, 0, PP_NOTOKEN},
{PP_STAR, 0, 0, 0, PP_NOTOKEN},
{PP_SLASH, 8, 0, 0, PP_NOTOKEN},
{PP_PERCENT, 0, 58, 94, PP_NOTOKEN},
{PP_PERCENT, 0, 58, 107, PP_NOTOKEN},
{PP_HAT, 0, 0, 0, PP_NOTOKEN},
{PP_AND, 0, 38, 89, PP_NOTOKEN},
{PP_CHARACTER, 0, 116, 64, PP_CHARACTER},
{PP_AND, 0, 38, 102, PP_NOTOKEN},
{PP_CHARACTER, 0, 116, 77, PP_CHARACTER},
{PP_CHARACTER, 9, 0, 0, PP_CHARACTER},
{PP_CHARACTER, 0, 110, 66, PP_CHARACTER},
{PP_CHARACTER, 0, 100, 67, PP_CHARACTER},
{PP_CHARACTER, 0, 110, 79, PP_CHARACTER},
{PP_CHARACTER, 0, 100, 80, PP_CHARACTER},
{PP_AND, 0, 0, 0, PP_CHARACTER},
{PP_OR, 0, 124, 90, PP_NOTOKEN},
{PP_CHARACTER, 0, 114, 70, PP_CHARACTER},
{PP_OR, 0, 124, 103, PP_NOTOKEN},
{PP_CHARACTER, 0, 114, 83, PP_CHARACTER},
{PP_OR, 0, 0, 0, PP_CHARACTER},
{PP_TILDE, 0, 0, 0, PP_NOTOKEN},
{PP_CHARACTER, 0, 109, 73, PP_CHARACTER},
{PP_CHARACTER, 0, 112, 74, PP_CHARACTER},
{PP_CHARACTER, 0, 108, 75, PP_CHARACTER},
{PP_CHARACTER, 0, 109, 86, PP_CHARACTER},
{PP_CHARACTER, 0, 112, 87, PP_CHARACTER},
{PP_CHARACTER, 0, 108, 88, PP_CHARACTER},
{PP_TILDE, 0, 0, 0, PP_CHARACTER},
{PP_NOT, 0, 61, 83, PP_NOTOKEN},
{PP_CHARACTER, 0, 116, 78, PP_CHARACTER},
{PP_NOT, 0, 95, 84, PP_CHARACTER},
{PP_NOT, 0, 61, 96, PP_NOTOKEN},
{PP_CHARACTER, 0, 116, 91, PP_CHARACTER},
{PP_NOT, 0, 95, 97, PP_CHARACTER},
{PP_LTLT, 0, 0, 0, PP_NOTOKEN},
{PP_GTGT, 0, 0, 0, PP_NOTOKEN},
{PP_INCOMPLETE, 0, 61, 82, PP_NOTOKEN},
{PP_INCOMPLETE, 0, 61, 95, PP_NOTOKEN},
{PP_EQEQ, 0, 0, 0, PP_NOTOKEN},
{PP_NE, 0, 0, 0, PP_NOTOKEN},
{PP_CHARACTER, 0, 101, 85, PP_CHARACTER},
{PP_CHARACTER, 0, 113, 86, PP_CHARACTER},
{PP_CHARACTER, 0, 101, 98, PP_CHARACTER},
{PP_CHARACTER, 0, 113, 99, PP_CHARACTER},
{PP_NE, 0, 0, 0, PP_CHARACTER},
{PP_LE, 0, 0, 0, PP_NOTOKEN},
{PP_GE, 0, 0, 0, PP_NOTOKEN},
@ -197,8 +210,8 @@ static const struct
{PP_QUESTION, 0, 0, 0, PP_NOTOKEN},
{PP_COLON, 0, 0, 0, PP_NOTOKEN},
{PP_HASHHASH, 0, 0, 0, PP_NOTOKEN},
{PP_INCOMPLETE, 0, 37, 95, PP_NOTOKEN},
{PP_INCOMPLETE, 0, 58, 93, PP_NOTOKEN},
{PP_INCOMPLETE, 0, 37, 108, PP_NOTOKEN},
{PP_INCOMPLETE, 0, 58, 106, PP_NOTOKEN},
{PP_QUOTE, 0, 0, 0, PP_NOTOKEN},
{PP_SINGLEQUOTE, 0, 0, 0, PP_NOTOKEN},
{PP_WHITESPACE, 0, 0, 0, PP_NOTOKEN},

View File

@ -706,17 +706,57 @@ void Preprocessor::substituteUntilNewline(Symbols &substituted)
macroExpand(&substituted, this, symbols, index, symbol().lineNum, true);
} else if (token == PP_DEFINED) {
bool braces = test(PP_LPAREN);
next(PP_IDENTIFIER);
Symbol definedOrNotDefined = symbol();
definedOrNotDefined.token = macros.contains(definedOrNotDefined)? PP_MOC_TRUE : PP_MOC_FALSE;
substituted += definedOrNotDefined;
if (test(PP_HAS_INCLUDE)) {
// __has_include is always supported
Symbol definedOrNotDefined = symbol();
definedOrNotDefined.token = PP_MOC_TRUE;
substituted += definedOrNotDefined;
} else {
next(PP_IDENTIFIER);
Symbol definedOrNotDefined = symbol();
definedOrNotDefined.token = macros.contains(definedOrNotDefined)? PP_MOC_TRUE : PP_MOC_FALSE;
substituted += definedOrNotDefined;
}
if (braces)
test(PP_RPAREN);
continue;
} else if (token == PP_NEWLINE) {
substituted += symbol();
break;
} else {
} else if (token == PP_HAS_INCLUDE) {
next(LPAREN);
Token tok = next(); // quote or LANGLE
bool usesAngleInclude = false;
QByteArray includeAsString;
Symbols innerSymbols;
if (tok == PP_LANGLE) {
usesAngleInclude = true;
next();
do {
Symbol currentSymbol = symbol();
includeAsString += currentSymbol.lexem();
if (currentSymbol.token == PP_IDENTIFIER)
macroExpand(&innerSymbols, this, symbols, index, symbol().lineNum, true);
else
innerSymbols.append(currentSymbol);
} while (next() != PP_RANGLE);
} else {
includeAsString = unquotedLexem();
}
next(RPAREN);
const QByteArray &relative = usesAngleInclude ? QByteArray() : currentFilenames.top();
bool result = !resolveInclude(includeAsString, relative).isNull();
if (usesAngleInclude && !result) {
// try with expansion
includeAsString = {};
for (const auto &innerSymbol: innerSymbols)
includeAsString.append(innerSymbol.lexem());
result = !resolveInclude(includeAsString, relative).isNull();
}
Symbol definedOrNotDefined = symbol();
definedOrNotDefined.token = result ? PP_MOC_TRUE : PP_MOC_FALSE;
substituted += definedOrNotDefined;
} else {
substituted += symbol();
}
}

View File

@ -186,6 +186,7 @@ QT_BEGIN_NAMESPACE
F(PP_ELSE) \
F(PP_ENDIF) \
F(PP_INCLUDE) \
F(PP_HAS_INCLUDE) \
F(PP_HASHHASH) \
F(PP_HASH) \
F(PP_DEFINED) \

View File

@ -1,4 +1,4 @@
// Copyright (C) 2016 The Qt Company Ltd.
// Copyright (C) 2016 TheQt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <stdio.h>
#include <string.h>
@ -27,6 +27,7 @@ static const Keyword pp_keywords[] = {
{ "#else", "PP_ELSE"},
{ "#endif", "PP_ENDIF"},
{ "#include", "PP_INCLUDE"},
{ "__has_include", "PP_HAS_INCLUDE"},
{ "defined", "PP_DEFINED"},
{ "+", "PP_PLUS" },
{ "-", "PP_MINUS" },

View File

@ -785,6 +785,7 @@ private slots:
void initTestCase();
void dontStripNamespaces();
void hasIncludeSupport();
void oldStyleCasts();
void faultyQmlRegistration_data();
void faultyQmlRegistration();
@ -943,6 +944,18 @@ private:
} while (false)
#ifdef __has_include
class HasIncludeTest {
Q_GADGET
# if __has_include(<vector>)
Q_INVOKABLE void couldFindVector() {}
# endif
# if __has_include("using-namespaces.h")
Q_INVOKABLE void couldFindLocal() {}
# endif
};
#endif
void tst_Moc::initTestCase()
{
QString binpath = QLibraryInfo::path(QLibraryInfo::BinariesPath);
@ -976,6 +989,14 @@ void tst_Moc::initTestCase()
QVERIFY(TestPointeeCanBeIncomplete::staticMetaObject.className());
}
void tst_Moc::hasIncludeSupport()
{
auto hasIncludeTestMo = &HasIncludeTest::staticMetaObject;
QVERIFY(hasIncludeTestMo->className());
QVERIFY(hasIncludeTestMo->indexOfMethod("couldFindVector()") != -1);
QVERIFY(hasIncludeTestMo->indexOfMethod("couldFindLocal()") != -1);
}
void tst_Moc::dontStripNamespaces()
{
Sender sender;