xref: /llvm-project/clang-tools-extra/unittests/clang-tidy/OverlappingReplacementsTest.cpp (revision 2946cd701067404b99c39fb29dc9c74bd7193eb3)
132af5bc5SAngel Garcia Gomez //===---- OverlappingReplacementsTest.cpp - clang-tidy --------------------===//
232af5bc5SAngel Garcia Gomez //
3*2946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*2946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
5*2946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
632af5bc5SAngel Garcia Gomez //
732af5bc5SAngel Garcia Gomez //===----------------------------------------------------------------------===//
832af5bc5SAngel Garcia Gomez 
932af5bc5SAngel Garcia Gomez #include "ClangTidyTest.h"
1032af5bc5SAngel Garcia Gomez #include "clang/AST/RecursiveASTVisitor.h"
1132af5bc5SAngel Garcia Gomez #include "gtest/gtest.h"
1232af5bc5SAngel Garcia Gomez 
1332af5bc5SAngel Garcia Gomez namespace clang {
1432af5bc5SAngel Garcia Gomez namespace tidy {
1532af5bc5SAngel Garcia Gomez namespace test {
1632af5bc5SAngel Garcia Gomez namespace {
1732af5bc5SAngel Garcia Gomez 
1832af5bc5SAngel Garcia Gomez const char BoundDecl[] = "decl";
1932af5bc5SAngel Garcia Gomez const char BoundIf[] = "if";
2032af5bc5SAngel Garcia Gomez 
2132af5bc5SAngel Garcia Gomez // We define a reduced set of very small checks that allow to test different
2232af5bc5SAngel Garcia Gomez // overlapping situations (no overlapping, replacements partially overlap, etc),
2332af5bc5SAngel Garcia Gomez // as well as different kinds of diagnostics (one check produces several errors,
2432af5bc5SAngel Garcia Gomez // several replacement ranges in an error, etc).
2532af5bc5SAngel Garcia Gomez class UseCharCheck : public ClangTidyCheck {
2632af5bc5SAngel Garcia Gomez public:
UseCharCheck(StringRef CheckName,ClangTidyContext * Context)2732af5bc5SAngel Garcia Gomez   UseCharCheck(StringRef CheckName, ClangTidyContext *Context)
2832af5bc5SAngel Garcia Gomez       : ClangTidyCheck(CheckName, Context) {}
registerMatchers(ast_matchers::MatchFinder * Finder)2932af5bc5SAngel Garcia Gomez   void registerMatchers(ast_matchers::MatchFinder *Finder) override {
3032af5bc5SAngel Garcia Gomez     using namespace ast_matchers;
3132af5bc5SAngel Garcia Gomez     Finder->addMatcher(varDecl(hasType(isInteger())).bind(BoundDecl), this);
3232af5bc5SAngel Garcia Gomez   }
check(const ast_matchers::MatchFinder::MatchResult & Result)3332af5bc5SAngel Garcia Gomez   void check(const ast_matchers::MatchFinder::MatchResult &Result) override {
3432af5bc5SAngel Garcia Gomez     auto *VD = Result.Nodes.getNodeAs<VarDecl>(BoundDecl);
3543465bf3SStephen Kelly     diag(VD->getBeginLoc(), "use char") << FixItHint::CreateReplacement(
3643465bf3SStephen Kelly         CharSourceRange::getTokenRange(VD->getBeginLoc(), VD->getBeginLoc()),
3732af5bc5SAngel Garcia Gomez         "char");
3832af5bc5SAngel Garcia Gomez   }
3932af5bc5SAngel Garcia Gomez };
4032af5bc5SAngel Garcia Gomez 
4132af5bc5SAngel Garcia Gomez class IfFalseCheck : public ClangTidyCheck {
4232af5bc5SAngel Garcia Gomez public:
IfFalseCheck(StringRef CheckName,ClangTidyContext * Context)4332af5bc5SAngel Garcia Gomez   IfFalseCheck(StringRef CheckName, ClangTidyContext *Context)
4432af5bc5SAngel Garcia Gomez       : ClangTidyCheck(CheckName, Context) {}
registerMatchers(ast_matchers::MatchFinder * Finder)4532af5bc5SAngel Garcia Gomez   void registerMatchers(ast_matchers::MatchFinder *Finder) override {
4632af5bc5SAngel Garcia Gomez     using namespace ast_matchers;
4732af5bc5SAngel Garcia Gomez     Finder->addMatcher(ifStmt().bind(BoundIf), this);
4832af5bc5SAngel Garcia Gomez   }
check(const ast_matchers::MatchFinder::MatchResult & Result)4932af5bc5SAngel Garcia Gomez   void check(const ast_matchers::MatchFinder::MatchResult &Result) override {
5032af5bc5SAngel Garcia Gomez     auto *If = Result.Nodes.getNodeAs<IfStmt>(BoundIf);
5132af5bc5SAngel Garcia Gomez     auto *Cond = If->getCond();
5232af5bc5SAngel Garcia Gomez     SourceRange Range = Cond->getSourceRange();
5332af5bc5SAngel Garcia Gomez     if (auto *D = If->getConditionVariable()) {
54c09197e0SStephen Kelly       Range = SourceRange(D->getBeginLoc(), D->getEndLoc());
5532af5bc5SAngel Garcia Gomez     }
5632af5bc5SAngel Garcia Gomez     diag(Range.getBegin(), "the cake is a lie") << FixItHint::CreateReplacement(
5732af5bc5SAngel Garcia Gomez         CharSourceRange::getTokenRange(Range), "false");
5832af5bc5SAngel Garcia Gomez   }
5932af5bc5SAngel Garcia Gomez };
6032af5bc5SAngel Garcia Gomez 
6132af5bc5SAngel Garcia Gomez class RefactorCheck : public ClangTidyCheck {
6232af5bc5SAngel Garcia Gomez public:
RefactorCheck(StringRef CheckName,ClangTidyContext * Context)6332af5bc5SAngel Garcia Gomez   RefactorCheck(StringRef CheckName, ClangTidyContext *Context)
6432af5bc5SAngel Garcia Gomez       : ClangTidyCheck(CheckName, Context), NamePattern("::$") {}
RefactorCheck(StringRef CheckName,ClangTidyContext * Context,StringRef NamePattern)6532af5bc5SAngel Garcia Gomez   RefactorCheck(StringRef CheckName, ClangTidyContext *Context,
6632af5bc5SAngel Garcia Gomez                 StringRef NamePattern)
6732af5bc5SAngel Garcia Gomez       : ClangTidyCheck(CheckName, Context), NamePattern(NamePattern) {}
6832af5bc5SAngel Garcia Gomez   virtual std::string newName(StringRef OldName) = 0;
6932af5bc5SAngel Garcia Gomez 
registerMatchers(ast_matchers::MatchFinder * Finder)7032af5bc5SAngel Garcia Gomez   void registerMatchers(ast_matchers::MatchFinder *Finder) final {
7132af5bc5SAngel Garcia Gomez     using namespace ast_matchers;
7232af5bc5SAngel Garcia Gomez     Finder->addMatcher(varDecl(matchesName(NamePattern)).bind(BoundDecl), this);
7332af5bc5SAngel Garcia Gomez   }
7432af5bc5SAngel Garcia Gomez 
check(const ast_matchers::MatchFinder::MatchResult & Result)7532af5bc5SAngel Garcia Gomez   void check(const ast_matchers::MatchFinder::MatchResult &Result) final {
7632af5bc5SAngel Garcia Gomez     auto *VD = Result.Nodes.getNodeAs<VarDecl>(BoundDecl);
7732af5bc5SAngel Garcia Gomez     std::string NewName = newName(VD->getName());
7832af5bc5SAngel Garcia Gomez 
7916693576SAngel Garcia Gomez     auto Diag = diag(VD->getLocation(), "refactor %0 into %1")
8016693576SAngel Garcia Gomez                 << VD->getName() << NewName
8132af5bc5SAngel Garcia Gomez                 << FixItHint::CreateReplacement(
8232af5bc5SAngel Garcia Gomez                        CharSourceRange::getTokenRange(VD->getLocation(),
8332af5bc5SAngel Garcia Gomez                                                       VD->getLocation()),
8432af5bc5SAngel Garcia Gomez                        NewName);
8532af5bc5SAngel Garcia Gomez 
8632af5bc5SAngel Garcia Gomez     class UsageVisitor : public RecursiveASTVisitor<UsageVisitor> {
8732af5bc5SAngel Garcia Gomez     public:
8832af5bc5SAngel Garcia Gomez       UsageVisitor(const ValueDecl *VD, StringRef NewName,
8932af5bc5SAngel Garcia Gomez                    DiagnosticBuilder &Diag)
9032af5bc5SAngel Garcia Gomez           : VD(VD), NewName(NewName), Diag(Diag) {}
9132af5bc5SAngel Garcia Gomez       bool VisitDeclRefExpr(DeclRefExpr *E) {
9232af5bc5SAngel Garcia Gomez         if (const ValueDecl *D = E->getDecl()) {
9332af5bc5SAngel Garcia Gomez           if (VD->getCanonicalDecl() == D->getCanonicalDecl()) {
9432af5bc5SAngel Garcia Gomez             Diag << FixItHint::CreateReplacement(
9532af5bc5SAngel Garcia Gomez                 CharSourceRange::getTokenRange(E->getSourceRange()), NewName);
9632af5bc5SAngel Garcia Gomez           }
9732af5bc5SAngel Garcia Gomez         }
9832af5bc5SAngel Garcia Gomez         return RecursiveASTVisitor<UsageVisitor>::VisitDeclRefExpr(E);
9932af5bc5SAngel Garcia Gomez       }
10032af5bc5SAngel Garcia Gomez 
10132af5bc5SAngel Garcia Gomez     private:
10232af5bc5SAngel Garcia Gomez       const ValueDecl *VD;
10332af5bc5SAngel Garcia Gomez       StringRef NewName;
10432af5bc5SAngel Garcia Gomez       DiagnosticBuilder &Diag;
10532af5bc5SAngel Garcia Gomez     };
10632af5bc5SAngel Garcia Gomez 
10732af5bc5SAngel Garcia Gomez     UsageVisitor(VD, NewName, Diag)
10832af5bc5SAngel Garcia Gomez         .TraverseDecl(Result.Context->getTranslationUnitDecl());
10932af5bc5SAngel Garcia Gomez   }
11032af5bc5SAngel Garcia Gomez 
11132af5bc5SAngel Garcia Gomez protected:
11232af5bc5SAngel Garcia Gomez   const std::string NamePattern;
11332af5bc5SAngel Garcia Gomez };
11432af5bc5SAngel Garcia Gomez 
11532af5bc5SAngel Garcia Gomez class StartsWithPotaCheck : public RefactorCheck {
11632af5bc5SAngel Garcia Gomez public:
StartsWithPotaCheck(StringRef CheckName,ClangTidyContext * Context)11732af5bc5SAngel Garcia Gomez   StartsWithPotaCheck(StringRef CheckName, ClangTidyContext *Context)
11832af5bc5SAngel Garcia Gomez       : RefactorCheck(CheckName, Context, "::pota") {}
11932af5bc5SAngel Garcia Gomez 
newName(StringRef OldName)12032af5bc5SAngel Garcia Gomez   std::string newName(StringRef OldName) override {
12132af5bc5SAngel Garcia Gomez     return "toma" + OldName.substr(4).str();
12232af5bc5SAngel Garcia Gomez   }
12332af5bc5SAngel Garcia Gomez };
12432af5bc5SAngel Garcia Gomez 
12532af5bc5SAngel Garcia Gomez class EndsWithTatoCheck : public RefactorCheck {
12632af5bc5SAngel Garcia Gomez public:
EndsWithTatoCheck(StringRef CheckName,ClangTidyContext * Context)12732af5bc5SAngel Garcia Gomez   EndsWithTatoCheck(StringRef CheckName, ClangTidyContext *Context)
12832af5bc5SAngel Garcia Gomez       : RefactorCheck(CheckName, Context, "tato$") {}
12932af5bc5SAngel Garcia Gomez 
newName(StringRef OldName)13032af5bc5SAngel Garcia Gomez   std::string newName(StringRef OldName) override {
13132af5bc5SAngel Garcia Gomez     return OldName.substr(0, OldName.size() - 4).str() + "melo";
13232af5bc5SAngel Garcia Gomez   }
13332af5bc5SAngel Garcia Gomez };
13432af5bc5SAngel Garcia Gomez 
13532af5bc5SAngel Garcia Gomez } // namespace
13632af5bc5SAngel Garcia Gomez 
TEST(OverlappingReplacementsTest,UseCharCheckTest)13732af5bc5SAngel Garcia Gomez TEST(OverlappingReplacementsTest, UseCharCheckTest) {
13832af5bc5SAngel Garcia Gomez   const char Code[] =
13932af5bc5SAngel Garcia Gomez       R"(void f() {
14032af5bc5SAngel Garcia Gomez   int a = 0;
14132af5bc5SAngel Garcia Gomez   if (int b = 0) {
14232af5bc5SAngel Garcia Gomez     int c = a;
14332af5bc5SAngel Garcia Gomez   }
14432af5bc5SAngel Garcia Gomez })";
14532af5bc5SAngel Garcia Gomez 
14632af5bc5SAngel Garcia Gomez   const char CharFix[] =
14732af5bc5SAngel Garcia Gomez       R"(void f() {
14832af5bc5SAngel Garcia Gomez   char a = 0;
14932af5bc5SAngel Garcia Gomez   if (char b = 0) {
15032af5bc5SAngel Garcia Gomez     char c = a;
15132af5bc5SAngel Garcia Gomez   }
15232af5bc5SAngel Garcia Gomez })";
15332af5bc5SAngel Garcia Gomez   EXPECT_EQ(CharFix, runCheckOnCode<UseCharCheck>(Code));
15432af5bc5SAngel Garcia Gomez }
15532af5bc5SAngel Garcia Gomez 
TEST(OverlappingReplacementsTest,IfFalseCheckTest)15632af5bc5SAngel Garcia Gomez TEST(OverlappingReplacementsTest, IfFalseCheckTest) {
15732af5bc5SAngel Garcia Gomez   const char Code[] =
15832af5bc5SAngel Garcia Gomez       R"(void f() {
15932af5bc5SAngel Garcia Gomez   int potato = 0;
16032af5bc5SAngel Garcia Gomez   if (int b = 0) {
16132af5bc5SAngel Garcia Gomez     int c = potato;
16232af5bc5SAngel Garcia Gomez   } else if (true) {
16332af5bc5SAngel Garcia Gomez     int d = 0;
16432af5bc5SAngel Garcia Gomez   }
16532af5bc5SAngel Garcia Gomez })";
16632af5bc5SAngel Garcia Gomez 
16732af5bc5SAngel Garcia Gomez   const char IfFix[] =
16832af5bc5SAngel Garcia Gomez       R"(void f() {
16932af5bc5SAngel Garcia Gomez   int potato = 0;
17032af5bc5SAngel Garcia Gomez   if (false) {
17132af5bc5SAngel Garcia Gomez     int c = potato;
17232af5bc5SAngel Garcia Gomez   } else if (false) {
17332af5bc5SAngel Garcia Gomez     int d = 0;
17432af5bc5SAngel Garcia Gomez   }
17532af5bc5SAngel Garcia Gomez })";
17632af5bc5SAngel Garcia Gomez   EXPECT_EQ(IfFix, runCheckOnCode<IfFalseCheck>(Code));
17732af5bc5SAngel Garcia Gomez }
17832af5bc5SAngel Garcia Gomez 
TEST(OverlappingReplacementsTest,StartsWithCheckTest)17932af5bc5SAngel Garcia Gomez TEST(OverlappingReplacementsTest, StartsWithCheckTest) {
18032af5bc5SAngel Garcia Gomez   const char Code[] =
18132af5bc5SAngel Garcia Gomez       R"(void f() {
18232af5bc5SAngel Garcia Gomez   int a = 0;
18332af5bc5SAngel Garcia Gomez   int potato = 0;
18432af5bc5SAngel Garcia Gomez   if (int b = 0) {
18532af5bc5SAngel Garcia Gomez     int c = potato;
18632af5bc5SAngel Garcia Gomez   } else if (true) {
18732af5bc5SAngel Garcia Gomez     int d = 0;
18832af5bc5SAngel Garcia Gomez   }
18932af5bc5SAngel Garcia Gomez })";
19032af5bc5SAngel Garcia Gomez 
19132af5bc5SAngel Garcia Gomez   const char StartsFix[] =
19232af5bc5SAngel Garcia Gomez       R"(void f() {
19332af5bc5SAngel Garcia Gomez   int a = 0;
19432af5bc5SAngel Garcia Gomez   int tomato = 0;
19532af5bc5SAngel Garcia Gomez   if (int b = 0) {
19632af5bc5SAngel Garcia Gomez     int c = tomato;
19732af5bc5SAngel Garcia Gomez   } else if (true) {
19832af5bc5SAngel Garcia Gomez     int d = 0;
19932af5bc5SAngel Garcia Gomez   }
20032af5bc5SAngel Garcia Gomez })";
20132af5bc5SAngel Garcia Gomez   EXPECT_EQ(StartsFix, runCheckOnCode<StartsWithPotaCheck>(Code));
20232af5bc5SAngel Garcia Gomez }
20332af5bc5SAngel Garcia Gomez 
TEST(OverlappingReplacementsTest,EndsWithCheckTest)20432af5bc5SAngel Garcia Gomez TEST(OverlappingReplacementsTest, EndsWithCheckTest) {
20532af5bc5SAngel Garcia Gomez   const char Code[] =
20632af5bc5SAngel Garcia Gomez       R"(void f() {
20732af5bc5SAngel Garcia Gomez   int a = 0;
20832af5bc5SAngel Garcia Gomez   int potato = 0;
20932af5bc5SAngel Garcia Gomez   if (int b = 0) {
21032af5bc5SAngel Garcia Gomez     int c = potato;
21132af5bc5SAngel Garcia Gomez   } else if (true) {
21232af5bc5SAngel Garcia Gomez     int d = 0;
21332af5bc5SAngel Garcia Gomez   }
21432af5bc5SAngel Garcia Gomez })";
21532af5bc5SAngel Garcia Gomez 
21632af5bc5SAngel Garcia Gomez   const char EndsFix[] =
21732af5bc5SAngel Garcia Gomez       R"(void f() {
21832af5bc5SAngel Garcia Gomez   int a = 0;
21932af5bc5SAngel Garcia Gomez   int pomelo = 0;
22032af5bc5SAngel Garcia Gomez   if (int b = 0) {
22132af5bc5SAngel Garcia Gomez     int c = pomelo;
22232af5bc5SAngel Garcia Gomez   } else if (true) {
22332af5bc5SAngel Garcia Gomez     int d = 0;
22432af5bc5SAngel Garcia Gomez   }
22532af5bc5SAngel Garcia Gomez })";
22632af5bc5SAngel Garcia Gomez   EXPECT_EQ(EndsFix, runCheckOnCode<EndsWithTatoCheck>(Code));
22732af5bc5SAngel Garcia Gomez }
22832af5bc5SAngel Garcia Gomez 
TEST(OverlappingReplacementTest,ReplacementsDoNotOverlap)22932af5bc5SAngel Garcia Gomez TEST(OverlappingReplacementTest, ReplacementsDoNotOverlap) {
23032af5bc5SAngel Garcia Gomez   std::string Res;
23132af5bc5SAngel Garcia Gomez   const char Code[] =
23232af5bc5SAngel Garcia Gomez       R"(void f() {
23332af5bc5SAngel Garcia Gomez   int potassium = 0;
23432af5bc5SAngel Garcia Gomez   if (true) {
23532af5bc5SAngel Garcia Gomez     int Potato = potassium;
23632af5bc5SAngel Garcia Gomez   }
23732af5bc5SAngel Garcia Gomez })";
23832af5bc5SAngel Garcia Gomez 
23932af5bc5SAngel Garcia Gomez   const char CharIfFix[] =
24032af5bc5SAngel Garcia Gomez       R"(void f() {
24132af5bc5SAngel Garcia Gomez   char potassium = 0;
24232af5bc5SAngel Garcia Gomez   if (false) {
24332af5bc5SAngel Garcia Gomez     char Potato = potassium;
24432af5bc5SAngel Garcia Gomez   }
24532af5bc5SAngel Garcia Gomez })";
24632af5bc5SAngel Garcia Gomez   Res = runCheckOnCode<UseCharCheck, IfFalseCheck>(Code);
24732af5bc5SAngel Garcia Gomez   EXPECT_EQ(CharIfFix, Res);
24832af5bc5SAngel Garcia Gomez 
24932af5bc5SAngel Garcia Gomez   const char StartsEndsFix[] =
25032af5bc5SAngel Garcia Gomez       R"(void f() {
25132af5bc5SAngel Garcia Gomez   int tomassium = 0;
25232af5bc5SAngel Garcia Gomez   if (true) {
25332af5bc5SAngel Garcia Gomez     int Pomelo = tomassium;
25432af5bc5SAngel Garcia Gomez   }
25532af5bc5SAngel Garcia Gomez })";
25632af5bc5SAngel Garcia Gomez   Res = runCheckOnCode<StartsWithPotaCheck, EndsWithTatoCheck>(Code);
25732af5bc5SAngel Garcia Gomez   EXPECT_EQ(StartsEndsFix, Res);
25832af5bc5SAngel Garcia Gomez 
25932af5bc5SAngel Garcia Gomez   const char CharIfStartsEndsFix[] =
26032af5bc5SAngel Garcia Gomez       R"(void f() {
26132af5bc5SAngel Garcia Gomez   char tomassium = 0;
26232af5bc5SAngel Garcia Gomez   if (false) {
26332af5bc5SAngel Garcia Gomez     char Pomelo = tomassium;
26432af5bc5SAngel Garcia Gomez   }
26532af5bc5SAngel Garcia Gomez })";
26632af5bc5SAngel Garcia Gomez   Res = runCheckOnCode<UseCharCheck, IfFalseCheck, StartsWithPotaCheck,
26732af5bc5SAngel Garcia Gomez                        EndsWithTatoCheck>(Code);
26832af5bc5SAngel Garcia Gomez   EXPECT_EQ(CharIfStartsEndsFix, Res);
26932af5bc5SAngel Garcia Gomez }
27032af5bc5SAngel Garcia Gomez 
TEST(OverlappingReplacementsTest,ReplacementInsideOtherReplacement)27132af5bc5SAngel Garcia Gomez TEST(OverlappingReplacementsTest, ReplacementInsideOtherReplacement) {
27232af5bc5SAngel Garcia Gomez   std::string Res;
27332af5bc5SAngel Garcia Gomez   const char Code[] =
27432af5bc5SAngel Garcia Gomez       R"(void f() {
27532af5bc5SAngel Garcia Gomez   if (char potato = 0) {
27632af5bc5SAngel Garcia Gomez   } else if (int a = 0) {
27732af5bc5SAngel Garcia Gomez     char potato = 0;
27832af5bc5SAngel Garcia Gomez     if (potato) potato;
27932af5bc5SAngel Garcia Gomez   }
28032af5bc5SAngel Garcia Gomez })";
28132af5bc5SAngel Garcia Gomez 
28232af5bc5SAngel Garcia Gomez   // Apply the UseCharCheck together with the IfFalseCheck.
28332af5bc5SAngel Garcia Gomez   //
28416693576SAngel Garcia Gomez   // The 'If' fix contains the other, so that is the one that has to be applied.
28532af5bc5SAngel Garcia Gomez   // } else if (int a = 0) {
28632af5bc5SAngel Garcia Gomez   //            ^^^ -> char
28732af5bc5SAngel Garcia Gomez   //            ~~~~~~~~~ -> false
28832af5bc5SAngel Garcia Gomez   const char CharIfFix[] =
28932af5bc5SAngel Garcia Gomez       R"(void f() {
29032af5bc5SAngel Garcia Gomez   if (false) {
29132af5bc5SAngel Garcia Gomez   } else if (false) {
29232af5bc5SAngel Garcia Gomez     char potato = 0;
29332af5bc5SAngel Garcia Gomez     if (false) potato;
29432af5bc5SAngel Garcia Gomez   }
29532af5bc5SAngel Garcia Gomez })";
29632af5bc5SAngel Garcia Gomez   Res = runCheckOnCode<UseCharCheck, IfFalseCheck>(Code);
29716693576SAngel Garcia Gomez   EXPECT_EQ(CharIfFix, Res);
29816693576SAngel Garcia Gomez   Res = runCheckOnCode<IfFalseCheck, UseCharCheck>(Code);
29916693576SAngel Garcia Gomez   EXPECT_EQ(CharIfFix, Res);
30032af5bc5SAngel Garcia Gomez 
30132af5bc5SAngel Garcia Gomez   // Apply the IfFalseCheck with the StartsWithPotaCheck.
30232af5bc5SAngel Garcia Gomez   //
30332af5bc5SAngel Garcia Gomez   // The 'If' replacement is bigger here.
30432af5bc5SAngel Garcia Gomez   // if (char potato = 0) {
30532af5bc5SAngel Garcia Gomez   //          ^^^^^^ -> tomato
30632af5bc5SAngel Garcia Gomez   //     ~~~~~~~~~~~~~~~ -> false
30732af5bc5SAngel Garcia Gomez   //
30816693576SAngel Garcia Gomez   // But the refactoring is the one that contains the other here:
30932af5bc5SAngel Garcia Gomez   // char potato = 0;
31032af5bc5SAngel Garcia Gomez   //      ^^^^^^ -> tomato
31132af5bc5SAngel Garcia Gomez   // if (potato) potato;
31232af5bc5SAngel Garcia Gomez   //     ^^^^^^  ^^^^^^ -> tomato, tomato
31332af5bc5SAngel Garcia Gomez   //     ~~~~~~ -> false
31432af5bc5SAngel Garcia Gomez   const char IfStartsFix[] =
31532af5bc5SAngel Garcia Gomez       R"(void f() {
31632af5bc5SAngel Garcia Gomez   if (false) {
31732af5bc5SAngel Garcia Gomez   } else if (false) {
31832af5bc5SAngel Garcia Gomez     char tomato = 0;
31932af5bc5SAngel Garcia Gomez     if (tomato) tomato;
32032af5bc5SAngel Garcia Gomez   }
32132af5bc5SAngel Garcia Gomez })";
32232af5bc5SAngel Garcia Gomez   Res = runCheckOnCode<IfFalseCheck, StartsWithPotaCheck>(Code);
32316693576SAngel Garcia Gomez   EXPECT_EQ(IfStartsFix, Res);
32416693576SAngel Garcia Gomez   Res = runCheckOnCode<StartsWithPotaCheck, IfFalseCheck>(Code);
32516693576SAngel Garcia Gomez   EXPECT_EQ(IfStartsFix, Res);
32632af5bc5SAngel Garcia Gomez }
32732af5bc5SAngel Garcia Gomez 
TEST(OverlappingReplacements,TwoReplacementsInsideOne)32816693576SAngel Garcia Gomez TEST(OverlappingReplacements, TwoReplacementsInsideOne) {
32916693576SAngel Garcia Gomez   std::string Res;
33016693576SAngel Garcia Gomez   const char Code[] =
33116693576SAngel Garcia Gomez       R"(void f() {
33216693576SAngel Garcia Gomez   if (int potato = 0) {
33316693576SAngel Garcia Gomez     int a = 0;
33416693576SAngel Garcia Gomez   }
33516693576SAngel Garcia Gomez })";
33616693576SAngel Garcia Gomez 
33716693576SAngel Garcia Gomez   // The two smallest replacements should not be applied.
33816693576SAngel Garcia Gomez   // if (int potato = 0) {
33916693576SAngel Garcia Gomez   //         ^^^^^^ -> tomato
34016693576SAngel Garcia Gomez   //     *** -> char
34116693576SAngel Garcia Gomez   //     ~~~~~~~~~~~~~~ -> false
34216693576SAngel Garcia Gomez   // But other errors from the same checks should not be affected.
34316693576SAngel Garcia Gomez   //   int a = 0;
34416693576SAngel Garcia Gomez   //   *** -> char
34516693576SAngel Garcia Gomez   const char Fix[] =
34616693576SAngel Garcia Gomez       R"(void f() {
34716693576SAngel Garcia Gomez   if (false) {
34816693576SAngel Garcia Gomez     char a = 0;
34916693576SAngel Garcia Gomez   }
35016693576SAngel Garcia Gomez })";
35116693576SAngel Garcia Gomez   Res = runCheckOnCode<UseCharCheck, IfFalseCheck, StartsWithPotaCheck>(Code);
35216693576SAngel Garcia Gomez   EXPECT_EQ(Fix, Res);
35316693576SAngel Garcia Gomez   Res = runCheckOnCode<StartsWithPotaCheck, IfFalseCheck, UseCharCheck>(Code);
35416693576SAngel Garcia Gomez   EXPECT_EQ(Fix, Res);
35516693576SAngel Garcia Gomez }
35616693576SAngel Garcia Gomez 
TEST(OverlappingReplacementsTest,ApplyAtMostOneOfTheChangesWhenPartialOverlapping)35716693576SAngel Garcia Gomez TEST(OverlappingReplacementsTest,
35816693576SAngel Garcia Gomez      ApplyAtMostOneOfTheChangesWhenPartialOverlapping) {
35916693576SAngel Garcia Gomez   std::string Res;
36016693576SAngel Garcia Gomez   const char Code[] =
36116693576SAngel Garcia Gomez       R"(void f() {
36216693576SAngel Garcia Gomez   if (int potato = 0) {
36316693576SAngel Garcia Gomez     int a = potato;
36416693576SAngel Garcia Gomez   }
36516693576SAngel Garcia Gomez })";
36616693576SAngel Garcia Gomez 
36716693576SAngel Garcia Gomez   // These two replacements overlap, but none of them is completely contained
36816693576SAngel Garcia Gomez   // inside the other.
36916693576SAngel Garcia Gomez   // if (int potato = 0) {
37016693576SAngel Garcia Gomez   //         ^^^^^^ -> tomato
37116693576SAngel Garcia Gomez   //     ~~~~~~~~~~~~~~ -> false
37216693576SAngel Garcia Gomez   //   int a = potato;
37316693576SAngel Garcia Gomez   //           ^^^^^^ -> tomato
37416693576SAngel Garcia Gomez   //
37516693576SAngel Garcia Gomez   // The 'StartsWithPotaCheck' fix has endpoints inside the 'IfFalseCheck' fix,
37616693576SAngel Garcia Gomez   // so it is going to be set as inapplicable. The 'if' fix will be applied.
37716693576SAngel Garcia Gomez   const char IfFix[] =
37816693576SAngel Garcia Gomez       R"(void f() {
37916693576SAngel Garcia Gomez   if (false) {
38016693576SAngel Garcia Gomez     int a = potato;
38116693576SAngel Garcia Gomez   }
38216693576SAngel Garcia Gomez })";
38316693576SAngel Garcia Gomez   Res = runCheckOnCode<IfFalseCheck, StartsWithPotaCheck>(Code);
38416693576SAngel Garcia Gomez   EXPECT_EQ(IfFix, Res);
38516693576SAngel Garcia Gomez }
38616693576SAngel Garcia Gomez 
TEST(OverlappingReplacementsTest,TwoErrorsHavePerfectOverlapping)38716693576SAngel Garcia Gomez TEST(OverlappingReplacementsTest, TwoErrorsHavePerfectOverlapping) {
38832af5bc5SAngel Garcia Gomez   std::string Res;
38932af5bc5SAngel Garcia Gomez   const char Code[] =
39032af5bc5SAngel Garcia Gomez       R"(void f() {
39132af5bc5SAngel Garcia Gomez   int potato = 0;
39232af5bc5SAngel Garcia Gomez   potato += potato * potato;
39316693576SAngel Garcia Gomez   if (char a = potato) potato;
39432af5bc5SAngel Garcia Gomez })";
39532af5bc5SAngel Garcia Gomez 
39616693576SAngel Garcia Gomez   // StartsWithPotaCheck will try to refactor 'potato' into 'tomato', and
39716693576SAngel Garcia Gomez   // EndsWithTatoCheck will try to use 'pomelo'. Both fixes have the same set of
39816693576SAngel Garcia Gomez   // ranges. This is a corner case of one error completely containing another:
39916693576SAngel Garcia Gomez   // the other completely contains the first one as well. Both errors are
40016693576SAngel Garcia Gomez   // discarded.
40116693576SAngel Garcia Gomez 
40232af5bc5SAngel Garcia Gomez   Res = runCheckOnCode<StartsWithPotaCheck, EndsWithTatoCheck>(Code);
40316693576SAngel Garcia Gomez   EXPECT_EQ(Code, Res);
40432af5bc5SAngel Garcia Gomez }
40532af5bc5SAngel Garcia Gomez 
40632af5bc5SAngel Garcia Gomez } // namespace test
40732af5bc5SAngel Garcia Gomez } // namespace tidy
40832af5bc5SAngel Garcia Gomez } // namespace clang
409