]> granicus.if.org Git - clang/commitdiff
Teach block rewriter to replace '^' with '*' in VarDecls.
authorSteve Naroff <snaroff@apple.com>
Tue, 23 Sep 2008 19:24:41 +0000 (19:24 +0000)
committerSteve Naroff <snaroff@apple.com>
Tue, 23 Sep 2008 19:24:41 +0000 (19:24 +0000)
Since we don't have DeclGroup's and location information for types, there is some fancy footwork to do this fairly reliably.

O.K...it's a kludge. One day, we can use this as motivation to do this more gracefully:-)

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@56499 91177308-0d34-0410-b5e6-96231b3b80d8

Driver/RewriteBlocks.cpp
test/Rewriter/block-test.c [new file with mode: 0644]

index 4f91606bfa421e9033db9448a33a7e9a3a8447c8..b61e9a224108783d08f704bd7d215886573f428b 100644 (file)
@@ -131,6 +131,9 @@ public:
   void RewriteCategoryDecl(ObjCCategoryDecl *CatDecl);
   void RewriteProtocolDecl(ObjCProtocolDecl *PDecl);
   void RewriteMethodDecl(ObjCMethodDecl *MDecl);
+  
+  bool BlockPointerTypeTakesAnyBlockArguments(QualType QT);
+  void GetExtentOfArgList(const char *Name, char *&LParen, char *&RParen);
 };
   
 }
@@ -455,9 +458,9 @@ std::string RewriteBlocks::SynthesizeBlockFunc(BlockExpr *CE, int i,
     }
     if (haveByRefDecls) {
       // Remove |...|.
-      const char *firstBarPtr = strchr(BodyStartBuf, '|');
-      const char *secondBarPtr = strchr(firstBarPtr+1, '|');
-      BodyBuf.replace(firstBarPtr-BodyStartBuf, secondBarPtr-firstBarPtr+1, "");
+      //const char *firstBarPtr = strchr(BodyStartBuf, '|');
+      //const char *secondBarPtr = strchr(firstBarPtr+1, '|');
+      //BodyBuf.replace(firstBarPtr-BodyStartBuf, secondBarPtr-firstBarPtr+1, "");
     } 
     S += "  ";
     S += BodyBuf;
@@ -809,6 +812,40 @@ void RewriteBlocks::RewriteBlockPointerFunctionArgs(FunctionDecl *FD) {
   return;
 }
 
+bool RewriteBlocks::BlockPointerTypeTakesAnyBlockArguments(QualType QT) {
+  const BlockPointerType *BPT = QT->getAsBlockPointerType();
+  assert(BPT && "BlockPointerTypeTakeAnyBlockArguments(): not a block pointer type");
+  const FunctionTypeProto *FTP = BPT->getPointeeType()->getAsFunctionTypeProto();
+  if (FTP) {
+    for (FunctionTypeProto::arg_type_iterator I = FTP->arg_type_begin(), 
+         E = FTP->arg_type_end(); I != E; ++I)
+      if (isBlockPointerType(*I))
+        return true;
+  }
+  return false;
+}
+
+void RewriteBlocks::GetExtentOfArgList(const char *Name, 
+                                       char *&LParen, char *&RParen) {
+  char *argPtr = strchr(Name, '(');
+  assert((*argPtr == '(') && "Rewriter fuzzy parser confused");
+  
+  LParen = argPtr; // output the start.
+  argPtr++; // skip past the left paren.
+  unsigned parenCount = 1;
+  
+  while (*argPtr && parenCount) {
+    switch (*argPtr) {
+      case '(': parenCount++; break;
+      case ')': parenCount--; break;
+      default: break;
+    }
+    if (parenCount) argPtr++;
+  }
+  assert((*argPtr == ')') && "Rewriter fuzzy parser confused");
+  RParen = argPtr; // output the end
+}
+
 void RewriteBlocks::RewriteBlockPointerDecl(NamedDecl *ND) {
   SourceLocation DeclLoc = ND->getLocation();
   const char *startBuf, *endBuf;
@@ -820,6 +857,29 @@ void RewriteBlocks::RewriteBlockPointerDecl(NamedDecl *ND) {
     DeclLoc = VD->getLocation();
     startBuf = SM->getCharacterData(DeclLoc);
     endBuf = startBuf;
+    // scan backward (from the decl location) for the end of the previous decl.
+    while (*startBuf != '^' && *startBuf != ';' && startBuf != MainFileStart)
+      startBuf--;
+    assert((*startBuf == '^') && 
+           "RewriteBlockPointerDecl() scan error: no caret");
+    // Replace the '^' with '*', computing a negative offset.
+    DeclLoc = DeclLoc.getFileLocWithOffset(startBuf-endBuf);
+    ReplaceText(DeclLoc, 1, "*", 1);
+    
+    if (BlockPointerTypeTakesAnyBlockArguments(VD->getType())) {
+      // Replace the '^' with '*' for arguments.
+      DeclLoc = VD->getLocation();
+      startBuf = SM->getCharacterData(DeclLoc);
+      char *argListBegin, *argListEnd;
+      GetExtentOfArgList(startBuf, argListBegin, argListEnd);
+      while (argListBegin < argListEnd) {
+        if (*argListBegin == '^') {
+          SourceLocation CaretLoc = DeclLoc.getFileLocWithOffset(argListBegin-startBuf);
+          ReplaceText(CaretLoc, 1, "*", 1);
+        }
+        argListBegin++;
+      }
+    }
   } else if (TypedefDecl *TDD = dyn_cast<TypedefDecl>(ND)) {
     DeclLoc = TDD->getLocation();
     startBuf = SM->getCharacterData(DeclLoc);
@@ -828,15 +888,7 @@ void RewriteBlocks::RewriteBlockPointerDecl(NamedDecl *ND) {
       DeclLoc = DeclLoc.getFileLocWithOffset(8);
     }
     endBuf = startBuf;
-  }  
-  // FIXME: need to skip past the argument list...then check for ','.
-  while (*endBuf && *endBuf != '=' && *endBuf != ';')
-    endBuf++;
-  
-  SourceLocation DeclEndLoc = DeclLoc.getFileLocWithOffset(endBuf-startBuf);
-  
-  std::string Tag = "struct __closure_impl *" + std::string(ND->getName());
-  ReplaceText(DeclLoc, endBuf-startBuf, Tag.c_str(), Tag.size());
+  }
   return;
 }
 
diff --git a/test/Rewriter/block-test.c b/test/Rewriter/block-test.c
new file mode 100644 (file)
index 0000000..0a6bde0
--- /dev/null
@@ -0,0 +1,27 @@
+// RUN: clang -rewrite-blocks %s -o -
+
+typedef void (^test_block_t)();
+
+int main(int argc, char **argv) {
+    int a;
+
+    void (^test_block_v)();
+    void (^test_block_v2)(int, float);
+
+    void (^test_block_v3)(void (^barg)(int));
+
+    a = 77;
+    test_block_v = ^(){ int local=1; printf("a=%d\n",a+local); };
+    test_block_v();
+    a++;
+    test_block_v();
+
+    __block int b;
+
+    b = 88; 
+    test_block_v2 = ^(int x, float f){ printf("b=%d\n",b); };
+    test_block_v2(1,2.0);
+    b++;
+    test_block_v2(3,4.0);
+    return 7;
+}