brintos

brintos / llvm-project-archived public Read only

0
0
Text · 12.2 KiB · 0057691 Raw
381 lines · cpp
1//===--- IncludeCleanerTest.cpp - clang-tidy -----------------------------===//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 "ClangTidyDiagnosticConsumer.h"10#include "ClangTidyOptions.h"11#include "ClangTidyTest.h"12#include "misc/IncludeCleanerCheck.h"13#include "llvm/ADT/SmallString.h"14#include "llvm/ADT/StringRef.h"15#include "llvm/Support/FormatVariadic.h"16#include "llvm/Support/Path.h"17#include "llvm/Support/Regex.h"18#include "llvm/Testing/Annotations/Annotations.h"19#include "gmock/gmock.h"20#include "gtest/gtest.h"21#include <initializer_list>22 23#include <optional>24#include <vector>25 26using namespace clang::tidy::misc;27 28namespace clang {29namespace tidy {30namespace test {31namespace {32 33std::string34appendPathFileSystemIndependent(std::initializer_list<std::string> Segments) {35  llvm::SmallString<32> Result;36  for (const auto &Segment : Segments)37    llvm::sys::path::append(Result, llvm::sys::path::Style::native, Segment);38  return std::string(Result.str());39}40 41TEST(IncludeCleanerCheckTest, BasicUnusedIncludes) {42  const char *PreCode = R"(43#include "bar.h"44#include <vector>45#include "bar.h"46)";47  const char *PostCode = "\n";48 49  std::vector<ClangTidyError> Errors;50  EXPECT_EQ(PostCode,51            runCheckOnCode<IncludeCleanerCheck>(52                PreCode, &Errors, "file.cpp", {}, ClangTidyOptions(),53                {{"bar.h", "#pragma once"}, {"vector", "#pragma once"}}));54}55 56TEST(IncludeCleanerCheckTest, SuppressUnusedIncludes) {57  const char *PreCode = R"(58#include "bar.h"59#include "foo/qux.h"60#include "baz/qux/qux.h"61#include <vector>62#include <list>63)";64 65  const char *PostCode = R"(66#include "bar.h"67#include "foo/qux.h"68#include <vector>69#include <list>70)";71 72  std::vector<ClangTidyError> Errors;73  ClangTidyOptions Opts;74  Opts.CheckOptions["test-check-0.IgnoreHeaders"] = llvm::StringRef{75      llvm::formatv("bar.h;{0};{1};vector;<list>;",76                    llvm::Regex::escape(77                        appendPathFileSystemIndependent({"foo", "qux.h"})),78                    llvm::Regex::escape(79                        appendPathFileSystemIndependent({"baz", "qux"})))};80  EXPECT_EQ(81      PostCode,82      runCheckOnCode<IncludeCleanerCheck>(83          PreCode, &Errors, "file.cpp", {}, Opts,84          {{"bar.h", "#pragma once"},85           {"vector", "#pragma once"},86           {"list", "#pragma once"},87           {appendPathFileSystemIndependent({"foo", "qux.h"}), "#pragma once"},88           {appendPathFileSystemIndependent({"baz", "qux", "qux.h"}),89            "#pragma once"}}));90}91 92TEST(IncludeCleanerCheckTest, BasicMissingIncludes) {93  const char *PreCode = R"(94#include "bar.h"95 96int BarResult = bar();97int BazResult = baz();98)";99  const char *PostCode = R"(100#include "bar.h"101#include "baz.h"102 103int BarResult = bar();104int BazResult = baz();105)";106 107  std::vector<ClangTidyError> Errors;108  EXPECT_EQ(PostCode, runCheckOnCode<IncludeCleanerCheck>(109                          PreCode, &Errors, "file.cpp", {}, ClangTidyOptions(),110                          {{"bar.h", R"(#pragma once111                              #include "baz.h"112                              int bar();113                           )"},114                           {"baz.h", R"(#pragma once115                              int baz();116                           )"}}));117}118 119TEST(IncludeCleanerCheckTest, DedupsMissingIncludes) {120  llvm::Annotations Code(R"(121#include "baz.h" // IWYU pragma: keep122 123int BarResult1 = $diag1^bar();124int BarResult2 = $diag2^bar();)");125 126  {127    std::vector<ClangTidyError> Errors;128    runCheckOnCode<IncludeCleanerCheck>(Code.code(), &Errors, "file.cpp", {},129                                        ClangTidyOptions(),130                                        {{"baz.h", R"(#pragma once131                              #include "bar.h"132                           )"},133                                         {"bar.h", R"(#pragma once134                              int bar();135                           )"}});136    ASSERT_THAT(Errors.size(), testing::Eq(1U));137    EXPECT_EQ(Errors.front().Message.Message,138              "no header providing \"bar\" is directly included");139    EXPECT_EQ(Errors.front().Message.FileOffset, Code.point("diag1"));140  }141  {142    std::vector<ClangTidyError> Errors;143    ClangTidyOptions Opts;144    Opts.CheckOptions["test-check-0.DeduplicateFindings"] = "false";145    runCheckOnCode<IncludeCleanerCheck>(Code.code(), &Errors, "file.cpp", {},146                                        Opts,147                                        {{"baz.h", R"(#pragma once148                              #include "bar.h"149                           )"},150                                         {"bar.h", R"(#pragma once151                              int bar();152                           )"}});153    ASSERT_THAT(Errors.size(), testing::Eq(2U));154    EXPECT_EQ(Errors.front().Message.Message,155              "no header providing \"bar\" is directly included");156    EXPECT_EQ(Errors.front().Message.FileOffset, Code.point("diag1"));157    EXPECT_EQ(Errors.back().Message.Message,158              "no header providing \"bar\" is directly included");159    EXPECT_EQ(Errors.back().Message.FileOffset, Code.point("diag2"));160  }161}162 163TEST(IncludeCleanerCheckTest, SuppressMissingIncludes) {164  const char *PreCode = R"(165#include "bar.h"166 167int BarResult = bar();168int BazResult = baz();169int QuxResult = qux();170int PrivResult = test();171std::vector x;172)";173 174  ClangTidyOptions Opts;175  Opts.CheckOptions["test-check-0.IgnoreHeaders"] = llvm::StringRef{176      "public.h;<vector>;baz.h;" +177      llvm::Regex::escape(appendPathFileSystemIndependent({"foo", "qux.h"}))};178  std::vector<ClangTidyError> Errors;179  EXPECT_EQ(PreCode, runCheckOnCode<IncludeCleanerCheck>(180                         PreCode, &Errors, "file.cpp", {}, Opts,181                         {{"bar.h", R"(#pragma once182                              #include "baz.h"183                              #include "foo/qux.h"184                              #include "private.h"185                              int bar();186                              namespace std { struct vector {}; }187                           )"},188                          {"baz.h", R"(#pragma once189                              int baz();190                           )"},191                          {"private.h", R"(#pragma once192                              // IWYU pragma: private, include "public.h"193                              int test();194                           )"},195                          {appendPathFileSystemIndependent({"foo", "qux.h"}),196                           R"(#pragma once197                              int qux();198                           )"}}));199}200 201TEST(IncludeCleanerCheckTest, MultipleTimeMissingInclude) {202  const char *PreCode = R"(203#include "bar.h"204 205int BarResult = bar();206int BazResult_0 = baz_0();207int BazResult_1 = baz_1();208)";209  const char *PostCode = R"(210#include "bar.h"211#include "baz.h"212 213int BarResult = bar();214int BazResult_0 = baz_0();215int BazResult_1 = baz_1();216)";217 218  std::vector<ClangTidyError> Errors;219  EXPECT_EQ(PostCode, runCheckOnCode<IncludeCleanerCheck>(220                          PreCode, &Errors, "file.cpp", {}, ClangTidyOptions(),221                          {{"bar.h", R"(#pragma once222                              #include "baz.h"223                              int bar();224                           )"},225                           {"baz.h", R"(#pragma once226                              int baz_0();227                              int baz_1();228                           )"}}));229}230 231TEST(IncludeCleanerCheckTest, SystemMissingIncludes) {232  const char *PreCode = R"(233#include <vector>234 235std::string HelloString;236std::vector Vec;237)";238  const char *PostCode = R"(239#include <string>240#include <vector>241 242std::string HelloString;243std::vector Vec;244)";245 246  std::vector<ClangTidyError> Errors;247  EXPECT_EQ(PostCode, runCheckOnCode<IncludeCleanerCheck>(248                          PreCode, &Errors, "file.cpp", {}, ClangTidyOptions(),249                          {{"string", R"(#pragma once250                              namespace std { class string {}; }251                            )"},252                           {"vector", R"(#pragma once253                              #include <string>254                              namespace std { class vector {}; }255                            )"}}));256}257 258TEST(IncludeCleanerCheckTest, PragmaMissingIncludes) {259  const char *PreCode = R"(260#include "bar.h"261 262int BarResult = bar();263int FooBarResult = foobar();264)";265  const char *PostCode = R"(266#include "bar.h"267#include "public.h"268 269int BarResult = bar();270int FooBarResult = foobar();271)";272 273  std::vector<ClangTidyError> Errors;274  EXPECT_EQ(PostCode, runCheckOnCode<IncludeCleanerCheck>(275                          PreCode, &Errors, "file.cpp", {}, ClangTidyOptions(),276                          {{"bar.h", R"(#pragma once277                              #include "private.h"278                              int bar();279                           )"},280                           {"private.h", R"(#pragma once281                                // IWYU pragma: private, include "public.h"282                                int foobar();283                               )"}}));284}285 286TEST(IncludeCleanerCheckTest, DeclFromMacroExpansion) {287  const char *PreCode = R"(288#include "foo.h"289 290DECLARE(myfunc) {291   int a;292}293)";294 295  std::vector<ClangTidyError> Errors;296  EXPECT_EQ(PreCode, runCheckOnCode<IncludeCleanerCheck>(297                         PreCode, &Errors, "file.cpp", {}, ClangTidyOptions(),298                         {{"foo.h",299                           R"(#pragma once300                     #define DECLARE(X) void X()301                  )"}}));302 303  PreCode = R"(304#include "foo.h"305 306DECLARE {307   int a;308}309)";310 311  EXPECT_EQ(PreCode, runCheckOnCode<IncludeCleanerCheck>(312                         PreCode, &Errors, "file.cpp", {}, ClangTidyOptions(),313                         {{"foo.h",314                           R"(#pragma once315                     #define DECLARE void myfunc()316                  )"}}));317}318 319TEST(IncludeCleanerCheckTest, UnusedIncludes) {320  const char *PreCode = R"(321#include "bar.h")";322 323  {324    std::vector<ClangTidyError> Errors;325    runCheckOnCode<IncludeCleanerCheck>(PreCode, &Errors, "file.cpp", {},326                                        ClangTidyOptions(),327                                        {{"bar.h", "#pragma once"}});328    ASSERT_THAT(Errors.size(), testing::Eq(1U));329    EXPECT_EQ(Errors.front().Message.Message,330              "included header bar.h is not used directly");331  }332  {333    std::vector<ClangTidyError> Errors;334    ClangTidyOptions Opts;335    Opts.CheckOptions["test-check-0.UnusedIncludes"] = "false";336    runCheckOnCode<IncludeCleanerCheck>(PreCode, &Errors, "file.cpp", {}, Opts,337                                        {{"bar.h", "#pragma once"}});338    ASSERT_THAT(Errors.size(), testing::Eq(0U));339  }340}341 342TEST(IncludeCleanerCheckTest, MissingIncludes) {343  const char *PreCode = R"(344#include "baz.h" // IWYU pragma: keep345 346int BarResult1 = bar();)";347 348  {349    std::vector<ClangTidyError> Errors;350    runCheckOnCode<IncludeCleanerCheck>(PreCode, &Errors, "file.cpp", {},351                                        ClangTidyOptions(),352                                        {{"baz.h", R"(#pragma once353                                          #include "bar.h"354                                       )"},355                                         {"bar.h", R"(#pragma once356                                          int bar();357                                       )"}});358    ASSERT_THAT(Errors.size(), testing::Eq(1U));359    EXPECT_EQ(Errors.front().Message.Message,360              "no header providing \"bar\" is directly included");361  }362  {363    std::vector<ClangTidyError> Errors;364    ClangTidyOptions Opts;365    Opts.CheckOptions["test-check-0.MissingIncludes"] = "false";366    runCheckOnCode<IncludeCleanerCheck>(PreCode, &Errors, "file.cpp", {}, Opts,367                                        {{"baz.h", R"(#pragma once368                                          #include "bar.h"369                                       )"},370                                         {"bar.h", R"(#pragma once371                                          int bar();372                                       )"}});373    ASSERT_THAT(Errors.size(), testing::Eq(0U));374  }375}376 377} // namespace378} // namespace test379} // namespace tidy380} // namespace clang381