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,14 @@ return (K == tok::identifier) || (K == tok::raw_identifier); } +/// Return true if this token is a predefined macro +/// unexpandable by MSVC preprocessor. +inline bool isUnexpandableMsMacro(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,13 @@ bool isTokenStringLiteral() const { return tok::isStringLiteral(Tok.getKind()); } + /// isTokenLikeStringLiteral - Return true if the cur token + /// can be treated as a string literal. + bool isTokenLikeStringLiteral() const { + return isTokenStringLiteral() || + getLangOpts().MicrosoftExt && + tok::isUnexpandableMsMacro(Tok.getKind()); + } /// isTokenSpecial - True if this token requires special consumption methods. bool isTokenSpecial() const { return isTokenStringLiteral() || isTokenParen() || isTokenBracket() || 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 @@ -3588,6 +3588,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); @@ -5674,6 +5678,7 @@ SourceLocation LitEndLoc, TemplateArgumentListInfo *ExplicitTemplateArgs = nullptr); + std::vector ExpandUnexpandableMsMacros(ArrayRef Toks); ExprResult BuildPredefinedExpr(SourceLocation Loc, PredefinedExpr::IdentKind IK); ExprResult ActOnPredefinedExpr(SourceLocation Loc, tok::TokenKind Kind); 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,9 +1297,14 @@ 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(); - break; + if (!getLangOpts().MicrosoftExt || !tok::isUnexpandableMsMacro(SavedKind) || + !tok::isUnexpandableMsMacro(NextToken().getKind()) && + !tok::isStringLiteral(NextToken().getKind())) { + Res = Actions.ActOnPredefinedExpr(Tok.getLocation(), SavedKind); + ConsumeToken(); + break; + } + [[fallthrough]]; // treat MS unexpandable macros as concatenable strings case tok::string_literal: // primary-expression: string-literal case tok::wide_string_literal: case tok::utf8_string_literal: @@ -3267,16 +3272,23 @@ ExprResult Parser::ParseStringLiteralExpression(bool AllowUserDefinedLiteral, bool Unevaluated) { - assert(isTokenStringLiteral() && "Not a string literal!"); + assert(isTokenLikeStringLiteral() && "Not a string-literal-like 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()); + ConsumeAnyToken(); + } while (isTokenLikeStringLiteral()); + + assert( + (StringToks.size() != 1 || + !tok::isUnexpandableMsMacro(StringToks[0].getKind())) && + "single predefined identifiers shall be handled by ActOnPredefinedExpr"); if (Unevaluated) { assert(!AllowUserDefinedLiteral && "UDL are always evaluated"); 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 @@ -1883,6 +1883,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, @@ -1946,6 +1967,46 @@ return Lit; } +std::vector Sema::ExpandUnexpandableMsMacros(ArrayRef Toks) { + // MSVC treats some of predefined identifiers (e.g. __FUNCTION__) as + // expandable macros defined as string literals, which may be concatenated. + // Expand them here (in Sema), because StringLiteralParser (in Lex) + // doesn't have access to AST. + assert(getLangOpts().MicrosoftExt); + Decl *currentDecl = getCurScopeDecl(); + std::vector ExpandedToks; + ExpandedToks.reserve(Toks.size()); + for (const Token &Tok : Toks) { + if (!tok::isUnexpandableMsMacro(Tok.getKind())) { + assert(tok::isStringLiteral(Tok.getKind())); + ExpandedToks.emplace_back(Tok); + 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 = ExpandedToks.emplace_back(); + 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()); + } + return ExpandedToks; +} + /// ActOnStringLiteral - The specified tokens were lexed as pasted string /// fragments (e.g. "foo" "bar" L"baz"). The result string has to handle string /// concatenation ([C99 5.1.1.2, translation phase #6]), so it may come from @@ -1956,6 +2017,10 @@ Sema::ActOnStringLiteral(ArrayRef StringToks, Scope *UDLScope) { assert(!StringToks.empty() && "Must have at least one string!"); + std::vector ExpandedToks; + if (getLangOpts().MicrosoftExt) + StringToks = ExpandedToks = ExpandUnexpandableMsMacros(StringToks); + StringLiteralParser Literal(StringToks, PP); if (Literal.hadError) return ExprError(); @@ -3625,20 +3690,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; @@ -3700,20 +3754,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 @@ -1,9 +1,62 @@ // RUN: %clang_cc1 %s -fsyntax-only -Wmicrosoft -verify -fms-extensions +using size_t = __SIZE_TYPE__; + void f() { const char a[] = __FUNCTION__; // expected-warning{{initializing an array from a '__FUNCTION__' predefined identifier is a Microsoft extension}} const char b[] = __FUNCDNAME__; // expected-warning{{initializing an array from a '__FUNCDNAME__' predefined identifier is a Microsoft extension}} 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}} +} + +constexpr size_t operator""_len(const char*, size_t len) { + return len; +} + +void test_udliteral() { + static_assert(__FUNCTION__ ""_len == 14); // expected-warning{{concatenation of predefined identifier '__FUNCTION__' is a Microsoft extension}} }