ogoffart created this revision.
ogoffart added reviewers: cfe-commits, rsmith, akyrtzi.
Herald added a subscriber: klimek.

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. 


http://reviews.llvm.org/D20821

Files:
  lib/Parse/ParseCXXInlineMethods.cpp
  lib/Parse/ParseObjc.cpp
  lib/Parse/ParseStmt.cpp
  lib/Parse/Parser.cpp
  lib/Sema/SemaDecl.cpp
  unittests/Tooling/ToolingTest.cpp

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<FunctionDecl>(D);
+    NamedDecl *F = dyn_cast<NamedDecl>(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<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) {
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<ObjCMethodDecl>(Decl))
     MD->setHasSkippedBody();
-  return ActOnFinishFunctionBody(Decl, nullptr);
+  return Decl;
 }
 
 Decl *Sema::ActOnFinishFunctionBody(Decl *D, Stmt *BodyArg) {
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/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<CXXMethodDecl>(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/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/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.
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to