From: Alexey Bataev Date: Wed, 15 Jun 2016 11:19:39 +0000 (+0000) Subject: [MSVC] Late parsing of in-class defined member functions in template X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=0253605771b8bd9d414aba74fe2742c730d6fd1a;p=clang [MSVC] Late parsing of in-class defined member functions in template classes. MSVC actively uses unqualified lookup in dependent bases, lookup at the instantiation point (non-dependent names may be resolved on things declared later) etc. and all this stuff is the main cause of incompatibility between clang and MSVC. Clang tries to emulate MSVC behavior but it may fail in many cases. clang could store lexed tokens for member functions definitions within ClassTemplateDecl for later parsing during template instantiation. It will allow resolving many possible issues with lookup in dependent base classes and removing many already existing MSVC-specific hacks/workarounds from the clang code. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@272774 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang-c/Index.h b/include/clang-c/Index.h index 89612b9c93..5fb1b2c5c0 100644 --- a/include/clang-c/Index.h +++ b/include/clang-c/Index.h @@ -2305,7 +2305,11 @@ enum CXCursorKind { */ CXCursor_OMPTargetUpdateDirective = 265, - CXCursor_LastStmt = CXCursor_OMPTargetUpdateDirective, + /** \brief A MS-specific late parsed compound statement. + */ + CXCursor_MSLateParsedCompoundStmt = 266, + + CXCursor_LastStmt = CXCursor_MSLateParsedCompoundStmt, /** * \brief Cursor that represents the translation unit itself. diff --git a/include/clang/AST/RecursiveASTVisitor.h b/include/clang/AST/RecursiveASTVisitor.h index 1693976cf8..bc727c7433 100644 --- a/include/clang/AST/RecursiveASTVisitor.h +++ b/include/clang/AST/RecursiveASTVisitor.h @@ -2332,6 +2332,7 @@ DEF_TRAVERSE_STMT(SEHExceptStmt, {}) DEF_TRAVERSE_STMT(SEHFinallyStmt, {}) DEF_TRAVERSE_STMT(SEHLeaveStmt, {}) DEF_TRAVERSE_STMT(CapturedStmt, { TRY_TO(TraverseDecl(S->getCapturedDecl())); }) +DEF_TRAVERSE_STMT(MSLateParsedCompoundStmt, {}) DEF_TRAVERSE_STMT(CXXOperatorCallExpr, {}) DEF_TRAVERSE_STMT(OpaqueValueExpr, {}) diff --git a/include/clang/AST/StmtCXX.h b/include/clang/AST/StmtCXX.h index 1d29c228a4..6c705cf596 100644 --- a/include/clang/AST/StmtCXX.h +++ b/include/clang/AST/StmtCXX.h @@ -18,6 +18,7 @@ #include "clang/AST/Expr.h" #include "clang/AST/NestedNameSpecifier.h" #include "clang/AST/Stmt.h" +#include "clang/Lex/Token.h" #include "llvm/Support/Compiler.h" namespace clang { @@ -417,6 +418,51 @@ public: } }; +/// This represents a group of statements like { stmt stmt } that must be parsed +/// only during instantiation. Required for better MSVC compatibility. +class MSLateParsedCompoundStmt final + : public Stmt, + private llvm::TrailingObjects { + friend class TrailingObjects; + friend class ASTStmtReader; + SourceLocation LBraceLoc, RBraceLoc; + StringRef StringRep; + unsigned NumToks; + + MSLateParsedCompoundStmt() + : Stmt(MSLateParsedCompoundStmtClass), NumToks(0) {} + + /// Set tokens for the statement. + void init(ASTContext &C, SourceLocation LB, SourceLocation RB, + ArrayRef Tokens, StringRef Rep); + +public: + static MSLateParsedCompoundStmt *Create(ASTContext &C, SourceLocation LB, + SourceLocation RB, + ArrayRef Tokens, + StringRef Rep); + /// Build an empty statement. + static MSLateParsedCompoundStmt *CreateEmpty(ASTContext &C, + unsigned NumTokens); + + SourceLocation getLocStart() const LLVM_READONLY { return LBraceLoc; } + SourceLocation getLocEnd() const LLVM_READONLY { return RBraceLoc; } + + /// Returns representation of the statement as a string. + StringRef getStringRepresentation() const { return StringRep; } + + /// Get list of tokens associated with the statement. + ArrayRef tokens() const; + + child_range children() { + return child_range(child_iterator(), child_iterator()); + } + + static bool classof(const Stmt *S) { + return S->getStmtClass() == MSLateParsedCompoundStmtClass; + } +}; + } // end namespace clang #endif diff --git a/include/clang/Basic/StmtNodes.td b/include/clang/Basic/StmtNodes.td index b4f3937d58..b7a88f3885 100644 --- a/include/clang/Basic/StmtNodes.td +++ b/include/clang/Basic/StmtNodes.td @@ -187,6 +187,7 @@ def SEHExceptStmt : Stmt; def SEHFinallyStmt : Stmt; def SEHLeaveStmt : Stmt; def MSDependentExistsStmt : Stmt; +def MSLateParsedCompoundStmt : Stmt; // OpenCL Extensions. def AsTypeExpr : DStmt; diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index 9cbad6b634..62b0926b54 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -1181,9 +1181,11 @@ private: void LexTemplateFunctionForLateParsing(CachedTokens &Toks); void ParseLateTemplatedFuncDef(LateParsedTemplate &LPT); + void ParseMSVCTemplatedFuncDef(LateParsedTemplate &LPT); static void LateTemplateParserCallback(void *P, LateParsedTemplate &LPT); static void LateTemplateParserCleanupCallback(void *P); + static void MSVCTemplateParserCallback(void *P, LateParsedTemplate &LPT); Sema::ParsingClassState PushParsingClass(Decl *TagOrTemplate, bool TopLevelClass, bool IsInterface); diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 9583dd5b26..f1f97ffa52 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -3507,6 +3507,11 @@ public: LabelDecl *GetOrCreateMSAsmLabel(StringRef ExternalLabelName, SourceLocation Location, bool AlwaysCreate); + /// Builds late parsed compound statement or just compound statement in MSVC + /// compatibility mode. + StmtResult ActOnMSLateParsedCompoundStmt(SourceLocation LB, SourceLocation RB, + ArrayRef Tokens, + StringRef Rep); VarDecl *BuildObjCExceptionDecl(TypeSourceInfo *TInfo, QualType ExceptionType, SourceLocation StartLoc, diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h index eb9ac238aa..5e8abc0e6f 100644 --- a/include/clang/Serialization/ASTBitCodes.h +++ b/include/clang/Serialization/ASTBitCodes.h @@ -1220,6 +1220,8 @@ namespace clang { STMT_GCCASM, /// \brief A MS-style AsmStmt record. STMT_MSASM, + /// \brief A MS-specific late parsed compound statement. + STMT_MS_LATE_PARSED_COMPOUND, /// \brief A PredefinedExpr record. EXPR_PREDEFINED, /// \brief A DeclRefExpr record. diff --git a/lib/AST/StmtCXX.cpp b/lib/AST/StmtCXX.cpp index 4692db84b5..08cdd20458 100644 --- a/lib/AST/StmtCXX.cpp +++ b/lib/AST/StmtCXX.cpp @@ -86,3 +86,38 @@ VarDecl *CXXForRangeStmt::getLoopVariable() { const VarDecl *CXXForRangeStmt::getLoopVariable() const { return const_cast(this)->getLoopVariable(); } + +MSLateParsedCompoundStmt * +MSLateParsedCompoundStmt::Create(ASTContext &C, SourceLocation LB, + SourceLocation RB, ArrayRef Tokens, + StringRef Rep) { + // Allocate space for private variables and initializer expressions. + void *Mem = C.Allocate(totalSizeToAlloc(Tokens.size()), + llvm::alignOf()); + auto *S = new (Mem) MSLateParsedCompoundStmt(); + S->init(C, LB, RB, Tokens, Rep); + return S; +} + +MSLateParsedCompoundStmt * +MSLateParsedCompoundStmt::CreateEmpty(ASTContext &C, unsigned NumTokens) { + // Allocate space for private variables and initializer expressions. + void *Mem = C.Allocate(totalSizeToAlloc(NumTokens), + llvm::alignOf()); + return new (Mem) MSLateParsedCompoundStmt(); +} + +void MSLateParsedCompoundStmt::init(ASTContext &C, SourceLocation LB, + SourceLocation RB, ArrayRef Tokens, + StringRef Rep) { + LBraceLoc = LB; + RBraceLoc = RB; + std::copy(Tokens.begin(), Tokens.end(), getTrailingObjects()); + StringRep = Rep.copy(C); + NumToks = Tokens.size(); +} + +ArrayRef MSLateParsedCompoundStmt::tokens() const { + return llvm::makeArrayRef(getTrailingObjects(), NumToks); +} + diff --git a/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp index 2e15c81ccc..07aef87f5f 100644 --- a/lib/AST/StmtPrinter.cpp +++ b/lib/AST/StmtPrinter.cpp @@ -2352,6 +2352,10 @@ void StmtPrinter::VisitCoreturnStmt(CoreturnStmt *S) { OS << ";"; } +void StmtPrinter::VisitMSLateParsedCompoundStmt(MSLateParsedCompoundStmt *S) { + OS << S->getStringRepresentation(); +} + void StmtPrinter::VisitCoawaitExpr(CoawaitExpr *S) { OS << "co_await "; PrintExpr(S->getOperand()); diff --git a/lib/AST/StmtProfile.cpp b/lib/AST/StmtProfile.cpp index f68bc3a397..4868beccb7 100644 --- a/lib/AST/StmtProfile.cpp +++ b/lib/AST/StmtProfile.cpp @@ -1499,6 +1499,11 @@ void StmtProfiler::VisitCoreturnStmt(const CoreturnStmt *S) { VisitStmt(S); } +void StmtProfiler::VisitMSLateParsedCompoundStmt( + const MSLateParsedCompoundStmt *S) { + VisitStmt(S); +} + void StmtProfiler::VisitCoawaitExpr(const CoawaitExpr *S) { VisitExpr(S); } diff --git a/lib/CodeGen/CGStmt.cpp b/lib/CodeGen/CGStmt.cpp index ff70bbc866..dfabd2e8d1 100644 --- a/lib/CodeGen/CGStmt.cpp +++ b/lib/CodeGen/CGStmt.cpp @@ -80,6 +80,7 @@ void CodeGenFunction::EmitStmt(const Stmt *S) { case Stmt::SEHExceptStmtClass: case Stmt::SEHFinallyStmtClass: case Stmt::MSDependentExistsStmtClass: + case Stmt::MSLateParsedCompoundStmtClass: llvm_unreachable("invalid statement class to emit generically"); case Stmt::NullStmtClass: case Stmt::CompoundStmtClass: diff --git a/lib/Parse/ParseTemplate.cpp b/lib/Parse/ParseTemplate.cpp index 6cf7b6d3dc..0369aae559 100644 --- a/lib/Parse/ParseTemplate.cpp +++ b/lib/Parse/ParseTemplate.cpp @@ -1417,6 +1417,90 @@ void Parser::ParseLateTemplatedFuncDef(LateParsedTemplate &LPT) { delete *I; } +void Parser::MSVCTemplateParserCallback(void *P, LateParsedTemplate &LPT) { + ((Parser *)P)->ParseMSVCTemplatedFuncDef(LPT); +} + +/// \brief Late parse a C++ function template in Microsoft mode. +void Parser::ParseMSVCTemplatedFuncDef(LateParsedTemplate &LPT) { + if (!LPT.D) + return; + + // Get the FunctionDecl. + FunctionDecl *FunD = LPT.D->getAsFunction(); + // Track template parameter depth. + TemplateParameterDepthRAII CurTemplateDepthTracker(TemplateParameterDepth); + + SmallVector TemplateParamScopeStack; + + // Get the list of DeclContexts to reenter. + SmallVector DeclContextsToReenter; + DeclContext *DD = FunD; + while (DD && !DD->isTranslationUnit()) { + DeclContextsToReenter.push_back(DD); + DD = DD->getLexicalParent(); + } + + // Reenter template scopes from outermost to innermost. + SmallVectorImpl::reverse_iterator II = + DeclContextsToReenter.rbegin(); + for (; II != DeclContextsToReenter.rend(); ++II) { + TemplateParamScopeStack.push_back(new ParseScope(this, + Scope::TemplateParamScope)); + unsigned NumParamLists = + Actions.ActOnReenterTemplateScope(getCurScope(), cast(*II)); + CurTemplateDepthTracker.addDepth(NumParamLists); + if (*II != FunD) { + TemplateParamScopeStack.push_back(new ParseScope(this, Scope::DeclScope)); + Actions.PushDeclContext(Actions.getCurScope(), *II); + } + } + + assert(!LPT.Toks.empty() && "Empty body!"); + + // Append the current token at the end of the new token stream so that it + // doesn't get lost. + LPT.Toks.push_back(Tok); + PP.EnterTokenStream(LPT.Toks, true); + + // Consume the previously pushed token. + ConsumeAnyToken(/*ConsumeCodeCompletionTok=*/true); + assert(Tok.isOneOf(tok::l_brace, tok::colon, tok::kw_try) && + "Inline method not starting with '{', ':' or 'try'"); + + // Parse the method body. Function body parsing code is similar enough + // to be re-used for method bodies as well. + ParseScope FnScope(this, Scope::FnScope|Scope::DeclScope); + + if (Tok.is(tok::kw_try)) { + ParseFunctionTryBlock(LPT.D, FnScope); + } else { + if (Tok.is(tok::colon)) + ParseConstructorInitializer(LPT.D); + else + Actions.ActOnDefaultCtorInitializers(LPT.D); + + if (Tok.is(tok::l_brace)) { + assert((!isa(LPT.D) || + cast(LPT.D) + ->getTemplateParameters() + ->getDepth() == TemplateParameterDepth - 1) && + "TemplateParameterDepth should be greater than the depth of " + "current template being instantiated!"); + ParseFunctionStatementBody(LPT.D, FnScope); + Actions.UnmarkAsLateParsedTemplate(FunD); + } else + Actions.ActOnFinishFunctionBody(LPT.D, nullptr); + } + + // Exit scopes. + FnScope.Exit(); + SmallVectorImpl::reverse_iterator I = + TemplateParamScopeStack.rbegin(); + for (; I != TemplateParamScopeStack.rend(); ++I) + delete *I; +} + /// \brief Lex a delayed template function for late parsing. void Parser::LexTemplateFunctionForLateParsing(CachedTokens &Toks) { tok::TokenKind kind = Tok.getKind(); diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 9ed2d72fcd..b8f68c03bd 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -89,6 +89,8 @@ Parser::Parser(Preprocessor &pp, Sema &actions, bool skipFunctionBodies) PP.addCommentHandler(CommentSemaHandler.get()); PP.setCodeCompletionHandler(*this); + if (getLangOpts().MSVCCompat) + Actions.SetLateTemplateParser(LateTemplateParserCallback, nullptr, this); } DiagnosticBuilder Parser::Diag(SourceLocation Loc, unsigned DiagID) { @@ -1053,12 +1055,35 @@ Decl *Parser::ParseFunctionDefinition(ParsingDeclarator &D, Actions.MarkAsLateParsedTemplate(FnD, DP, Toks); } return DP; - } - else if (CurParsedObjCImpl && - !TemplateInfo.TemplateParams && - (Tok.is(tok::l_brace) || Tok.is(tok::kw_try) || - Tok.is(tok::colon)) && - Actions.CurContext->isTranslationUnit()) { + } else if (getLangOpts().MSVCCompat && Tok.isNot(tok::equal) && + TemplateInfo.Kind == ParsedTemplateInfo::Template && + Actions.canDelayFunctionBody(D)) { + // In delayed template parsing mode, for function template we consume the + // tokens and store them for late parsing at the end of the translation + // unit. + MultiTemplateParamsArg TemplateParameterLists(*TemplateInfo.TemplateParams); + + ParseScope BodyScope(this, Scope::FnScope | Scope::DeclScope); + + CachedTokens Toks; + LexTemplateFunctionForLateParsing(Toks); + + Decl *Res = Actions.ActOnStartOfFunctionDef(getCurScope(), D, + *TemplateInfo.TemplateParams); + D.complete(Res); + D.getMutableDeclSpec().abort(); + StmtResult Body = Actions.ActOnMSLateParsedCompoundStmt( + Toks.begin()->getLocation(), Tok.getLocation(), Toks, + Lexer::getSourceText( + {{Toks.begin()->getLocation(), Tok.getLocation()}, false}, + Actions.getASTContext().getSourceManager(), getLangOpts())); + BodyScope.Exit(); + + return Actions.ActOnFinishFunctionBody(Res, Body.get()); + } else if (CurParsedObjCImpl && !TemplateInfo.TemplateParams && + (Tok.is(tok::l_brace) || Tok.is(tok::kw_try) || + Tok.is(tok::colon)) && + Actions.CurContext->isTranslationUnit()) { ParseScope BodyScope(this, Scope::FnScope|Scope::DeclScope); Scope *ParentScope = getCurScope()->getParent(); diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 6f23115f02..098a76379b 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -2213,11 +2213,11 @@ Sema::ActOnIdExpression(Scope *S, CXXScopeSpec &SS, bool ADL = UseArgumentDependentLookup(SS, R, HasTrailingLParen); if (R.empty() && !ADL) { - if (SS.isEmpty() && getLangOpts().MSVCCompat) { - if (Expr *E = recoverFromMSUnqualifiedLookup(*this, Context, NameInfo, - TemplateKWLoc, TemplateArgs)) - return E; - } + // if (SS.isEmpty() && getLangOpts().MSVCCompat) { + // if (Expr *E = recoverFromMSUnqualifiedLookup(*this, Context, NameInfo, + // TemplateKWLoc, TemplateArgs)) + // return E; + // } // Don't diagnose an empty lookup for inline assembly. if (IsInlineAsmIdentifier) diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp index d2d4098d17..51ae9c1b84 100644 --- a/lib/Sema/SemaStmt.cpp +++ b/lib/Sema/SemaStmt.cpp @@ -3978,3 +3978,30 @@ StmtResult Sema::ActOnCapturedRegionEnd(Stmt *S) { return Res; } + +StmtResult Sema::ActOnMSLateParsedCompoundStmt(SourceLocation LB, + SourceLocation RB, + ArrayRef Tokens, + StringRef Rep) { + if (CurContext->isDependentContext()) + return MSLateParsedCompoundStmt::Create(getASTContext(), LB, RB, Tokens, + Rep); + + QualType CXXThisTy = getCurrentThisType(); + assert(!CXXThisTy.isNull()); + auto *CXXThisRD = CXXThisTy->castAs() + ->getPointeeCXXRecordDecl() + ->getCanonicalDecl(); + DeclContext *DC = getFunctionLevelDeclContext(); + while (auto *PCXXRD = dyn_cast(DC)) { + if (PCXXRD->getCanonicalDecl() == CXXThisRD) + break; + DC = DC->getParent(); + } + auto *MD = dyn_cast(DC); + LateParsedTemplate LPT; + LPT.Toks.append(Tokens.begin(), Tokens.end()); + LPT.D = MD; + LateTemplateParser(OpaqueParser, LPT); + return MD->getBody(); +} diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index bba9019101..985f3072ef 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -1292,6 +1292,17 @@ public: Constraints, Clobbers, Exprs, EndLoc); } + /// Build a new compound statement. + /// + /// By default, performs semantic analysis to build the new statement. + /// Subclasses may override this routine to provide different behavior. + StmtResult RebuildMSLateParsedCompoundStmt(SourceLocation LB, + SourceLocation RB, + ArrayRef Tokens, + StringRef Rep) { + return getSema().ActOnMSLateParsedCompoundStmt(LB, RB, Tokens, Rep); + } + /// \brief Build a new co_return statement. /// /// By default, performs semantic analysis to build the new statement. @@ -6606,6 +6617,16 @@ TreeTransform::TransformMSAsmStmt(MSAsmStmt *S) { TransformedExprs, S->getEndLoc()); } +template +StmtResult TreeTransform::TransformMSLateParsedCompoundStmt( + MSLateParsedCompoundStmt *S) { + if (SemaRef.CurContext->isDependentContext()) + return S; + return getDerived().RebuildMSLateParsedCompoundStmt( + S->getLocStart(), S->getLocEnd(), S->tokens(), + S->getStringRepresentation()); +} + // C++ Coroutines TS template diff --git a/lib/Serialization/ASTReaderStmt.cpp b/lib/Serialization/ASTReaderStmt.cpp index 15e289f6f8..578193c9ee 100644 --- a/lib/Serialization/ASTReaderStmt.cpp +++ b/lib/Serialization/ASTReaderStmt.cpp @@ -382,6 +382,21 @@ void ASTStmtReader::VisitMSAsmStmt(MSAsmStmt *S) { Constraints, Exprs, Clobbers); } +void ASTStmtReader::VisitMSLateParsedCompoundStmt(MSLateParsedCompoundStmt *S) { + VisitStmt(S); + SourceLocation LB = ReadSourceLocation(Record, Idx); + SourceLocation RB = ReadSourceLocation(Record, Idx); + std::string StringRep = ReadString(Record, Idx); + unsigned NumToks = Record[Idx++]; + + // Read the tokens. + SmallVector Toks; + Toks.reserve(NumToks); + for (unsigned I = 0, E = NumToks; I != E; ++I) + Toks.push_back(ReadToken(Record, Idx)); + S->init(Reader.getContext(), LB, RB, Toks, StringRep); +} + void ASTStmtReader::VisitCoroutineBodyStmt(CoroutineBodyStmt *S) { // FIXME: Implement coroutine serialization. llvm_unreachable("unimplemented"); @@ -2867,6 +2882,11 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) { S = new (Context) MSAsmStmt(Empty); break; + case STMT_MS_LATE_PARSED_COMPOUND: + S = MSLateParsedCompoundStmt::CreateEmpty( + Context, Record[ASTStmtReader::NumStmtFields]); + break; + case STMT_CAPTURED: S = CapturedStmt::CreateDeserialized(Context, Record[ASTStmtReader::NumStmtFields]); diff --git a/lib/Serialization/ASTWriterStmt.cpp b/lib/Serialization/ASTWriterStmt.cpp index 5382b4af92..942485f209 100644 --- a/lib/Serialization/ASTWriterStmt.cpp +++ b/lib/Serialization/ASTWriterStmt.cpp @@ -297,6 +297,18 @@ void ASTStmtWriter::VisitMSAsmStmt(MSAsmStmt *S) { Code = serialization::STMT_MSASM; } +void ASTStmtWriter::VisitMSLateParsedCompoundStmt(MSLateParsedCompoundStmt *S) { + VisitStmt(S); + Record.push_back(S->tokens().size()); + Record.AddSourceLocation(S->getLocStart()); + Record.AddSourceLocation(S->getLocEnd()); + Record.AddString(S->getStringRepresentation()); + for (auto &Tok : S->tokens()) + Writer.AddToken(Tok, Record.getRecordData()); + + Code = serialization::STMT_MS_LATE_PARSED_COMPOUND; +} + void ASTStmtWriter::VisitCoroutineBodyStmt(CoroutineBodyStmt *S) { // FIXME: Implement coroutine serialization. llvm_unreachable("unimplemented"); diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp index 84515022fc..8df4c0ff85 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -808,6 +808,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::SwitchStmtClass: case Stmt::WhileStmtClass: case Expr::MSDependentExistsStmtClass: + case Expr::MSLateParsedCompoundStmtClass: case Stmt::CapturedStmtClass: case Stmt::OMPParallelDirectiveClass: case Stmt::OMPSimdDirectiveClass: diff --git a/test/SemaTemplate/ms-lookup-template-base-classes.cpp b/test/SemaTemplate/ms-lookup-template-base-classes.cpp index 6afc709126..71b7eb4b30 100644 --- a/test/SemaTemplate/ms-lookup-template-base-classes.cpp +++ b/test/SemaTemplate/ms-lookup-template-base-classes.cpp @@ -556,6 +556,7 @@ struct Base { template struct Template : T { void member() { f(); // expected-warning {{found via unqualified lookup into dependent bases}} + T::f(); } }; void test() { diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp index 4a1badb68d..d412f80e97 100644 --- a/tools/libclang/CIndex.cpp +++ b/tools/libclang/CIndex.cpp @@ -4632,6 +4632,8 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) { return cxstring::createRef("GCCAsmStmt"); case CXCursor_MSAsmStmt: return cxstring::createRef("MSAsmStmt"); + case CXCursor_MSLateParsedCompoundStmt: + return cxstring::createRef("MSLateParsedCompoundStmt"); case CXCursor_ObjCAtTryStmt: return cxstring::createRef("ObjCAtTryStmt"); case CXCursor_ObjCAtCatchStmt: diff --git a/tools/libclang/CXCursor.cpp b/tools/libclang/CXCursor.cpp index 075ff291d1..a718af37ea 100644 --- a/tools/libclang/CXCursor.cpp +++ b/tools/libclang/CXCursor.cpp @@ -168,6 +168,10 @@ CXCursor cxcursor::MakeCXCursor(const Stmt *S, const Decl *Parent, K = CXCursor_MSAsmStmt; break; + case Stmt::MSLateParsedCompoundStmtClass: + K = CXCursor_MSLateParsedCompoundStmt; + break; + case Stmt::ObjCAtTryStmtClass: K = CXCursor_ObjCAtTryStmt; break;