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.
/// 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;
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");
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<CXXMethodDecl>(Decl);
}
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.
D.complete(DP);
D.getMutableDeclSpec().abort();
+ if (SkipFunctionBodies && (!DP || Actions.canSkipFunctionBody(DP)) &&
+ trySkippingFunctionBody()) {
+ BodyScope.Exit();
+ return Actions.ActOnSkippedFunctionBody(DP);
+ }
+
CachedTokens Toks;
LexTemplateFunctionForLateParsing(Toks);
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);
FD->setHasSkippedBody();
else if (ObjCMethodDecl *MD = dyn_cast_or_null<ObjCMethodDecl>(Decl))
MD->setHasSkippedBody();
- return ActOnFinishFunctionBody(Decl, nullptr);
+ return Decl;
}
Decl *Sema::ActOnFinishFunctionBody(Decl *D, Stmt *BodyArg) {
--- /dev/null
+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#>)
struct SkipBodyConsumer : public clang::ASTConsumer {
/// Skip the 'skipMe' function.
bool shouldSkipFunctionBody(Decl *D) override {
- FunctionDecl *F = dyn_cast<FunctionDecl>(D);
+ NamedDecl *F = dyn_cast<NamedDecl>(D);
return F && F->getNameAsString() == "skipMe";
}
};
};
TEST(runToolOnCode, TestSkipFunctionBody) {
+ std::vector<std::string> 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<b<c>(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<typename T> int skipMe() { an_error_here }"
+ "int x = skipMe<int>();"));
+ EXPECT_FALSE(
+ runToolOnCode(new SkipBodyAction,
+ "template<typename T> int skipMeNot() { an_error_here }"));
}
TEST(runToolOnCodeWithArgs, TestNoDepFile) {