]> granicus.if.org Git - clang/commitdiff
[refactor] AST selection tree should contain syntactic form
authorAlex Lorenz <arphaman@gmail.com>
Wed, 30 Aug 2017 15:00:27 +0000 (15:00 +0000)
committerAlex Lorenz <arphaman@gmail.com>
Wed, 30 Aug 2017 15:00:27 +0000 (15:00 +0000)
of PseudoObjectExpr

The AST selection finder now constructs a selection tree that contains only the
syntactic form of PseudoObjectExpr. This form of selection tree is more
meaningful when doing downstream analysis as we're interested in the syntactic
features of the AST and the correct lexical parent relation.

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

lib/Tooling/Refactoring/ASTSelection.cpp
unittests/Tooling/ASTSelectionTest.cpp

index 4fa0086697816422573871abb7939c42bfb5b09e..841b9e384d02e834ddeefbd1d0e5241340b6de9e 100644 (file)
@@ -10,6 +10,7 @@
 #include "clang/Tooling/Refactoring/ASTSelection.h"
 #include "clang/AST/LexicallyOrderedRecursiveASTVisitor.h"
 #include "clang/Lex/Lexer.h"
+#include "llvm/Support/SaveAndRestore.h"
 
 using namespace clang;
 using namespace tooling;
@@ -60,6 +61,21 @@ public:
     return std::move(Result);
   }
 
+  bool TraversePseudoObjectExpr(PseudoObjectExpr *E) {
+    // Avoid traversing the semantic expressions. They should be handled by
+    // looking through the appropriate opaque expressions in order to build
+    // a meaningful selection tree.
+    llvm::SaveAndRestore<bool> LookThrough(LookThroughOpaqueValueExprs, true);
+    return TraverseStmt(E->getSyntacticForm());
+  }
+
+  bool TraverseOpaqueValueExpr(OpaqueValueExpr *E) {
+    if (!LookThroughOpaqueValueExprs)
+      return true;
+    llvm::SaveAndRestore<bool> LookThrough(LookThroughOpaqueValueExprs, false);
+    return TraverseStmt(E->getSourceExpr());
+  }
+
   bool TraverseDecl(Decl *D) {
     if (isa<TranslationUnitDecl>(D))
       return LexicallyOrderedRecursiveASTVisitor::TraverseDecl(D);
@@ -97,6 +113,8 @@ public:
   bool TraverseStmt(Stmt *S) {
     if (!S)
       return true;
+    if (auto *Opaque = dyn_cast<OpaqueValueExpr>(S))
+      return TraverseOpaqueValueExpr(Opaque);
     // FIXME (Alex Lorenz): Improve handling for macro locations.
     SourceSelectionKind SelectionKind =
         selectionKindFor(CharSourceRange::getTokenRange(S->getSourceRange()));
@@ -149,6 +167,10 @@ private:
   FileID TargetFile;
   const ASTContext &Context;
   std::vector<SelectedASTNode> SelectionStack;
+  /// Controls whether we can traverse through the OpaqueValueExpr. This is
+  /// typically enabled during the traversal of syntactic form for
+  /// PseudoObjectExprs.
+  bool LookThroughOpaqueValueExprs = false;
 };
 
 } // end anonymous namespace
index bde052e849ca670691461185e666f58369395a70..6b76bf06cb59942e9a301c014bdb17332b28aadc 100644 (file)
@@ -513,4 +513,140 @@ TEST(ASTSelectionFinder, CorrectEndForObjectiveCImplementation) {
                        SelectionFinderVisitor::Lang_OBJC);
 }
 
