brintos

brintos / llvm-project-archived public Read only

0
0
Text · 18.0 KiB · 410cebf Raw
462 lines · cpp
1#include "ClangTidyOptions.h"2#include "ClangTidyCheck.h"3#include "ClangTidyDiagnosticConsumer.h"4#include "llvm/ADT/StringExtras.h"5#include "llvm/Support/ScopedPrinter.h"6#include "llvm/Testing/Annotations/Annotations.h"7#include "gmock/gmock.h"8#include "gtest/gtest.h"9#include <optional>10 11namespace clang {12namespace tidy {13 14enum class Colours { Red, Orange, Yellow, Green, Blue, Indigo, Violet };15 16template <> struct OptionEnumMapping<Colours> {17  static llvm::ArrayRef<std::pair<Colours, StringRef>> getEnumMapping() {18    static constexpr std::pair<Colours, StringRef> Mapping[] = {19        {Colours::Red, "Red"},       {Colours::Orange, "Orange"},20        {Colours::Yellow, "Yellow"}, {Colours::Green, "Green"},21        {Colours::Blue, "Blue"},     {Colours::Indigo, "Indigo"},22        {Colours::Violet, "Violet"}};23    return ArrayRef(Mapping);24  }25};26 27namespace test {28 29TEST(ParseLineFilter, EmptyFilter) {30  ClangTidyGlobalOptions Options;31  EXPECT_FALSE(parseLineFilter("", Options));32  EXPECT_TRUE(Options.LineFilter.empty());33  EXPECT_FALSE(parseLineFilter("[]", Options));34  EXPECT_TRUE(Options.LineFilter.empty());35}36 37TEST(ParseLineFilter, InvalidFilter) {38  ClangTidyGlobalOptions Options;39  EXPECT_TRUE(!!parseLineFilter("asdf", Options));40  EXPECT_TRUE(Options.LineFilter.empty());41 42  EXPECT_TRUE(!!parseLineFilter("[{}]", Options));43  EXPECT_TRUE(!!parseLineFilter("[{\"name\":\"\"}]", Options));44  EXPECT_TRUE(45      !!parseLineFilter("[{\"name\":\"test\",\"lines\":[[1]]}]", Options));46  EXPECT_TRUE(47      !!parseLineFilter("[{\"name\":\"test\",\"lines\":[[1,2,3]]}]", Options));48  EXPECT_TRUE(49      !!parseLineFilter("[{\"name\":\"test\",\"lines\":[[1,-1]]}]", Options));50}51 52TEST(ParseLineFilter, ValidFilter) {53  ClangTidyGlobalOptions Options;54  std::error_code Error = parseLineFilter(55      "[{\"name\":\"file1.cpp\",\"lines\":[[3,15],[20,30],[42,42]]},"56      "{\"name\":\"file2.h\"},"57      "{\"name\":\"file3.cc\",\"lines\":[[100,1000]]}]",58      Options);59  EXPECT_FALSE(Error);60  EXPECT_EQ(3u, Options.LineFilter.size());61  EXPECT_EQ("file1.cpp", Options.LineFilter[0].Name);62  EXPECT_EQ(3u, Options.LineFilter[0].LineRanges.size());63  EXPECT_EQ(3u, Options.LineFilter[0].LineRanges[0].first);64  EXPECT_EQ(15u, Options.LineFilter[0].LineRanges[0].second);65  EXPECT_EQ(20u, Options.LineFilter[0].LineRanges[1].first);66  EXPECT_EQ(30u, Options.LineFilter[0].LineRanges[1].second);67  EXPECT_EQ(42u, Options.LineFilter[0].LineRanges[2].first);68  EXPECT_EQ(42u, Options.LineFilter[0].LineRanges[2].second);69  EXPECT_EQ("file2.h", Options.LineFilter[1].Name);70  EXPECT_EQ(0u, Options.LineFilter[1].LineRanges.size());71  EXPECT_EQ("file3.cc", Options.LineFilter[2].Name);72  EXPECT_EQ(1u, Options.LineFilter[2].LineRanges.size());73  EXPECT_EQ(100u, Options.LineFilter[2].LineRanges[0].first);74  EXPECT_EQ(1000u, Options.LineFilter[2].LineRanges[0].second);75}76 77TEST(ParseConfiguration, ValidConfiguration) {78  llvm::ErrorOr<ClangTidyOptions> Options =79      parseConfiguration(llvm::MemoryBufferRef(80          "Checks: \"-*,misc-*\"\n"81          "HeaderFileExtensions: [\"\",\"h\",\"hh\",\"hpp\",\"hxx\"]\n"82          "ImplementationFileExtensions: [\"c\",\"cc\",\"cpp\",\"cxx\"]\n"83          "HeaderFilterRegex: \".*\"\n"84          "User: some.user",85          "Options"));86  EXPECT_TRUE(!!Options);87  EXPECT_EQ("-*,misc-*", *Options->Checks);88  EXPECT_EQ(std::vector<std::string>({"", "h", "hh", "hpp", "hxx"}),89            *Options->HeaderFileExtensions);90  EXPECT_EQ(std::vector<std::string>({"c", "cc", "cpp", "cxx"}),91            *Options->ImplementationFileExtensions);92  EXPECT_EQ(".*", *Options->HeaderFilterRegex);93  EXPECT_EQ("some.user", *Options->User);94}95 96TEST(ParseConfiguration, ChecksSeparatedByNewlines) {97  auto MemoryBuffer = llvm::MemoryBufferRef("Checks: |\n"98                                            "  -*,misc-*\n"99                                            "  llvm-*\n"100                                            "  -clang-*,\n"101                                            "  google-*",102                                            "Options");103 104  auto Options = parseConfiguration(MemoryBuffer);105 106  EXPECT_TRUE(!!Options);107  EXPECT_EQ("-*,misc-*\nllvm-*\n-clang-*,\ngoogle-*\n", *Options->Checks);108}109 110TEST(ParseConfiguration, MergeConfigurations) {111  llvm::ErrorOr<ClangTidyOptions> Options1 =112      parseConfiguration(llvm::MemoryBufferRef(R"(113      Checks: "check1,check2"114      HeaderFileExtensions: ["h","hh"]115      ImplementationFileExtensions: ["c","cc"]116      HeaderFilterRegex: "filter1"117      User: user1118      ExtraArgs: ['arg1', 'arg2']119      ExtraArgsBefore: ['arg-before1', 'arg-before2']120      UseColor: false121      SystemHeaders: false122  )",123                                               "Options1"));124  ASSERT_TRUE(!!Options1);125  llvm::ErrorOr<ClangTidyOptions> Options2 =126      parseConfiguration(llvm::MemoryBufferRef(R"(127      Checks: "check3,check4"128      HeaderFileExtensions: ["hpp","hxx"]129      ImplementationFileExtensions: ["cpp","cxx"]130      HeaderFilterRegex: "filter2"131      User: user2132      ExtraArgs: ['arg3', 'arg4']133      ExtraArgsBefore: ['arg-before3', 'arg-before4']134      UseColor: true135      SystemHeaders: true136  )",137                                               "Options2"));138  ASSERT_TRUE(!!Options2);139  ClangTidyOptions Options = Options1->merge(*Options2, 0);140  EXPECT_EQ("check1,check2,check3,check4", *Options.Checks);141  EXPECT_EQ(std::vector<std::string>({"hpp", "hxx"}),142            *Options.HeaderFileExtensions);143  EXPECT_EQ(std::vector<std::string>({"cpp", "cxx"}),144            *Options.ImplementationFileExtensions);145  EXPECT_EQ("filter2", *Options.HeaderFilterRegex);146  EXPECT_EQ("user2", *Options.User);147  ASSERT_TRUE(Options.ExtraArgs.has_value());148  EXPECT_EQ("arg1,arg2,arg3,arg4", llvm::join(Options.ExtraArgs->begin(),149                                              Options.ExtraArgs->end(), ","));150  ASSERT_TRUE(Options.ExtraArgsBefore.has_value());151  EXPECT_EQ("arg-before1,arg-before2,arg-before3,arg-before4",152            llvm::join(Options.ExtraArgsBefore->begin(),153                       Options.ExtraArgsBefore->end(), ","));154  ASSERT_TRUE(Options.UseColor.has_value());155  EXPECT_TRUE(*Options.UseColor);156 157  ASSERT_TRUE(Options.SystemHeaders.has_value());158  EXPECT_TRUE(*Options.SystemHeaders);159}160 161namespace {162class DiagCollecter {163public:164  struct Diag {165  private:166    static size_t posToOffset(const llvm::SMLoc Loc,167                              const llvm::SourceMgr *Src) {168      return Loc.getPointer() -169             Src->getMemoryBuffer(Src->FindBufferContainingLoc(Loc))170                 ->getBufferStart();171    }172 173  public:174    Diag(const llvm::SMDiagnostic &D)175        : Message(D.getMessage()), Kind(D.getKind()),176          Pos(posToOffset(D.getLoc(), D.getSourceMgr())) {177      if (!D.getRanges().empty()) {178        // Ranges are stored as column numbers on the line that has the error.179        unsigned Offset = Pos - D.getColumnNo();180        Range.emplace();181        Range->Begin = Offset + D.getRanges().front().first,182        Range->End = Offset + D.getRanges().front().second;183      }184    }185    std::string Message;186    llvm::SourceMgr::DiagKind Kind;187    size_t Pos;188    std::optional<llvm::Annotations::Range> Range;189 190    friend void PrintTo(const Diag &D, std::ostream *OS) {191      *OS << (D.Kind == llvm::SourceMgr::DK_Error ? "error: " : "warning: ")192          << D.Message << "@" << llvm::to_string(D.Pos);193      if (D.Range)194        *OS << ":[" << D.Range->Begin << ", " << D.Range->End << ")";195    }196  };197 198  DiagCollecter() = default;199  DiagCollecter(const DiagCollecter &) = delete;200 201  std::function<void(const llvm::SMDiagnostic &)>202  getCallback(bool Clear = true) & {203    if (Clear)204      Diags.clear();205    return [&](const llvm::SMDiagnostic &Diag) { Diags.emplace_back(Diag); };206  }207 208  std::function<void(const llvm::SMDiagnostic &)>209  getCallback(bool Clear = true) && = delete;210 211  llvm::ArrayRef<Diag> getDiags() const { return Diags; }212 213private:214  std::vector<Diag> Diags;215};216 217MATCHER_P(DiagMessage, M, "") { return arg.Message == M; }218MATCHER_P(DiagKind, K, "") { return arg.Kind == K; }219MATCHER_P(DiagPos, P, "") { return arg.Pos == P; }220MATCHER_P(DiagRange, P, "") { return arg.Range && *arg.Range == P; }221} // namespace222 223using ::testing::AllOf;224using ::testing::ElementsAre;225using ::testing::UnorderedElementsAre;226 227TEST(ParseConfiguration, CollectDiags) {228  DiagCollecter Collector;229  auto ParseWithDiags = [&](llvm::StringRef Buffer) {230    return parseConfigurationWithDiags(llvm::MemoryBufferRef(Buffer, "Options"),231                                       Collector.getCallback());232  };233  llvm::Annotations Options(R"(234    [[Check]]: llvm-include-order235  )");236  llvm::ErrorOr<ClangTidyOptions> ParsedOpt = ParseWithDiags(Options.code());237  EXPECT_TRUE(!ParsedOpt);238  EXPECT_THAT(Collector.getDiags(),239              testing::ElementsAre(AllOf(DiagMessage("unknown key 'Check'"),240                                         DiagKind(llvm::SourceMgr::DK_Error),241                                         DiagPos(Options.range().Begin),242                                         DiagRange(Options.range()))));243 244  Options = llvm::Annotations(R"(245    UseColor: [[NotABool]]246  )");247  ParsedOpt = ParseWithDiags(Options.code());248  EXPECT_TRUE(!ParsedOpt);249  EXPECT_THAT(Collector.getDiags(),250              testing::ElementsAre(AllOf(DiagMessage("invalid boolean"),251                                         DiagKind(llvm::SourceMgr::DK_Error),252                                         DiagPos(Options.range().Begin),253                                         DiagRange(Options.range()))));254 255  Options = llvm::Annotations(R"(256    SystemHeaders: [[NotABool]]257  )");258  ParsedOpt = ParseWithDiags(Options.code());259  EXPECT_TRUE(!ParsedOpt);260  EXPECT_THAT(Collector.getDiags(),261              testing::ElementsAre(AllOf(DiagMessage("invalid boolean"),262                                         DiagKind(llvm::SourceMgr::DK_Error),263                                         DiagPos(Options.range().Begin),264                                         DiagRange(Options.range()))));265}266 267namespace {268class TestCheck : public ClangTidyCheck {269public:270  TestCheck(ClangTidyContext *Context) : ClangTidyCheck("test", Context) {}271 272  template <typename... Args> auto getLocal(Args &&... Arguments) {273    return Options.get(std::forward<Args>(Arguments)...);274  }275 276  template <typename... Args> auto getGlobal(Args &&... Arguments) {277    return Options.getLocalOrGlobal(std::forward<Args>(Arguments)...);278  }279 280  template <typename IntType = int, typename... Args>281  auto getIntLocal(Args &&... Arguments) {282    return Options.get<IntType>(std::forward<Args>(Arguments)...);283  }284 285  template <typename IntType = int, typename... Args>286  auto getIntGlobal(Args &&... Arguments) {287    return Options.getLocalOrGlobal<IntType>(std::forward<Args>(Arguments)...);288  }289};290 291#define CHECK_VAL(Value, Expected)                                             \292  do {                                                                         \293    auto Item = Value;                                                         \294    ASSERT_TRUE(!!Item);                                                       \295    EXPECT_EQ(*Item, Expected);                                                \296  } while (false)297 298MATCHER_P(ToolDiagMessage, M, "") { return arg.Message.Message == M; }299MATCHER_P(ToolDiagLevel, L, "") { return arg.DiagLevel == L; }300 301} // namespace302 303} // namespace test304 305static constexpr auto Warning = tooling::Diagnostic::Warning;306static constexpr auto Error = tooling::Diagnostic::Error;307 308static void PrintTo(const ClangTidyError &Err, ::std::ostream *OS) {309  *OS << (Err.DiagLevel == Error ? "error: " : "warning: ")310      << Err.Message.Message;311}312 313namespace test {314 315TEST(CheckOptionsValidation, MissingOptions) {316  ClangTidyOptions Options;317  ClangTidyContext Context(std::make_unique<DefaultOptionsProvider>(318      ClangTidyGlobalOptions(), Options));319  ClangTidyDiagnosticConsumer DiagConsumer(Context);320  auto DiagOpts = std::make_unique<DiagnosticOptions>();321  DiagnosticsEngine DE(DiagnosticIDs::create(), *DiagOpts, &DiagConsumer,322                       false);323  Context.setDiagnosticsEngine(std::move(DiagOpts), &DE);324  TestCheck TestCheck(&Context);325  EXPECT_FALSE(TestCheck.getLocal("Opt"));326  EXPECT_EQ(TestCheck.getLocal("Opt", "Unknown"), "Unknown");327  // Missing options aren't errors.328  EXPECT_TRUE(DiagConsumer.take().empty());329}330 331TEST(CheckOptionsValidation, ValidIntOptions) {332  ClangTidyOptions Options;333  auto &CheckOptions = Options.CheckOptions;334  CheckOptions["test.IntExpected"] = "1";335  CheckOptions["test.IntInvalid1"] = "1WithMore";336  CheckOptions["test.IntInvalid2"] = "NoInt";337  CheckOptions["GlobalIntExpected"] = "1";338  CheckOptions["GlobalIntInvalid"] = "NoInt";339  CheckOptions["test.DefaultedIntInvalid"] = "NoInt";340  CheckOptions["test.BoolITrueValue"] = "1";341  CheckOptions["test.BoolIFalseValue"] = "0";342  CheckOptions["test.BoolTrueValue"] = "true";343  CheckOptions["test.BoolFalseValue"] = "false";344  CheckOptions["test.BoolTrueShort"] = "Y";345  CheckOptions["test.BoolFalseShort"] = "N";346  CheckOptions["test.BoolUnparseable"] = "Nothing";347 348  ClangTidyContext Context(std::make_unique<DefaultOptionsProvider>(349      ClangTidyGlobalOptions(), Options));350  ClangTidyDiagnosticConsumer DiagConsumer(Context);351  auto DiagOpts = std::make_unique<DiagnosticOptions>();352  DiagnosticsEngine DE(DiagnosticIDs::create(), *DiagOpts, &DiagConsumer,353                       false);354  Context.setDiagnosticsEngine(std::move(DiagOpts), &DE);355  TestCheck TestCheck(&Context);356 357  CHECK_VAL(TestCheck.getIntLocal("IntExpected"), 1);358  CHECK_VAL(TestCheck.getIntGlobal("GlobalIntExpected"), 1);359  EXPECT_FALSE(TestCheck.getIntLocal("IntInvalid1").has_value());360  EXPECT_FALSE(TestCheck.getIntLocal("IntInvalid2").has_value());361  EXPECT_FALSE(TestCheck.getIntGlobal("GlobalIntInvalid").has_value());362  ASSERT_EQ(TestCheck.getIntLocal("DefaultedIntInvalid", 1), 1);363 364  CHECK_VAL(TestCheck.getIntLocal<bool>("BoolITrueValue"), true);365  CHECK_VAL(TestCheck.getIntLocal<bool>("BoolIFalseValue"), false);366  CHECK_VAL(TestCheck.getIntLocal<bool>("BoolTrueValue"), true);367  CHECK_VAL(TestCheck.getIntLocal<bool>("BoolFalseValue"), false);368  CHECK_VAL(TestCheck.getIntLocal<bool>("BoolTrueShort"), true);369  CHECK_VAL(TestCheck.getIntLocal<bool>("BoolFalseShort"), false);370  EXPECT_FALSE(TestCheck.getIntLocal<bool>("BoolUnparseable"));371 372  EXPECT_THAT(373      DiagConsumer.take(),374      UnorderedElementsAre(375          AllOf(ToolDiagMessage(376                    "invalid configuration value '1WithMore' for option "377                    "'test.IntInvalid1'; expected an integer"),378                ToolDiagLevel(Warning)),379          AllOf(380              ToolDiagMessage("invalid configuration value 'NoInt' for option "381                              "'test.IntInvalid2'; expected an integer"),382              ToolDiagLevel(Warning)),383          AllOf(384              ToolDiagMessage("invalid configuration value 'NoInt' for option "385                              "'GlobalIntInvalid'; expected an integer"),386              ToolDiagLevel(Warning)),387          AllOf(ToolDiagMessage(388                    "invalid configuration value 'NoInt' for option "389                    "'test.DefaultedIntInvalid'; expected an integer"),390                ToolDiagLevel(Warning)),391          AllOf(ToolDiagMessage(392                    "invalid configuration value 'Nothing' for option "393                    "'test.BoolUnparseable'; expected a bool"),394                ToolDiagLevel(Warning))));395}396 397TEST(ValidConfiguration, ValidEnumOptions) {398 399  ClangTidyOptions Options;400  auto &CheckOptions = Options.CheckOptions;401 402  CheckOptions["test.Valid"] = "Red";403  CheckOptions["test.Invalid"] = "Scarlet";404  CheckOptions["test.ValidWrongCase"] = "rED";405  CheckOptions["test.NearMiss"] = "Oragne";406  CheckOptions["GlobalValid"] = "Violet";407  CheckOptions["GlobalInvalid"] = "Purple";408  CheckOptions["GlobalValidWrongCase"] = "vIOLET";409  CheckOptions["GlobalNearMiss"] = "Yelow";410 411  ClangTidyContext Context(std::make_unique<DefaultOptionsProvider>(412      ClangTidyGlobalOptions(), Options));413  ClangTidyDiagnosticConsumer DiagConsumer(Context);414  auto DiagOpts = std::make_unique<DiagnosticOptions>();415  DiagnosticsEngine DE(DiagnosticIDs::create(), *DiagOpts, &DiagConsumer,416                       false);417  Context.setDiagnosticsEngine(std::move(DiagOpts), &DE);418  TestCheck TestCheck(&Context);419 420  CHECK_VAL(TestCheck.getIntLocal<Colours>("Valid"), Colours::Red);421  CHECK_VAL(TestCheck.getIntGlobal<Colours>("GlobalValid"), Colours::Violet);422 423  EXPECT_FALSE(TestCheck.getIntLocal<Colours>("ValidWrongCase").has_value());424  EXPECT_FALSE(TestCheck.getIntLocal<Colours>("NearMiss").has_value());425  EXPECT_FALSE(TestCheck.getIntGlobal<Colours>("GlobalInvalid").has_value());426  EXPECT_FALSE(427      TestCheck.getIntGlobal<Colours>("GlobalValidWrongCase").has_value());428  EXPECT_FALSE(TestCheck.getIntGlobal<Colours>("GlobalNearMiss").has_value());429 430  EXPECT_FALSE(TestCheck.getIntLocal<Colours>("Invalid").has_value());431  EXPECT_THAT(432      DiagConsumer.take(),433      UnorderedElementsAre(434          AllOf(ToolDiagMessage("invalid configuration value "435                                "'Scarlet' for option 'test.Invalid'"),436                ToolDiagLevel(Warning)),437          AllOf(ToolDiagMessage("invalid configuration value 'rED' for option "438                                "'test.ValidWrongCase'; did you mean 'Red'?"),439                ToolDiagLevel(Warning)),440          AllOf(441              ToolDiagMessage("invalid configuration value 'Oragne' for option "442                              "'test.NearMiss'; did you mean 'Orange'?"),443              ToolDiagLevel(Warning)),444          AllOf(ToolDiagMessage("invalid configuration value "445                                "'Purple' for option 'GlobalInvalid'"),446                ToolDiagLevel(Warning)),447          AllOf(448              ToolDiagMessage("invalid configuration value 'vIOLET' for option "449                              "'GlobalValidWrongCase'; did you mean 'Violet'?"),450              ToolDiagLevel(Warning)),451          AllOf(452              ToolDiagMessage("invalid configuration value 'Yelow' for option "453                              "'GlobalNearMiss'; did you mean 'Yellow'?"),454              ToolDiagLevel(Warning))));455}456 457#undef CHECK_VAL458 459} // namespace test460} // namespace tidy461} // namespace clang462