189 lines · cpp
1//===- unittest/Tooling/FixitTest.cpp ------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "clang/Tooling/FixIt.h"10#include "clang/ASTMatchers/ASTMatchFinder.h"11#include "clang/ASTMatchers/ASTMatchers.h"12#include "clang/Testing/TestAST.h"13#include "gtest/gtest.h"14 15using namespace clang;16 17using tooling::fixit::getText;18using tooling::fixit::createRemoval;19using tooling::fixit::createReplacement;20 21namespace {22 23const CallExpr &onlyCall(ASTContext &Ctx) {24 using namespace ast_matchers;25 auto Calls = match(callExpr().bind(""), Ctx);26 EXPECT_EQ(Calls.size(), 1u);27 return *Calls.front().getNodeAs<CallExpr>("");28}29 30TEST(FixItTest, getText) {31 TestAST AST("void foo(int x, int y) { foo(x, y); }");32 const CallExpr &CE = onlyCall(AST.context());33 EXPECT_EQ("foo(x, y)", getText(CE, AST.context()));34 EXPECT_EQ("foo(x, y)", getText(CE.getSourceRange(), AST.context()));35 EXPECT_EQ("x", getText(*CE.getArg(0), AST.context()));36 EXPECT_EQ("y", getText(*CE.getArg(1), AST.context()));37 38 AST = TestAST("#define APPLY(f, x, y) f(x, y)\n"39 "void foo(int x, int y) { APPLY(foo, x, y); }");40 const CallExpr &CE2 = onlyCall(AST.context());41 EXPECT_EQ("APPLY(foo, x, y)", getText(CE2, AST.context()));42}43 44TEST(FixItTest, getTextWithMacro) {45 TestAST AST("#define F foo(\n"46 "#define OO x, y)\n"47 "void foo(int x, int y) { F OO ; }");48 const CallExpr &CE = onlyCall(AST.context());49 EXPECT_EQ("F OO", getText(CE, AST.context()));50 EXPECT_EQ("", getText(*CE.getArg(0), AST.context()));51 EXPECT_EQ("", getText(*CE.getArg(1), AST.context()));52 53 AST = TestAST("#define FOO(x, y) (void)x; (void)y; foo(x, y);\n"54 "void foo(int x, int y) { FOO(x,y) }");55 const CallExpr &CE2 = onlyCall(AST.context());56 EXPECT_EQ("", getText(CE2, AST.context()));57 EXPECT_EQ("x", getText(*CE2.getArg(0), AST.context()));58 EXPECT_EQ("y", getText(*CE2.getArg(1), AST.context()));59}60 61TEST(FixItTest, createRemoval) {62 TestAST AST("void foo(int x, int y) { foo(x, y); }");63 const CallExpr &CE = onlyCall(AST.context());64 65 FixItHint Hint = createRemoval(CE);66 EXPECT_EQ("foo(x, y)", getText(Hint.RemoveRange.getAsRange(), AST.context()));67 EXPECT_TRUE(Hint.InsertFromRange.isInvalid());68 EXPECT_TRUE(Hint.CodeToInsert.empty());69 70 FixItHint Hint0 = createRemoval(*CE.getArg(0));71 EXPECT_EQ("x", getText(Hint0.RemoveRange.getAsRange(), AST.context()));72 EXPECT_TRUE(Hint0.InsertFromRange.isInvalid());73 EXPECT_TRUE(Hint0.CodeToInsert.empty());74 75 FixItHint Hint1 = createRemoval(*CE.getArg(1));76 EXPECT_EQ("y", getText(Hint1.RemoveRange.getAsRange(), AST.context()));77 EXPECT_TRUE(Hint1.InsertFromRange.isInvalid());78 EXPECT_TRUE(Hint1.CodeToInsert.empty());79 80 AST = TestAST("void foo(int x, int y) { foo(x + y, y + x); }");81 const CallExpr &CE2 = onlyCall(AST.context());82 Hint0 = createRemoval(*CE2.getArg(0));83 EXPECT_EQ("x + y", getText(Hint0.RemoveRange.getAsRange(), AST.context()));84 85 Hint1 = createRemoval(*CE2.getArg(1));86 EXPECT_EQ("y + x", getText(Hint1.RemoveRange.getAsRange(), AST.context()));87}88 89TEST(FixItTest, createRemovalWithMacro) {90 TestAST AST("#define FOO foo(1, 1)\n"91 "void foo(int x, int y) { FOO; }");92 const CallExpr &CE = onlyCall(AST.context());93 FixItHint Hint = createRemoval(CE);94 EXPECT_EQ("FOO", getText(Hint.RemoveRange.getAsRange(), AST.context()));95 EXPECT_TRUE(Hint.InsertFromRange.isInvalid());96 EXPECT_TRUE(Hint.CodeToInsert.empty());97 98 FixItHint Hint0 = createRemoval(*CE.getArg(0));99 EXPECT_EQ("input.mm:2:26 <Spelling=input.mm:1:17>",100 Hint0.RemoveRange.getBegin().printToString(AST.sourceManager()));101 EXPECT_EQ("input.mm:2:26 <Spelling=input.mm:1:17>",102 Hint0.RemoveRange.getEnd().printToString(AST.sourceManager()));103 EXPECT_TRUE(Hint0.InsertFromRange.isInvalid());104 EXPECT_TRUE(Hint0.CodeToInsert.empty());105 106 FixItHint Hint1 = createRemoval(*CE.getArg(1));107 EXPECT_EQ("input.mm:2:26 <Spelling=input.mm:1:20>",108 Hint1.RemoveRange.getBegin().printToString(AST.sourceManager()));109 EXPECT_EQ("input.mm:2:26 <Spelling=input.mm:1:20>",110 Hint1.RemoveRange.getEnd().printToString(AST.sourceManager()));111 EXPECT_TRUE(Hint1.InsertFromRange.isInvalid());112 EXPECT_TRUE(Hint1.CodeToInsert.empty());113 114 AST = TestAST("#define FOO(x, y) (void)x; (void)y; foo(x, y);\n"115 "void foo(int x, int y) { FOO(x,y) }");116 const CallExpr &CE2 = onlyCall(AST.context());117 Hint = createRemoval(CE2);118 EXPECT_EQ("input.mm:2:26 <Spelling=input.mm:1:37>",119 Hint.RemoveRange.getBegin().printToString(AST.sourceManager()));120 EXPECT_EQ("input.mm:2:26 <Spelling=input.mm:1:45>",121 Hint.RemoveRange.getEnd().printToString(AST.sourceManager()));122 EXPECT_TRUE(Hint.InsertFromRange.isInvalid());123 EXPECT_TRUE(Hint.CodeToInsert.empty());124}125 126TEST(FixItTest, createReplacement) {127 for (const char *Code : {128 "void foo(int x, int y) { foo(x, y); }",129 130 "#define APPLY(f, x, y) f(x, y)\n"131 "void foo(int x, int y) { APPLY(foo, x, y); }",132 133 "#define APPLY(f, P) f(P)\n"134 "#define PAIR(x, y) x, y\n"135 "void foo(int x, int y) { APPLY(foo, PAIR(x, y)); }\n",136 }) {137 TestAST AST(Code);138 const CallExpr &CE = onlyCall(AST.context());139 const Expr *P0 = CE.getArg(0);140 const Expr *P1 = CE.getArg(1);141 FixItHint Hint0 = createReplacement(*P0, *P1, AST.context());142 FixItHint Hint1 = createReplacement(*P1, *P0, AST.context());143 144 // Validate Hint0 fields.145 EXPECT_EQ("x", getText(Hint0.RemoveRange.getAsRange(), AST.context()));146 EXPECT_TRUE(Hint0.InsertFromRange.isInvalid());147 EXPECT_EQ(Hint0.CodeToInsert, "y");148 149 // Validate Hint1 fields.150 EXPECT_EQ("y", getText(Hint1.RemoveRange.getAsRange(), AST.context()));151 EXPECT_TRUE(Hint1.InsertFromRange.isInvalid());152 EXPECT_EQ(Hint1.CodeToInsert, "x");153 }154}155 156TEST(FixItTest, createReplacementWithMacro) {157 TestAST AST("#define FOO foo(1, 1)\n"158 "void foo(int x, int y) { FOO; }");159 const CallExpr &CE = onlyCall(AST.context());160 FixItHint Hint =161 createReplacement(*CE.getArg(0), *CE.getArg(1), AST.context());162 EXPECT_EQ("input.mm:2:26 <Spelling=input.mm:1:17>",163 Hint.RemoveRange.getBegin().printToString(AST.sourceManager()));164 EXPECT_EQ("input.mm:2:26 <Spelling=input.mm:1:17>",165 Hint.RemoveRange.getEnd().printToString(AST.sourceManager()));166 EXPECT_TRUE(Hint.InsertFromRange.isInvalid());167 EXPECT_TRUE(Hint.CodeToInsert.empty());168 169 AST = TestAST("#define FOO(x, y) (void)x; (void)y; foo(x, y);\n"170 "void foo(int x, int y) { FOO(x,y) }");171 const CallExpr &CE2 = onlyCall(AST.context());172 Hint = createReplacement(*CE2.getArg(0), *CE2.getArg(1), AST.context());173 EXPECT_EQ("input.mm:2:26 <Spelling=input.mm:2:30>",174 Hint.RemoveRange.getEnd().printToString(AST.sourceManager()));175 EXPECT_EQ("input.mm:2:26 <Spelling=input.mm:2:30>",176 Hint.RemoveRange.getBegin().printToString(AST.sourceManager()));177 EXPECT_TRUE(Hint.InsertFromRange.isInvalid());178 EXPECT_EQ("y", Hint.CodeToInsert);179 180 AST = TestAST("void foo(int x, int y) { foo(x + y, y + x); }");181 const CallExpr &CE3 = onlyCall(AST.context());182 Hint = createReplacement(*CE3.getArg(0), *CE3.getArg(1), AST.context());183 EXPECT_EQ("x + y", getText(Hint.RemoveRange.getAsRange(), AST.context()));184 EXPECT_TRUE(Hint.InsertFromRange.isInvalid());185 EXPECT_EQ("y + x", Hint.CodeToInsert);186}187 188} // end anonymous namespace189