brintos

brintos / llvm-project-archived public Read only

0
0
Text · 11.5 KiB · 2524ac3 Raw
307 lines · cpp
1//=== unittests/Sema/ExternalSemaSourceTest.cpp - ExternalSemaSource tests ===//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/AST/ASTConsumer.h"10#include "clang/AST/ASTContext.h"11#include "clang/Frontend/CompilerInstance.h"12#include "clang/Lex/Preprocessor.h"13#include "clang/Parse/ParseAST.h"14#include "clang/Sema/ExternalSemaSource.h"15#include "clang/Sema/Sema.h"16#include "clang/Sema/SemaDiagnostic.h"17#include "clang/Sema/TypoCorrection.h"18#include "clang/Tooling/Tooling.h"19#include "gtest/gtest.h"20 21using namespace clang;22using namespace clang::tooling;23 24namespace {25 26// \brief Counts the number of times MaybeDiagnoseMissingCompleteType27// is called. Returns the result it was provided on creation.28class CompleteTypeDiagnoser : public clang::ExternalSemaSource {29public:30  CompleteTypeDiagnoser(bool MockResult) : CallCount(0), Result(MockResult) {}31 32  bool MaybeDiagnoseMissingCompleteType(SourceLocation L, QualType T) override {33    ++CallCount;34    return Result;35  }36 37  int CallCount;38  bool Result;39};40 41/// Counts the number of typo-correcting diagnostics correcting from one name to42/// another while still passing all diagnostics along a chain of consumers.43class DiagnosticWatcher : public clang::DiagnosticConsumer {44  DiagnosticConsumer *Chained;45  std::string FromName;46  std::string ToName;47 48public:49  DiagnosticWatcher(StringRef From, StringRef To)50      : Chained(nullptr), FromName(From), ToName("'"), SeenCount(0) {51    ToName.append(std::string(To));52    ToName.append("'");53  }54 55  void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,56                        const Diagnostic &Info) override {57    if (Chained)58      Chained->HandleDiagnostic(DiagLevel, Info);59    if (Info.getID() - 1 == diag::err_using_directive_member_suggest) {60      const IdentifierInfo *Ident = Info.getArgIdentifier(0);61      const std::string &CorrectedQuotedStr = Info.getArgStdStr(1);62      if (Ident->getName() == FromName && CorrectedQuotedStr == ToName)63        ++SeenCount;64    } else if (Info.getID() == diag::err_no_member_suggest) {65      auto Ident = DeclarationName::getFromOpaqueInteger(Info.getRawArg(0));66      const std::string &CorrectedQuotedStr = Info.getArgStdStr(3);67      if (Ident.getAsString() == FromName && CorrectedQuotedStr == ToName)68        ++SeenCount;69    }70  }71 72  void clear() override {73    DiagnosticConsumer::clear();74    if (Chained)75      Chained->clear();76  }77 78  bool IncludeInDiagnosticCounts() const override {79    if (Chained)80      return Chained->IncludeInDiagnosticCounts();81    return false;82  }83 84  DiagnosticWatcher *Chain(DiagnosticConsumer *ToChain) {85    Chained = ToChain;86    return this;87  }88 89  int SeenCount;90};91 92// \brief Always corrects a typo matching CorrectFrom with a new namespace93// with the name CorrectTo.94class NamespaceTypoProvider : public clang::ExternalSemaSource {95  std::string CorrectFrom;96  std::string CorrectTo;97  Sema *CurrentSema;98 99public:100  NamespaceTypoProvider(StringRef From, StringRef To)101      : CorrectFrom(From), CorrectTo(To), CurrentSema(nullptr), CallCount(0) {}102 103  void InitializeSema(Sema &S) override { CurrentSema = &S; }104 105  void ForgetSema() override { CurrentSema = nullptr; }106 107  TypoCorrection CorrectTypo(const DeclarationNameInfo &Typo, int LookupKind,108                             Scope *S, CXXScopeSpec *SS,109                             CorrectionCandidateCallback &CCC,110                             DeclContext *MemberContext, bool EnteringContext,111                             const ObjCObjectPointerType *OPT) override {112    ++CallCount;113    if (CurrentSema && Typo.getName().getAsString() == CorrectFrom) {114      DeclContext *DestContext = nullptr;115      ASTContext &Context = CurrentSema->getASTContext();116      if (SS)117        DestContext = CurrentSema->computeDeclContext(*SS, EnteringContext);118      if (!DestContext)119        DestContext = Context.getTranslationUnitDecl();120      IdentifierInfo *ToIdent =121          CurrentSema->getPreprocessor().getIdentifierInfo(CorrectTo);122      NamespaceDecl *NewNamespace =123          NamespaceDecl::Create(Context, DestContext, false, Typo.getBeginLoc(),124                                Typo.getLoc(), ToIdent, nullptr, false);125      DestContext->addDecl(NewNamespace);126      TypoCorrection Correction(ToIdent);127      Correction.addCorrectionDecl(NewNamespace);128      return Correction;129    }130    return TypoCorrection();131  }132 133  int CallCount;134};135 136class FunctionTypoProvider : public clang::ExternalSemaSource {137  std::string CorrectFrom;138  std::string CorrectTo;139  Sema *CurrentSema;140 141public:142  FunctionTypoProvider(StringRef From, StringRef To)143      : CorrectFrom(From), CorrectTo(To), CurrentSema(nullptr), CallCount(0) {}144 145  void InitializeSema(Sema &S) override { CurrentSema = &S; }146 147  void ForgetSema() override { CurrentSema = nullptr; }148 149  TypoCorrection CorrectTypo(const DeclarationNameInfo &Typo, int LookupKind,150                             Scope *S, CXXScopeSpec *SS,151                             CorrectionCandidateCallback &CCC,152                             DeclContext *MemberContext, bool EnteringContext,153                             const ObjCObjectPointerType *OPT) override {154    ++CallCount;155    if (CurrentSema && Typo.getName().getAsString() == CorrectFrom) {156      DeclContext *DestContext = nullptr;157      ASTContext &Context = CurrentSema->getASTContext();158      if (SS)159        DestContext = CurrentSema->computeDeclContext(*SS, EnteringContext);160      if (!DestContext)161        DestContext = Context.getTranslationUnitDecl();162      IdentifierInfo *ToIdent =163          CurrentSema->getPreprocessor().getIdentifierInfo(CorrectTo);164      auto *NewFunction = FunctionDecl::Create(165          Context, DestContext, SourceLocation(), SourceLocation(), ToIdent,166          Context.getFunctionType(Context.VoidTy, {}, {}), nullptr, SC_Static,167          /*UsesFPIntrin*/ false);168      DestContext->addDecl(NewFunction);169      TypoCorrection Correction(ToIdent);170      Correction.addCorrectionDecl(NewFunction);171      return Correction;172    }173    return TypoCorrection();174  }175 176  int CallCount;177};178 179// \brief Chains together a vector of DiagnosticWatchers and180// adds a vector of ExternalSemaSources to the CompilerInstance before181// performing semantic analysis.182class ExternalSemaSourceInstaller : public clang::ASTFrontendAction {183  std::vector<DiagnosticWatcher *> Watchers;184  std::vector<llvm::IntrusiveRefCntPtr<clang::ExternalSemaSource>> Sources;185  std::unique_ptr<DiagnosticConsumer> OwnedClient;186 187protected:188  std::unique_ptr<clang::ASTConsumer>189  CreateASTConsumer(clang::CompilerInstance &Compiler,190                    llvm::StringRef /* dummy */) override {191    return std::make_unique<clang::ASTConsumer>();192  }193 194  void ExecuteAction() override {195    CompilerInstance &CI = getCompilerInstance();196    ASSERT_FALSE(CI.hasSema());197    CI.createSema(getTranslationUnitKind(), nullptr);198    ASSERT_TRUE(CI.hasDiagnostics());199    DiagnosticsEngine &Diagnostics = CI.getDiagnostics();200    DiagnosticConsumer *Client = Diagnostics.getClient();201    if (Diagnostics.ownsClient())202      OwnedClient = Diagnostics.takeClient();203    for (size_t I = 0, E = Watchers.size(); I < E; ++I)204      Client = Watchers[I]->Chain(Client);205    Diagnostics.setClient(Client, false);206    for (size_t I = 0, E = Sources.size(); I < E; ++I) {207      Sources[I]->InitializeSema(CI.getSema());208      CI.getSema().addExternalSource(Sources[I]);209    }210    ParseAST(CI.getSema(), CI.getFrontendOpts().ShowStats,211             CI.getFrontendOpts().SkipFunctionBodies);212  }213 214public:215  void PushSource(llvm::IntrusiveRefCntPtr<clang::ExternalSemaSource> Source) {216    Sources.push_back(std::move(Source));217  }218 219  void PushWatcher(DiagnosticWatcher *Watcher) { Watchers.push_back(Watcher); }220};221 222using llvm::makeIntrusiveRefCnt;223 224// Make sure that the DiagnosticWatcher is not miscounting.225TEST(ExternalSemaSource, DiagCheck) {226  auto Installer = std::make_unique<ExternalSemaSourceInstaller>();227  DiagnosticWatcher Watcher("AAB", "BBB");228  Installer->PushWatcher(&Watcher);229  std::vector<std::string> Args(1, "-std=c++11");230  ASSERT_TRUE(clang::tooling::runToolOnCodeWithArgs(231      std::move(Installer), "namespace AAA { } using namespace AAB;", Args));232  ASSERT_EQ(0, Watcher.SeenCount);233}234 235// Check that when we add a NamespaceTypeProvider, we use that suggestion236// instead of the usual suggestion we would use above.237TEST(ExternalSemaSource, ExternalTypoCorrectionPrioritized) {238  auto Installer = std::make_unique<ExternalSemaSourceInstaller>();239  auto Provider = makeIntrusiveRefCnt<NamespaceTypoProvider>("AAB", "BBB");240  DiagnosticWatcher Watcher("AAB", "BBB");241  Installer->PushSource(Provider);242  Installer->PushWatcher(&Watcher);243  std::vector<std::string> Args(1, "-std=c++11");244  ASSERT_TRUE(clang::tooling::runToolOnCodeWithArgs(245      std::move(Installer), "namespace AAA { } using namespace AAB;", Args));246  ASSERT_LE(0, Provider->CallCount);247  ASSERT_EQ(1, Watcher.SeenCount);248}249 250// Check that we use the first successful TypoCorrection returned from an251// ExternalSemaSource.252TEST(ExternalSemaSource, ExternalTypoCorrectionOrdering) {253  auto Installer = std::make_unique<ExternalSemaSourceInstaller>();254  auto First = makeIntrusiveRefCnt<NamespaceTypoProvider>("XXX", "BBB");255  auto Second = makeIntrusiveRefCnt<NamespaceTypoProvider>("AAB", "CCC");256  auto Third = makeIntrusiveRefCnt<NamespaceTypoProvider>("AAB", "DDD");257  DiagnosticWatcher Watcher("AAB", "CCC");258  Installer->PushSource(First);259  Installer->PushSource(Second);260  Installer->PushSource(Third);261  Installer->PushWatcher(&Watcher);262  std::vector<std::string> Args(1, "-std=c++11");263  ASSERT_TRUE(clang::tooling::runToolOnCodeWithArgs(264      std::move(Installer), "namespace AAA { } using namespace AAB;", Args));265  ASSERT_LE(1, First->CallCount);266  ASSERT_LE(1, Second->CallCount);267  ASSERT_EQ(0, Third->CallCount);268  ASSERT_EQ(1, Watcher.SeenCount);269}270 271// We should only try MaybeDiagnoseMissingCompleteType if we can't otherwise272// solve the problem.273TEST(ExternalSemaSource, TryOtherTacticsBeforeDiagnosing) {274  auto Installer = std::make_unique<ExternalSemaSourceInstaller>();275  auto Diagnoser = makeIntrusiveRefCnt<CompleteTypeDiagnoser>(false);276  Installer->PushSource(Diagnoser);277  std::vector<std::string> Args(1, "-std=c++11");278  // This code hits the class template specialization/class member of a class279  // template specialization checks in Sema::RequireCompleteTypeImpl.280  ASSERT_TRUE(clang::tooling::runToolOnCodeWithArgs(281      std::move(Installer),282      "template <typename T> struct S { class C { }; }; S<char>::C SCInst;",283      Args));284  ASSERT_EQ(0, Diagnoser->CallCount);285}286 287// The first ExternalSemaSource where MaybeDiagnoseMissingCompleteType returns288// true should be the last one called.289TEST(ExternalSemaSource, FirstDiagnoserTaken) {290  auto Installer = std::make_unique<ExternalSemaSourceInstaller>();291  auto First = makeIntrusiveRefCnt<CompleteTypeDiagnoser>(false);292  auto Second = makeIntrusiveRefCnt<CompleteTypeDiagnoser>(true);293  auto Third = makeIntrusiveRefCnt<CompleteTypeDiagnoser>(true);294  Installer->PushSource(First);295  Installer->PushSource(Second);296  Installer->PushSource(Third);297  std::vector<std::string> Args(1, "-std=c++11");298  ASSERT_FALSE(clang::tooling::runToolOnCodeWithArgs(299      std::move(Installer), "class Incomplete; Incomplete IncompleteInstance;",300      Args));301  ASSERT_EQ(1, First->CallCount);302  ASSERT_EQ(1, Second->CallCount);303  ASSERT_EQ(0, Third->CallCount);304}305 306} // anonymous namespace307