+const SelectedASTNode &checkFnBody(const Optional<SelectedASTNode> &Node,
+                                   StringRef Name) {
+  EXPECT_TRUE(Node);
+  EXPECT_EQ(Node->Children.size(), 1u);
+  const auto &Fn = checkNode<FunctionDecl>(
+      Node->Children[0], SourceSelectionKind::ContainsSelection,
+      /*NumChildren=*/1, Name);
+  return checkNode<CompoundStmt>(Fn.Children[0],
+                                 SourceSelectionKind::ContainsSelection,
+                                 /*NumChildren=*/1);
+}
+
+TEST(ASTSelectionFinder, SelectObjectiveCPseudoObjectExprs) {
+  StringRef Source = R"(
+@interface I
+@property(readwrite) int prop;
+@end
+void selectProp(I *i) {
+(void)i.prop;
+i.prop = 21;
+}
+
+typedef unsigned int size_t;
+@interface NSMutableArray
+- (id)objectAtIndexedSubscript:(size_t)index;
+- (void)setObject:(id)object atIndexedSubscript:(size_t)index;
+@end
+
+void selectSubscript(NSMutableArray *array, I *i) {
+  (void)array[10];
+  array[i.prop] = i;
+}
+)";
+  // Just 'i.prop'.
+  findSelectedASTNodes(
+      Source, {6, 7}, FileRange{{6, 7}, {6, 13}},
+      [](Optional<SelectedASTNode> Node) {
+        const auto &CS = checkFnBody(Node, /*Name=*/"selectProp");
+        const auto &CCast = checkNode<CStyleCastExpr>(
+            CS.Children[0], SourceSelectionKind::ContainsSelection,
+            /*NumChildren=*/1);
+        const auto &POE = checkNode<PseudoObjectExpr>(
+            CCast.Children[0], SourceSelectionKind::ContainsSelection,
+            /*NumChildren=*/1);
+        const auto &PRE = checkNode<ObjCPropertyRefExpr>(
+            POE.Children[0], SourceSelectionKind::ContainsSelection,
+            /*NumChildren=*/1);
+        const auto &Cast = checkNode<ImplicitCastExpr>(
+            PRE.Children[0], SourceSelectionKind::InsideSelection,
+            /*NumChildren=*/1);
+        checkNode<DeclRefExpr>(Cast.Children[0],
+                               SourceSelectionKind::InsideSelection);
+      },
+      SelectionFinderVisitor::Lang_OBJC);
+  // Just 'i.prop = 21'
+  findSelectedASTNodes(
+      Source, {7, 1}, FileRange{{7, 1}, {7, 12}},
+      [](Optional<SelectedASTNode> Node) {
+        const auto &CS = checkFnBody(Node, /*Name=*/"selectProp");
+        const auto &POE = checkNode<PseudoObjectExpr>(
+            CS.Children[0], SourceSelectionKind::ContainsSelection,
+            /*NumChildren=*/1);
+        const auto &BinOp = checkNode<BinaryOperator>(
+            POE.Children[0], SourceSelectionKind::ContainsSelection,
+            /*NumChildren=*/2);
+        const auto &PRE = checkNode<ObjCPropertyRefExpr>(
+            BinOp.Children[0], SourceSelectionKind::InsideSelection,
+            /*NumChildren=*/1);
+        const auto &Cast = checkNode<ImplicitCastExpr>(
+            PRE.Children[0], SourceSelectionKind::InsideSelection,
+            /*NumChildren=*/1);
+        checkNode<DeclRefExpr>(Cast.Children[0],
+                               SourceSelectionKind::InsideSelection);
+        checkNode<IntegerLiteral>(BinOp.Children[1],
+                                  SourceSelectionKind::InsideSelection);
+      },
+      SelectionFinderVisitor::Lang_OBJC);
+  // Just 'array[10]'
+  findSelectedASTNodes(
+      Source, {17, 9}, FileRange{{17, 9}, {17, 18}},
+      [](Optional<SelectedASTNode> Node) {
+        const auto &CS = checkFnBody(Node, /*Name=*/"selectSubscript");
+        const auto &CCast = checkNode<CStyleCastExpr>(
+            CS.Children[0], SourceSelectionKind::ContainsSelection,
+            /*NumChildren=*/1);
+        const auto &POE = checkNode<PseudoObjectExpr>(
+            CCast.Children[0], SourceSelectionKind::ContainsSelection,
+            /*NumChildren=*/1);
+        const auto &SRE = checkNode<ObjCSubscriptRefExpr>(
+            POE.Children[0], SourceSelectionKind::ContainsSelection,
+            /*NumChildren=*/2);
+        const auto &Cast = checkNode<ImplicitCastExpr>(
+            SRE.Children[0], SourceSelectionKind::InsideSelection,
+            /*NumChildren=*/1);
+        checkNode<DeclRefExpr>(Cast.Children[0],
+                               SourceSelectionKind::InsideSelection);
+        checkNode<IntegerLiteral>(SRE.Children[1],
+                                  SourceSelectionKind::InsideSelection);
+      },
+      SelectionFinderVisitor::Lang_OBJC);
+  // Just 'array[i.prop] = array'
+  findSelectedASTNodes(
+      Source, {18, 3}, FileRange{{18, 3}, {18, 20}},
+      [](Optional<SelectedASTNode> Node) {
+        const auto &CS = checkFnBody(Node, /*Name=*/"selectSubscript");
+        const auto &POE = checkNode<PseudoObjectExpr>(
+            CS.Children[0], SourceSelectionKind::ContainsSelection,
+            /*NumChildren=*/1);
+        const auto &BinOp = checkNode<BinaryOperator>(
+            POE.Children[0], SourceSelectionKind::ContainsSelection,
+            /*NumChildren=*/2);
+        const auto &SRE = checkNode<ObjCSubscriptRefExpr>(
+            BinOp.Children[0], SourceSelectionKind::InsideSelection,
+            /*NumChildren=*/2);
+        const auto &Cast = checkNode<ImplicitCastExpr>(
+            SRE.Children[0], SourceSelectionKind::InsideSelection,
+            /*NumChildren=*/1);
+        checkNode<DeclRefExpr>(Cast.Children[0],
+                               SourceSelectionKind::InsideSelection);
+        const auto &POE2 = checkNode<PseudoObjectExpr>(
+            SRE.Children[1], SourceSelectionKind::InsideSelection,
+            /*NumChildren=*/1);
+        const auto &PRE = checkNode<ObjCPropertyRefExpr>(
+            POE2.Children[0], SourceSelectionKind::InsideSelection,
+            /*NumChildren=*/1);
+        const auto &Cast2 = checkNode<ImplicitCastExpr>(
+            PRE.Children[0], SourceSelectionKind::InsideSelection,
+            /*NumChildren=*/1);
+        checkNode<DeclRefExpr>(Cast2.Children[0],
+                               SourceSelectionKind::InsideSelection);
+        checkNode<DeclRefExpr>(BinOp.Children[1],
+                               SourceSelectionKind::InsideSelection);
+      },
+      SelectionFinderVisitor::Lang_OBJC);
+}
+
 } // end anonymous namespace