Index: lib/Parse/ParseCXXInlineMethods.cpp =================================================================== --- lib/Parse/ParseCXXInlineMethods.cpp +++ lib/Parse/ParseCXXInlineMethods.cpp @@ -101,6 +101,12 @@ return FnD; } + if (SkipFunctionBodies && (!FnD || Actions.canSkipFunctionBody(FnD)) && + trySkippingFunctionBody()) { + Actions.ActOnSkippedFunctionBody(FnD); + return FnD; + } + // In delayed template parsing mode, if we are within a class template // or if we are about to parse function member template then consume // the tokens and store them for parsing at the end of the translation unit. Index: lib/Parse/ParseObjc.cpp =================================================================== --- lib/Parse/ParseObjc.cpp +++ lib/Parse/ParseObjc.cpp @@ -2657,6 +2657,12 @@ /// StashAwayMethodOrFunctionBodyTokens - Consume the tokens and store them /// for later parsing. void Parser::StashAwayMethodOrFunctionBodyTokens(Decl *MDecl) { + if (SkipFunctionBodies && (!MDecl || Actions.canSkipFunctionBody(MDecl)) && + trySkippingFunctionBody()) { + Actions.ActOnSkippedFunctionBody(MDecl); + return; + } + LexedMethod* LM = new LexedMethod(this, MDecl); CurParsedObjCImpl->LateParsedObjCMethods.push_back(LM); CachedTokens &Toks = LM->Toks; Index: lib/Parse/ParseStmt.cpp =================================================================== --- lib/Parse/ParseStmt.cpp +++ lib/Parse/ParseStmt.cpp @@ -1916,12 +1916,6 @@ assert(Tok.is(tok::l_brace)); SourceLocation LBraceLoc = Tok.getLocation(); - if (SkipFunctionBodies && (!Decl || Actions.canSkipFunctionBody(Decl)) && - trySkippingFunctionBody()) { - BodyScope.Exit(); - return Actions.ActOnSkippedFunctionBody(Decl); - } - PrettyDeclStackTraceEntry CrashInfo(Actions, Decl, LBraceLoc, "parsing function body"); @@ -1964,12 +1958,6 @@ else Actions.ActOnDefaultCtorInitializers(Decl); - if (SkipFunctionBodies && Actions.canSkipFunctionBody(Decl) && - trySkippingFunctionBody()) { - BodyScope.Exit(); - return Actions.ActOnSkippedFunctionBody(Decl); - } - // Save and reset current vtordisp stack if we have entered a C++ method body. bool IsCXXMethod = getLangOpts().CPlusPlus && Decl && isa(Decl); @@ -1990,27 +1978,81 @@ } bool Parser::trySkippingFunctionBody() { - assert(Tok.is(tok::l_brace)); assert(SkipFunctionBodies && "Should only be called when SkipFunctionBodies is enabled"); + bool IsTryCatch = Tok.is(tok::kw_try); if (!PP.isCodeCompletionEnabled()) { + if (IsTryCatch) + ConsumeToken(); + if (Tok.is(tok::colon)) { + // skip constructor initializer list + SkipUntil(tok::l_brace, StopAtSemi | StopBeforeMatch); + } + if (!Tok.is(tok::l_brace)) + return true; ConsumeBrace(); - SkipUntil(tok::r_brace); + if (!SkipUntil(tok::r_brace)) + return true; + if (IsTryCatch) { + if ((Tok.is(tok::identifier) && + Tok.getIdentifierInfo() == getSEHExceptKeyword()) || + Tok.is(tok::kw___finally)) + ConsumeToken(); + while (Tok.is(tok::kw_catch) && + SkipUntil(tok::l_brace, StopAtSemi | StopBeforeMatch)) { + ConsumeBrace(); + SkipUntil(tok::r_brace); + } + } return true; } // We're in code-completion mode. Skip parsing for all function bodies unless // the body contains the code-completion point. TentativeParsingAction PA(*this); - ConsumeBrace(); - if (SkipUntil(tok::r_brace, StopAtCodeCompletion)) { + if (IsTryCatch) + ConsumeToken(); + if (Tok.is(tok::colon)) { + // skip constructor initializer list + if (!SkipUntil(tok::l_brace, StopAtSemi | StopBeforeMatch | + StopAtCodeCompletion)) { + if (Tok.is(tok::code_completion)) { + PA.Revert(); + return false; + } + } + } + if (!Tok.is(tok::l_brace)) { PA.Commit(); return true; } - - PA.Revert(); - return false; + ConsumeBrace(); + if (!SkipUntil(tok::r_brace, StopAtCodeCompletion)) { + PA.Revert(); + return false; + } + if (IsTryCatch) { + if ((Tok.is(tok::identifier) && + Tok.getIdentifierInfo() == getSEHExceptKeyword()) || + Tok.is(tok::kw___finally)) + ConsumeToken(); + while (Tok.is(tok::kw_catch)) { + SkipUntil(tok::l_brace, StopAtSemi | StopBeforeMatch | + StopAtCodeCompletion); + if (!Tok.is(tok::l_brace)) { + PA.Revert(); + return false; + } + ConsumeBrace(); + if(!SkipUntil(tok::r_brace, StopAtCodeCompletion)) { + PA.Revert(); + return false; + } + } + } + PA.Commit(); + return true; } /// ParseCXXTryBlock - Parse a C++ try-block. Index: lib/Parse/Parser.cpp =================================================================== --- lib/Parse/Parser.cpp +++ lib/Parse/Parser.cpp @@ -1044,6 +1044,12 @@ D.complete(DP); D.getMutableDeclSpec().abort(); + if (SkipFunctionBodies && (!DP || Actions.canSkipFunctionBody(DP)) && + trySkippingFunctionBody()) { + BodyScope.Exit(); + return Actions.ActOnSkippedFunctionBody(DP); + } + CachedTokens Toks; LexTemplateFunctionForLateParsing(Toks); @@ -1136,6 +1142,13 @@ return Res; } + if (SkipFunctionBodies && (!Res || Actions.canSkipFunctionBody(Res)) && + trySkippingFunctionBody()) { + BodyScope.Exit(); + Actions.ActOnSkippedFunctionBody(Res); + return Actions.ActOnFinishFunctionBody(Res, nullptr, false); + } + if (Tok.is(tok::kw_try)) return ParseFunctionTryBlock(Res, BodyScope); Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -11378,7 +11378,7 @@ FD->setHasSkippedBody(); else if (ObjCMethodDecl *MD = dyn_cast_or_null(Decl)) MD->setHasSkippedBody(); - return ActOnFinishFunctionBody(Decl, nullptr); + return Decl; } Decl *Sema::ActOnFinishFunctionBody(Decl *D, Stmt *BodyArg) { Index: unittests/Tooling/ToolingTest.cpp =================================================================== --- unittests/Tooling/ToolingTest.cpp +++ unittests/Tooling/ToolingTest.cpp @@ -241,7 +241,7 @@ struct SkipBodyConsumer : public clang::ASTConsumer { /// Skip the 'skipMe' function. bool shouldSkipFunctionBody(Decl *D) override { - FunctionDecl *F = dyn_cast(D); + NamedDecl *F = dyn_cast(D); return F && F->getNameAsString() == "skipMe"; } }; @@ -259,6 +259,36 @@ "int skipMe() { an_error_here }")); EXPECT_FALSE(runToolOnCode(new SkipBodyAction, "int skipMeNot() { an_error_here }")); + + // Test constructors with initializers + EXPECT_TRUE(runToolOnCode(new SkipBodyAction, + "struct skipMe { skipMe() : an_error() { more error } };")); + EXPECT_TRUE(runToolOnCode(new SkipBodyAction, + "struct skipMe { skipMe(); };" + "skipMe::skipMe() : an_error() { more error }")); + EXPECT_FALSE(runToolOnCode(new SkipBodyAction, + "struct skipMeNot { skipMeNot() : an_error() { } };")); + EXPECT_FALSE(runToolOnCode(new SkipBodyAction, + "struct skipMeNot { skipMeNot(); };" + "skipMeNot::skipMeNot() : an_error() { }")); + + // Try/catch + EXPECT_TRUE(runToolOnCode(new SkipBodyAction, + "void skipMe() try { an_error() } catch(error) { error };")); + EXPECT_TRUE(runToolOnCode(new SkipBodyAction, + "struct S { void skipMe() try { an_error() } catch(error) { error } };")); + EXPECT_TRUE(runToolOnCode(new SkipBodyAction, + "void skipMe() try { an_error() } catch(error) { error; }" + "catch(error) { error } catch (error) { }")); + EXPECT_FALSE(runToolOnCode(new SkipBodyAction, + "void skipMe() try something;")); // don't crash while parsing + + // Template + EXPECT_TRUE(runToolOnCode(new SkipBodyAction, + "template int skipMe() { an_error_here }" + "int x = skipMe();")); + EXPECT_FALSE(runToolOnCode(new SkipBodyAction, + "template int skipMeNot() { an_error_here }")); } TEST(runToolOnCodeWithArgs, TestNoDepFile) {