diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -1202,6 +1202,7 @@ def MicrosoftInaccessibleBase : DiagGroup<"microsoft-inaccessible-base">; def MicrosoftStaticAssert : DiagGroup<"microsoft-static-assert">; def MicrosoftInitFromPredefined : DiagGroup<"microsoft-init-from-predefined">; +def MicrosoftConcatPredefined : DiagGroup<"microsoft-concat-predefined">; // Aliases. def : DiagGroup<"msvc-include", [MicrosoftInclude]>; @@ -1219,7 +1220,8 @@ MicrosoftFlexibleArray, MicrosoftExtraQualification, MicrosoftCast, MicrosoftConstInit, MicrosoftVoidPseudoDtor, MicrosoftAnonTag, MicrosoftCommentPaste, MicrosoftEndOfFile, MicrosoftStaticAssert, - MicrosoftInitFromPredefined, MicrosoftInconsistentDllImport]>; + MicrosoftInitFromPredefined, MicrosoftConcatPredefined, + MicrosoftInconsistentDllImport]>; def ClangClPch : DiagGroup<"clang-cl-pch">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -115,6 +115,9 @@ def ext_init_from_predefined : ExtWarn< "initializing an array from a '%0' predefined identifier is a Microsoft extension">, InGroup; +def ext_concat_predef_ms : ExtWarn< + "concatenation of predefined identifier '%0' is a Microsoft extension">, + InGroup; def warn_float_overflow : Warning< "magnitude of floating-point constant too large for type %0; maximum is %1">, InGroup; diff --git a/clang/include/clang/Basic/TokenKinds.h b/clang/include/clang/Basic/TokenKinds.h --- a/clang/include/clang/Basic/TokenKinds.h +++ b/clang/include/clang/Basic/TokenKinds.h @@ -84,6 +84,13 @@ return (K == tok::identifier) || (K == tok::raw_identifier); } +/// Return true if this token is a predefined macro, for MSVC. +inline bool isMSPredefinedMacro(TokenKind K) { + return K == tok::kw___FUNCTION__ || K == tok::kw___FUNCSIG__ || + K == tok::kw_L__FUNCTION__ || K == tok::kw_L__FUNCSIG__ || + K == tok::kw___FUNCDNAME__; +} + /// Return true if this is a C or C++ string-literal (or /// C++11 user-defined-string-literal) token. inline bool isStringLiteral(TokenKind K) { diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -574,6 +574,12 @@ bool isTokenStringLiteral() const { return tok::isStringLiteral(Tok.getKind()); } + /// isTokenConcatenable - Return true if the cur token is concatenable. + bool isTokenConcatenable() const { + return isTokenStringLiteral() || + getLangOpts().MicrosoftExt && + tok::isMSPredefinedMacro(Tok.getKind()); + } /// isTokenSpecial - True if this token requires special consumption methods. bool isTokenSpecial() const { return isTokenStringLiteral() || isTokenParen() || isTokenBracket() || @@ -1787,6 +1793,7 @@ unsigned &NumLineToksConsumed, bool IsUnevaluated); + ExprResult ParsePredefinedExpression(bool AllowUserDefinedLiteral = false); ExprResult ParseStringLiteralExpression(bool AllowUserDefinedLiteral = false); private: diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -3582,6 +3582,10 @@ /// in a 'block', this returns the containing context. NamedDecl *getCurFunctionOrMethodDecl() const; + /// getCurScopeDecl - Return the Decl for either of: + /// block, lambda, captured statement, function, or translation unit. + Decl *getCurScopeDecl(); + /// Add this decl to the scope shadowed decl chains. void PushOnScopeChains(NamedDecl *D, Scope *S, bool AddToContext = true); diff --git a/clang/lib/Parse/ParseExpr.cpp b/clang/lib/Parse/ParseExpr.cpp --- a/clang/lib/Parse/ParseExpr.cpp +++ b/clang/lib/Parse/ParseExpr.cpp @@ -1297,8 +1297,7 @@ case tok::kw_L__FUNCTION__: // primary-expression: L__FUNCTION__ [MS] case tok::kw_L__FUNCSIG__: // primary-expression: L__FUNCSIG__ [MS] case tok::kw___PRETTY_FUNCTION__: // primary-expression: __P..Y_F..N__ [GNU] - Res = Actions.ActOnPredefinedExpr(Tok.getLocation(), SavedKind); - ConsumeToken(); + Res = ParsePredefinedExpression(true); break; case tok::string_literal: // primary-expression: string-literal case tok::wide_string_literal: @@ -3247,6 +3246,33 @@ return Result; } +/// ParsePredefinedExpression +ExprResult Parser::ParsePredefinedExpression(bool AllowUserDefinedLiteral) { + if (getLangOpts().MicrosoftExt && isTokenConcatenable()) { + SmallVector ConcatToks; + do { + ConcatToks.push_back(Tok); + if (isTokenStringLiteral()) + ConsumeStringToken(); + else + ConsumeToken(); + } while (isTokenConcatenable()); + + // Treat as string literal only if we are trying to concat multiple tokens, + // otherwise fallback to PredefinedExpr. + if (ConcatToks.size() > 1) + return Actions.ActOnStringLiteral( + ConcatToks, AllowUserDefinedLiteral ? getCurScope() : nullptr); + else + return Actions.ActOnPredefinedExpr(ConcatToks.front().getLocation(), + ConcatToks.front().getKind()); + } + + auto Res = Actions.ActOnPredefinedExpr(Tok.getLocation(), Tok.getKind()); + ConsumeToken(); + return Res; +} + /// ParseStringLiteralExpression - This handles the various token types that /// form string literals, and also handles string concatenation [C99 5.1.1.2, /// translation phase #6]. @@ -3256,16 +3282,21 @@ /// string-literal /// \verbatim ExprResult Parser::ParseStringLiteralExpression(bool AllowUserDefinedLiteral) { - assert(isTokenStringLiteral() && "Not a string literal!"); + assert(isTokenConcatenable() && "Not a concatenable token!"); - // String concat. Note that keywords like __func__ and __FUNCTION__ are not - // considered to be strings for concatenation purposes. + // String concatenation. + // Note that keywords like __func__ and __FUNCTION__ + // are not considered to be strings for concatenation purposes, + // unless Microsoft extensions are enabled. SmallVector StringToks; do { StringToks.push_back(Tok); - ConsumeStringToken(); - } while (isTokenStringLiteral()); + if (isTokenStringLiteral()) + ConsumeStringToken(); + else + ConsumeToken(); + } while (isTokenConcatenable()); // Pass the set of string tokens, ready for concatenation, to the actions. return Actions.ActOnStringLiteral(StringToks, diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -1491,6 +1491,19 @@ return nullptr; } +Decl *Sema::getCurScopeDecl() { + if (const BlockScopeInfo *BSI = getCurBlock()) + return BSI->TheDecl; + else if (const LambdaScopeInfo *LSI = getCurLambda()) + return LSI->CallOperator; + else if (const CapturedRegionScopeInfo *CSI = getCurCapturedRegion()) + return CSI->TheCapturedDecl; + else if (NamedDecl *ND = getCurFunctionOrMethodDecl()) + return ND; + else + return Context.getTranslationUnitDecl(); +} + LangAS Sema::getDefaultCXXMethodAddrSpace() const { if (getLangOpts().OpenCL) return getASTContext().getDefaultOpenCLPointeeAddrSpace(); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -1882,6 +1882,27 @@ ContainsUnexpandedParameterPack, ResultIndex); } +static PredefinedExpr::IdentKind getPredefinedExprKind(tok::TokenKind Kind) { + switch (Kind) { + default: + llvm_unreachable("unexpected TokenKind"); + case tok::kw___func__: + return PredefinedExpr::Func; // [C99 6.4.2.2] + case tok::kw___FUNCTION__: + return PredefinedExpr::Function; + case tok::kw___FUNCDNAME__: + return PredefinedExpr::FuncDName; // [MS] + case tok::kw___FUNCSIG__: + return PredefinedExpr::FuncSig; // [MS] + case tok::kw_L__FUNCTION__: + return PredefinedExpr::LFunction; // [MS] + case tok::kw_L__FUNCSIG__: + return PredefinedExpr::LFuncSig; // [MS] + case tok::kw___PRETTY_FUNCTION__: + return PredefinedExpr::PrettyFunction; + } +} + /// getUDSuffixLoc - Create a SourceLocation for a ud-suffix, given the /// location of the token and the offset of the ud-suffix within it. static SourceLocation getUDSuffixLoc(Sema &S, SourceLocation TokLoc, @@ -1931,6 +1952,44 @@ Sema::ActOnStringLiteral(ArrayRef StringToks, Scope *UDLScope) { assert(!StringToks.empty() && "Must have at least one string!"); + // MSVC treats some of predefined identifiers (e.g. __FUNCTION__) as + // expandable predefined macros defined as string literals, + // which may be concatenated. Expand them here (in Sema), + // because StringLiteralParser (in Lex) doesn't have access to AST. + std::vector ExpandedToks; + if (getLangOpts().MicrosoftExt) { + ExpandedToks = StringToks.vec(); + Decl *currentDecl = getCurScopeDecl(); + for (Token &Tok : ExpandedToks) { + if (!tok::isMSPredefinedMacro(Tok.getKind())) { + continue; + } + // Stringify predefined expression + Diag(Tok.getLocation(), diag::ext_concat_predef_ms) << Tok.getKind(); + if (isa(currentDecl)) { + Diag(Tok.getLocation(), diag::ext_predef_outside_function); + } + SmallString<64> Str; + llvm::raw_svector_ostream OS(Str); + Token Exp; + Exp.startToken(); + if (Tok.getKind() == tok::kw_L__FUNCTION__ || + Tok.getKind() == tok::kw_L__FUNCSIG__) { + OS << 'L'; + Exp.setKind(tok::wide_string_literal); + } else { + Exp.setKind(tok::string_literal); + } + OS << '"' + << Lexer::Stringify(PredefinedExpr::ComputeName( + getPredefinedExprKind(Tok.getKind()), currentDecl)) + << '"'; + PP.CreateString(OS.str(), Exp, Tok.getLocation(), Tok.getEndLoc()); + Tok = Exp; + } + StringToks = ExpandedToks; + } + StringLiteralParser Literal(StringToks, PP); if (Literal.hadError) return ExprError(); @@ -3600,20 +3659,9 @@ ExprResult Sema::BuildPredefinedExpr(SourceLocation Loc, PredefinedExpr::IdentKind IK) { - // Pick the current block, lambda, captured statement or function. - Decl *currentDecl = nullptr; - if (const BlockScopeInfo *BSI = getCurBlock()) - currentDecl = BSI->TheDecl; - else if (const LambdaScopeInfo *LSI = getCurLambda()) - currentDecl = LSI->CallOperator; - else if (const CapturedRegionScopeInfo *CSI = getCurCapturedRegion()) - currentDecl = CSI->TheCapturedDecl; - else - currentDecl = getCurFunctionOrMethodDecl(); - - if (!currentDecl) { + Decl *currentDecl = getCurScopeDecl(); + if (isa(currentDecl)) { Diag(Loc, diag::ext_predef_outside_function); - currentDecl = Context.getTranslationUnitDecl(); } QualType ResTy; @@ -3675,20 +3723,7 @@ } ExprResult Sema::ActOnPredefinedExpr(SourceLocation Loc, tok::TokenKind Kind) { - PredefinedExpr::IdentKind IK; - - switch (Kind) { - default: llvm_unreachable("Unknown simple primary expr!"); - case tok::kw___func__: IK = PredefinedExpr::Func; break; // [C99 6.4.2.2] - case tok::kw___FUNCTION__: IK = PredefinedExpr::Function; break; - case tok::kw___FUNCDNAME__: IK = PredefinedExpr::FuncDName; break; // [MS] - case tok::kw___FUNCSIG__: IK = PredefinedExpr::FuncSig; break; // [MS] - case tok::kw_L__FUNCTION__: IK = PredefinedExpr::LFunction; break; // [MS] - case tok::kw_L__FUNCSIG__: IK = PredefinedExpr::LFuncSig; break; // [MS] - case tok::kw___PRETTY_FUNCTION__: IK = PredefinedExpr::PrettyFunction; break; - } - - return BuildPredefinedExpr(Loc, IK); + return BuildPredefinedExpr(Loc, getPredefinedExprKind(Kind)); } ExprResult Sema::ActOnCharacterConstant(const Token &Tok, Scope *UDLScope) { diff --git a/clang/test/Sema/ms_predefined_expr.cpp b/clang/test/Sema/ms_predefined_expr.cpp --- a/clang/test/Sema/ms_predefined_expr.cpp +++ b/clang/test/Sema/ms_predefined_expr.cpp @@ -6,4 +6,47 @@ const char c[] = __FUNCSIG__; // expected-warning{{initializing an array from a '__FUNCSIG__' predefined identifier is a Microsoft extension}} const char d[] = __func__; // expected-warning{{initializing an array from a '__func__' predefined identifier is a Microsoft extension}} const char e[] = __PRETTY_FUNCTION__; // expected-warning{{initializing an array from a '__PRETTY_FUNCTION__' predefined identifier is a Microsoft extension}} + const wchar_t f[] = L__FUNCTION__; // expected-warning{{initializing an array from a 'L__FUNCTION__' predefined identifier is a Microsoft extension}} + const wchar_t g[] = L__FUNCSIG__; // expected-warning{{initializing an array from a 'L__FUNCSIG__' predefined identifier is a Microsoft extension}} +} + +// Test concatenation + +void eat_const_char_p(const char*); +void eat_const_wchar_p(const wchar_t*); + +void test_concat() { + eat_const_char_p("s" __FUNCTION__); // expected-warning{{concatenation of predefined identifier '__FUNCTION__' is a Microsoft extension}} + eat_const_char_p("s" __FUNCDNAME__); // expected-warning{{concatenation of predefined identifier '__FUNCDNAME__' is a Microsoft extension}} + eat_const_char_p("s" __FUNCSIG__); // expected-warning{{concatenation of predefined identifier '__FUNCSIG__' is a Microsoft extension}} + + eat_const_char_p(__FUNCTION__ "s"); // expected-warning{{concatenation of predefined identifier '__FUNCTION__' is a Microsoft extension}} + eat_const_char_p(__FUNCDNAME__ "s"); // expected-warning{{concatenation of predefined identifier '__FUNCDNAME__' is a Microsoft extension}} + eat_const_char_p(__FUNCSIG__ "s"); // expected-warning{{concatenation of predefined identifier '__FUNCSIG__' is a Microsoft extension}} + + eat_const_char_p("s" __FUNCTION__ "s"); // expected-warning{{concatenation of predefined identifier '__FUNCTION__' is a Microsoft extension}} + eat_const_char_p("s" __FUNCDNAME__ "s"); // expected-warning{{concatenation of predefined identifier '__FUNCDNAME__' is a Microsoft extension}} + eat_const_char_p("s" __FUNCSIG__ "s"); // expected-warning{{concatenation of predefined identifier '__FUNCSIG__' is a Microsoft extension}} +} + +void test_wide_concat() { + eat_const_wchar_p(L"s" L__FUNCTION__); // expected-warning{{concatenation of predefined identifier 'L__FUNCTION__' is a Microsoft extension}} + eat_const_wchar_p(L"s" L__FUNCSIG__); // expected-warning{{concatenation of predefined identifier 'L__FUNCSIG__' is a Microsoft extension}} + + eat_const_wchar_p(L__FUNCTION__ L"s"); // expected-warning{{concatenation of predefined identifier 'L__FUNCTION__' is a Microsoft extension}} + eat_const_wchar_p(L__FUNCSIG__ L"s"); // expected-warning{{concatenation of predefined identifier 'L__FUNCSIG__' is a Microsoft extension}} + + eat_const_wchar_p(L"s" L__FUNCTION__ L"s"); // expected-warning{{concatenation of predefined identifier 'L__FUNCTION__' is a Microsoft extension}} + eat_const_wchar_p(L"s" L__FUNCSIG__ L"s"); // expected-warning{{concatenation of predefined identifier 'L__FUNCSIG__' is a Microsoft extension}} +} + +const char* test_return() { + return __FUNCTION__ "s" __FUNCSIG__; // expected-warning{{concatenation of predefined identifier '__FUNCTION__' is a Microsoft extension}} \ + // expected-warning{{concatenation of predefined identifier '__FUNCSIG__' is a Microsoft extension}} +} + +void test_struct_init_fn() { + struct test_struct_init { + const char* str; + } struct_init = { "struct: " __FUNCSIG__ }; // expected-warning{{concatenation of predefined identifier '__FUNCSIG__' is a Microsoft extension}} }