From 12a83d357c51ff1a219aa576c36776be29c719fd Mon Sep 17 00:00:00 2001 From: Olivier Goffart Date: Thu, 16 Jun 2016 21:40:06 +0000 Subject: [PATCH] Fix a few issues while skipping function bodies - In functions with try { } catch { }, only the try block would be skipped, not the catch blocks - The template functions would still be parsed. - The initializers within a constructor would still be parsed. - The inline functions within class would still be stored, only to be discared later. - Invalid code with try would assert (as in "int foo() try assert_here") This attempt to do even less while skipping function bodies. Differential Revision: http://reviews.llvm.org/D20821 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@272963 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Parse/ParseCXXInlineMethods.cpp | 6 +++ lib/Parse/ParseObjc.cpp | 6 +++ lib/Parse/ParseStmt.cpp | 46 ++++++++++--------- lib/Parse/Parser.cpp | 13 ++++++ lib/Sema/SemaDecl.cpp | 2 +- test/CodeCompletion/ctor-initializer.cpp | 41 +++++++++++++++++ unittests/Tooling/ToolingTest.cpp | 56 +++++++++++++++++++++++- 7 files changed, 147 insertions(+), 23 deletions(-) create mode 100644 test/CodeCompletion/ctor-initializer.cpp diff --git a/lib/Parse/ParseCXXInlineMethods.cpp b/lib/Parse/ParseCXXInlineMethods.cpp index 3c5134268e..39fcc82704 100644 --- a/lib/Parse/ParseCXXInlineMethods.cpp +++ b/lib/Parse/ParseCXXInlineMethods.cpp @@ -101,6 +101,12 @@ NamedDecl *Parser::ParseCXXInlineMethodDef(AccessSpecifier AS, 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. diff --git a/lib/Parse/ParseObjc.cpp b/lib/Parse/ParseObjc.cpp index 95a0680994..22c832650b 100644 --- a/lib/Parse/ParseObjc.cpp +++ b/lib/Parse/ParseObjc.cpp @@ -2656,6 +2656,12 @@ Parser::ParseObjCAutoreleasePoolStmt(SourceLocation atLoc) { /// 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; diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index 69989fe522..8173d09cbb 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -1916,12 +1916,6 @@ Decl *Parser::ParseFunctionStatementBody(Decl *Decl, ParseScope &BodyScope) { 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 @@ Decl *Parser::ParseFunctionTryBlock(Decl *Decl, ParseScope &BodyScope) { 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,43 @@ Decl *Parser::ParseFunctionTryBlock(Decl *Decl, ParseScope &BodyScope) { } bool Parser::trySkippingFunctionBody() { - assert(Tok.is(tok::l_brace)); assert(SkipFunctionBodies && "Should only be called when SkipFunctionBodies is enabled"); - if (!PP.isCodeCompletionEnabled()) { - ConsumeBrace(); - SkipUntil(tok::r_brace); + SkipFunctionBody(); 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)) { + bool IsTryCatch = Tok.is(tok::kw_try); + CachedTokens Toks; + bool ErrorInPrologue = ConsumeAndStoreFunctionPrologue(Toks); + if (llvm::any_of(Toks, [](const Token &Tok) { + return Tok.is(tok::code_completion); + })) { + PA.Revert(); + return false; + } + if (ErrorInPrologue) { PA.Commit(); + SkipMalformedDecl(); return true; } - - PA.Revert(); - return false; + if (!SkipUntil(tok::r_brace, StopAtCodeCompletion)) { + PA.Revert(); + return false; + } + while (IsTryCatch && Tok.is(tok::kw_catch)) { + if (!SkipUntil(tok::l_brace, StopAtCodeCompletion) || + !SkipUntil(tok::r_brace, StopAtCodeCompletion)) { + PA.Revert(); + return false; + } + } + PA.Commit(); + return true; } /// ParseCXXTryBlock - Parse a C++ try-block. diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index 9ed2d72fcd..f968f995d5 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -1044,6 +1044,12 @@ Decl *Parser::ParseFunctionDefinition(ParsingDeclarator &D, 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 @@ Decl *Parser::ParseFunctionDefinition(ParsingDeclarator &D, 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); diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 49a650e216..deb8d15c8f 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -11384,7 +11384,7 @@ Decl *Sema::ActOnSkippedFunctionBody(Decl *Decl) { 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) { diff --git a/test/CodeCompletion/ctor-initializer.cpp b/test/CodeCompletion/ctor-initializer.cpp new file mode 100644 index 0000000000..00af64dd4f --- /dev/null +++ b/test/CodeCompletion/ctor-initializer.cpp @@ -0,0 +1,41 @@ +struct Base1 { + Base1() : {} + // RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:2:12 %s -o - | FileCheck -check-prefix=CHECK-CC1 %s + // CHECK-CC1: COMPLETION: Pattern : member1(<#args#>) + // CHECK-CC1: COMPLETION: Pattern : member2(<#args#> + + Base1(int) : member1(123), {} + // RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:7:30 %s -o - | FileCheck -check-prefix=CHECK-CC2 %s + // CHECK-CC2-NOT: COMPLETION: Pattern : member1(<#args#>) + // CHECK-CC2: COMPLETION: Pattern : member2(<#args#> + + int member1; + float member2; +}; + +struct Derived : public Base1 { + Derived(); + Derived(int); + Derived(float); + int deriv1; +}; + +Derived::Derived() : {} +// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:23:22 %s -o - | FileCheck -check-prefix=CHECK-CC3 %s +// CHECK-CC3: COMPLETION: Pattern : Base1(<#args#>) +// CHECK-CC3: COMPLETION: Pattern : deriv1(<#args#>) + +Derived::Derived(int) try : { +} catch (...) { +} +// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:28:29 %s -o - | FileCheck -check-prefix=CHECK-CC4 %s +// CHECK-CC4: COMPLETION: Pattern : Base1(<#args#>) +// CHECK-CC4: COMPLETION: Pattern : deriv1(<#args#>) + +Derived::Derived(float) try : Base1(), +{ +} catch (...) { +} +// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:35:39 %s -o - | FileCheck -check-prefix=CHECK-CC5 %s +// CHECK-CC5-NOT: COMPLETION: Pattern : Base1(<#args#>) +// CHECK-CC5: COMPLETION: Pattern : deriv1(<#args#>) diff --git a/unittests/Tooling/ToolingTest.cpp b/unittests/Tooling/ToolingTest.cpp index c4b174f183..c0b8695e34 100644 --- a/unittests/Tooling/ToolingTest.cpp +++ b/unittests/Tooling/ToolingTest.cpp @@ -241,7 +241,7 @@ TEST(newFrontendActionFactory, InjectsSourceFileCallbacks) { 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"; } }; @@ -255,10 +255,64 @@ struct SkipBodyAction : public clang::ASTFrontendAction { }; TEST(runToolOnCode, TestSkipFunctionBody) { + std::vector Args = {"-std=c++11"}; + EXPECT_TRUE(runToolOnCode(new SkipBodyAction, "int skipMe() { an_error_here }")); EXPECT_FALSE(runToolOnCode(new SkipBodyAction, "int skipMeNot() { an_error_here }")); + + // Test constructors with initializers + EXPECT_TRUE(runToolOnCodeWithArgs( + new SkipBodyAction, + "struct skipMe { skipMe() : an_error() { more error } };", Args)); + EXPECT_TRUE(runToolOnCodeWithArgs( + new SkipBodyAction, "struct skipMe { skipMe(); };" + "skipMe::skipMe() : an_error([](){;}) { more error }", + Args)); + EXPECT_TRUE(runToolOnCodeWithArgs( + new SkipBodyAction, "struct skipMe { skipMe(); };" + "skipMe::skipMe() : an_error{[](){;}} { more error }", + Args)); + EXPECT_TRUE(runToolOnCodeWithArgs( + new SkipBodyAction, + "struct skipMe { skipMe(); };" + "skipMe::skipMe() : a(e)>>(), f{}, g() { error }", + Args)); + EXPECT_TRUE(runToolOnCodeWithArgs( + new SkipBodyAction, "struct skipMe { skipMe() : bases()... { error } };", + Args)); + + EXPECT_FALSE(runToolOnCodeWithArgs( + new SkipBodyAction, "struct skipMeNot { skipMeNot() : an_error() { } };", + Args)); + EXPECT_FALSE(runToolOnCodeWithArgs(new SkipBodyAction, + "struct skipMeNot { skipMeNot(); };" + "skipMeNot::skipMeNot() : an_error() { }", + Args)); + + // 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) { -- 2.40.0