brintos

brintos / llvm-project-archived public Read only

0
0
Text · 15.3 KiB · 5492146 Raw
398 lines · cpp
1//===- unittests/Basic/DiagnosticTest.cpp -- Diagnostic engine 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/Basic/Diagnostic.h"10#include "clang/Basic/DiagnosticError.h"11#include "clang/Basic/DiagnosticIDs.h"12#include "clang/Basic/DiagnosticLex.h"13#include "clang/Basic/DiagnosticSema.h"14#include "clang/Basic/FileManager.h"15#include "clang/Basic/SourceLocation.h"16#include "clang/Basic/SourceManager.h"17#include "llvm/ADT/ArrayRef.h"18#include "llvm/ADT/IntrusiveRefCntPtr.h"19#include "llvm/ADT/StringRef.h"20#include "llvm/Support/MemoryBuffer.h"21#include "llvm/Support/VirtualFileSystem.h"22#include "gmock/gmock.h"23#include "gtest/gtest.h"24#include <memory>25#include <optional>26#include <vector>27 28using namespace llvm;29using namespace clang;30 31// Declare DiagnosticsTestHelper to avoid GCC warning32namespace clang {33void DiagnosticsTestHelper(DiagnosticsEngine &diag);34}35 36void clang::DiagnosticsTestHelper(DiagnosticsEngine &diag) {37  EXPECT_FALSE(diag.DiagStates.empty());38  EXPECT_TRUE(diag.DiagStatesByLoc.empty());39  EXPECT_TRUE(diag.DiagStateOnPushStack.empty());40}41 42namespace {43using testing::AllOf;44using testing::ElementsAre;45using testing::IsEmpty;46 47// Check that DiagnosticErrorTrap works with SuppressAllDiagnostics.48TEST(DiagnosticTest, suppressAndTrap) {49  DiagnosticOptions DiagOpts;50  DiagnosticsEngine Diags(DiagnosticIDs::create(), DiagOpts,51                          new IgnoringDiagConsumer());52  Diags.setSuppressAllDiagnostics(true);53 54  {55    DiagnosticErrorTrap trap(Diags);56 57    // Diag that would set UncompilableErrorOccurred and ErrorOccurred.58    Diags.Report(diag::err_target_unknown_triple) << "unknown";59 60    // Diag that would set UnrecoverableErrorOccurred and ErrorOccurred.61    Diags.Report(diag::err_cannot_open_file) << "file" << "error";62 63    // Diag that would set FatalErrorOccurred64    // (via non-note following a fatal error).65    Diags.Report(diag::warn_apinotes_message) << "warning";66 67    EXPECT_TRUE(trap.hasErrorOccurred());68    EXPECT_TRUE(trap.hasUnrecoverableErrorOccurred());69  }70 71  EXPECT_FALSE(Diags.hasErrorOccurred());72  EXPECT_FALSE(Diags.hasFatalErrorOccurred());73  EXPECT_FALSE(Diags.hasUncompilableErrorOccurred());74  EXPECT_FALSE(Diags.hasUnrecoverableErrorOccurred());75}76 77// Check that FatalsAsError works as intended78TEST(DiagnosticTest, fatalsAsError) {79  for (unsigned FatalsAsError = 0; FatalsAsError != 2; ++FatalsAsError) {80    DiagnosticOptions DiagOpts;81    DiagnosticsEngine Diags(DiagnosticIDs::create(), DiagOpts,82                            new IgnoringDiagConsumer());83    Diags.setFatalsAsError(FatalsAsError);84 85    // Diag that would set UnrecoverableErrorOccurred and ErrorOccurred.86    Diags.Report(diag::err_cannot_open_file) << "file" << "error";87 88    // Diag that would set FatalErrorOccurred89    // (via non-note following a fatal error).90    Diags.Report(diag::warn_apinotes_message) << "warning";91 92    EXPECT_TRUE(Diags.hasErrorOccurred());93    EXPECT_EQ(Diags.hasFatalErrorOccurred(), FatalsAsError ? 0u : 1u);94    EXPECT_TRUE(Diags.hasUncompilableErrorOccurred());95    EXPECT_TRUE(Diags.hasUnrecoverableErrorOccurred());96 97    // The warning should be emitted and counted only if we're not suppressing98    // after fatal errors.99    EXPECT_EQ(Diags.getNumWarnings(), FatalsAsError);100  }101}102 103TEST(DiagnosticTest, tooManyErrorsIsAlwaysFatal) {104  DiagnosticOptions DiagOpts;105  DiagnosticsEngine Diags(DiagnosticIDs::create(), DiagOpts,106                          new IgnoringDiagConsumer());107  Diags.setFatalsAsError(true);108 109  // Report a fatal_too_many_errors diagnostic to ensure that still110  // acts as a fatal error despite downgrading fatal errors to errors.111  Diags.Report(diag::fatal_too_many_errors);112  EXPECT_TRUE(Diags.hasFatalErrorOccurred());113 114  // Ensure that the severity of that diagnostic is really "fatal".115  EXPECT_EQ(Diags.getDiagnosticLevel(diag::fatal_too_many_errors, {}),116            DiagnosticsEngine::Level::Fatal);117}118 119// Check that soft RESET works as intended120TEST(DiagnosticTest, softReset) {121  DiagnosticOptions DiagOpts;122  DiagnosticsEngine Diags(DiagnosticIDs::create(), DiagOpts,123                          new IgnoringDiagConsumer());124 125  unsigned numWarnings = 0U, numErrors = 0U;126 127  Diags.Reset(true);128  // Check For ErrorOccurred and TrapNumErrorsOccurred129  EXPECT_FALSE(Diags.hasErrorOccurred());130  EXPECT_FALSE(Diags.hasFatalErrorOccurred());131  EXPECT_FALSE(Diags.hasUncompilableErrorOccurred());132  // Check for UnrecoverableErrorOccurred and TrapNumUnrecoverableErrorsOccurred133  EXPECT_FALSE(Diags.hasUnrecoverableErrorOccurred());134 135  EXPECT_EQ(Diags.getNumWarnings(), numWarnings);136  EXPECT_EQ(Diags.getNumErrors(), numErrors);137 138  // Check for private variables of DiagnosticsEngine differentiating soft reset139  DiagnosticsTestHelper(Diags);140 141  EXPECT_TRUE(Diags.isLastDiagnosticIgnored());142}143 144TEST(DiagnosticTest, diagnosticError) {145  DiagnosticOptions DiagOpts;146  DiagnosticsEngine Diags(DiagnosticIDs::create(), DiagOpts,147                          new IgnoringDiagConsumer());148  PartialDiagnostic::DiagStorageAllocator Alloc;149  llvm::Expected<std::pair<int, int>> Value = DiagnosticError::create(150      SourceLocation(), PartialDiagnostic(diag::err_cannot_open_file, Alloc)151                            << "file"152                            << "error");153  ASSERT_TRUE(!Value);154  llvm::Error Err = Value.takeError();155  std::optional<PartialDiagnosticAt> ErrDiag = DiagnosticError::take(Err);156  llvm::cantFail(std::move(Err));157  ASSERT_FALSE(!ErrDiag);158  EXPECT_EQ(ErrDiag->first, SourceLocation());159  EXPECT_EQ(ErrDiag->second.getDiagID(), diag::err_cannot_open_file);160 161  Value = std::make_pair(20, 1);162  ASSERT_FALSE(!Value);163  EXPECT_EQ(*Value, std::make_pair(20, 1));164  EXPECT_EQ(Value->first, 20);165}166 167TEST(DiagnosticTest, storedDiagEmptyWarning) {168  DiagnosticOptions DiagOpts;169  DiagnosticsEngine Diags(DiagnosticIDs::create(), DiagOpts);170 171  class CaptureDiagnosticConsumer : public DiagnosticConsumer {172  public:173    SmallVector<StoredDiagnostic> StoredDiags;174 175    void HandleDiagnostic(DiagnosticsEngine::Level level,176                          const Diagnostic &Info) override {177      StoredDiags.push_back(StoredDiagnostic(level, Info));178    }179  };180 181  CaptureDiagnosticConsumer CaptureConsumer;182  Diags.setClient(&CaptureConsumer, /*ShouldOwnClient=*/false);183  Diags.Report(diag::pp_hash_warning) << "";184  ASSERT_TRUE(CaptureConsumer.StoredDiags.size() == 1);185 186  // Make sure an empty warning can round-trip with \c StoredDiagnostic.187  Diags.Report(CaptureConsumer.StoredDiags.front());188}189 190class SuppressionMappingTest : public testing::Test {191public:192  SuppressionMappingTest() {193    Diags.setClient(&CaptureConsumer, /*ShouldOwnClient=*/false);194  }195 196protected:197  llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS =198      llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();199  DiagnosticOptions DiagOpts;200  DiagnosticsEngine Diags{DiagnosticIDs::create(), DiagOpts};201 202  llvm::ArrayRef<StoredDiagnostic> diags() {203    return CaptureConsumer.StoredDiags;204  }205 206  SourceLocation locForFile(llvm::StringRef FileName) {207    auto Buf = MemoryBuffer::getMemBuffer("", FileName);208    SourceManager &SM = Diags.getSourceManager();209    FileID FooID = SM.createFileID(std::move(Buf));210    return SM.getLocForStartOfFile(FooID);211  }212 213private:214  FileManager FM{{}, FS};215  SourceManager SM{Diags, FM};216 217  class CaptureDiagnosticConsumer : public DiagnosticConsumer {218  public:219    std::vector<StoredDiagnostic> StoredDiags;220 221    void HandleDiagnostic(DiagnosticsEngine::Level level,222                          const Diagnostic &Info) override {223      StoredDiags.push_back(StoredDiagnostic(level, Info));224    }225  };226  CaptureDiagnosticConsumer CaptureConsumer;227};228 229MATCHER_P(WithMessage, Msg, "has diagnostic message") {230  return arg.getMessage() == Msg;231}232MATCHER(IsError, "has error severity") {233  return arg.getLevel() == DiagnosticsEngine::Level::Error;234}235 236TEST_F(SuppressionMappingTest, MissingMappingFile) {237  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";238  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);239  EXPECT_THAT(diags(), ElementsAre(AllOf(240                           WithMessage("no such file or directory: 'foo.txt'"),241                           IsError())));242}243 244TEST_F(SuppressionMappingTest, MalformedFile) {245  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";246  FS->addFile("foo.txt", /*ModificationTime=*/{},247              llvm::MemoryBuffer::getMemBuffer("asdf", "foo.txt"));248  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);249  EXPECT_THAT(diags(),250              ElementsAre(AllOf(251                  WithMessage("failed to process suppression mapping file "252                              "'foo.txt': malformed line 1: 'asdf'"),253                  IsError())));254}255 256TEST_F(SuppressionMappingTest, UnknownDiagName) {257  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";258  FS->addFile("foo.txt", /*ModificationTime=*/{},259              llvm::MemoryBuffer::getMemBuffer("[non-existing-warning]"));260  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);261  EXPECT_THAT(diags(), ElementsAre(WithMessage(262                           "unknown warning option 'non-existing-warning'")));263}264 265TEST_F(SuppressionMappingTest, SuppressesGroup) {266  llvm::StringLiteral SuppressionMappingFile = R"(267  [unused]268  src:*)";269  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";270  FS->addFile("foo.txt", /*ModificationTime=*/{},271              llvm::MemoryBuffer::getMemBuffer(SuppressionMappingFile));272  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);273  EXPECT_THAT(diags(), IsEmpty());274 275  SourceLocation FooLoc = locForFile("foo.cpp");276  EXPECT_TRUE(Diags.isSuppressedViaMapping(diag::warn_unused_function, FooLoc));277  EXPECT_FALSE(Diags.isSuppressedViaMapping(diag::warn_deprecated, FooLoc));278}279 280TEST_F(SuppressionMappingTest, EmitCategoryIsExcluded) {281  llvm::StringLiteral SuppressionMappingFile = R"(282  [unused]283  src:*284  src:*foo.cpp=emit)";285  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";286  FS->addFile("foo.txt", /*ModificationTime=*/{},287              llvm::MemoryBuffer::getMemBuffer(SuppressionMappingFile));288  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);289  EXPECT_THAT(diags(), IsEmpty());290 291  EXPECT_TRUE(Diags.isSuppressedViaMapping(diag::warn_unused_function,292                                           locForFile("bar.cpp")));293  EXPECT_FALSE(Diags.isSuppressedViaMapping(diag::warn_unused_function,294                                            locForFile("foo.cpp")));295}296 297TEST_F(SuppressionMappingTest, LastMatchWins) {298  llvm::StringLiteral SuppressionMappingFile = R"(299  [unused]300  src:*clang/*301  src:*clang/lib/Sema/*=emit302  src:*clang/lib/Sema/foo*)";303  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";304  FS->addFile("foo.txt", /*ModificationTime=*/{},305              llvm::MemoryBuffer::getMemBuffer(SuppressionMappingFile));306  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);307  EXPECT_THAT(diags(), IsEmpty());308 309  EXPECT_TRUE(Diags.isSuppressedViaMapping(310      diag::warn_unused_function, locForFile("clang/lib/Basic/foo.h")));311  EXPECT_FALSE(Diags.isSuppressedViaMapping(312      diag::warn_unused_function, locForFile("clang/lib/Sema/bar.h")));313  EXPECT_TRUE(Diags.isSuppressedViaMapping(diag::warn_unused_function,314                                           locForFile("clang/lib/Sema/foo.h")));315}316 317TEST_F(SuppressionMappingTest, LongShortMatch) {318  llvm::StringLiteral SuppressionMappingFile = R"(319  [unused]320  src:*test/*321  src:*lld/*=emit)";322  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";323  FS->addFile("foo.txt", /*ModificationTime=*/{},324              llvm::MemoryBuffer::getMemBuffer(SuppressionMappingFile));325  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);326  EXPECT_THAT(diags(), IsEmpty());327 328  EXPECT_TRUE(Diags.isSuppressedViaMapping(diag::warn_unused_function,329                                           locForFile("test/t1.cpp")));330  EXPECT_FALSE(Diags.isSuppressedViaMapping(diag::warn_unused_function,331                                            locForFile("lld/test/t2.cpp")));332}333 334TEST_F(SuppressionMappingTest, ShortLongMatch) {335  llvm::StringLiteral SuppressionMappingFile = R"(336  [unused]337  src:*lld/*=emit338  src:*test/*)";339  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";340  FS->addFile("foo.txt", /*ModificationTime=*/{},341              llvm::MemoryBuffer::getMemBuffer(SuppressionMappingFile));342  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);343  EXPECT_THAT(diags(), IsEmpty());344 345  EXPECT_TRUE(Diags.isSuppressedViaMapping(diag::warn_unused_function,346                                           locForFile("test/t1.cpp")));347  EXPECT_TRUE(Diags.isSuppressedViaMapping(diag::warn_unused_function,348                                           locForFile("lld/test/t2.cpp")));349}350 351TEST_F(SuppressionMappingTest, IsIgnored) {352  llvm::StringLiteral SuppressionMappingFile = R"(353  [unused]354  src:*clang/*)";355  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";356  Diags.getDiagnosticOptions().Warnings = {"unused"};357  FS->addFile("foo.txt", /*ModificationTime=*/{},358              llvm::MemoryBuffer::getMemBuffer(SuppressionMappingFile));359  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);360  ASSERT_THAT(diags(), IsEmpty());361 362  SourceManager &SM = Diags.getSourceManager();363  auto ClangID =364      SM.createFileID(llvm::MemoryBuffer::getMemBuffer("", "clang/foo.h"));365  auto NonClangID =366      SM.createFileID(llvm::MemoryBuffer::getMemBuffer("", "llvm/foo.h"));367  auto PresumedClangID =368      SM.createFileID(llvm::MemoryBuffer::getMemBuffer("", "llvm/foo2.h"));369  // Add a line directive to point into clang/foo.h370  SM.AddLineNote(SM.getLocForStartOfFile(PresumedClangID), 42,371                 SM.getLineTableFilenameID("clang/foo.h"), false, false,372                 clang::SrcMgr::C_User);373 374  EXPECT_TRUE(Diags.isIgnored(diag::warn_unused_function,375                              SM.getLocForStartOfFile(ClangID)));376  EXPECT_FALSE(Diags.isIgnored(diag::warn_unused_function,377                               SM.getLocForStartOfFile(NonClangID)));378  EXPECT_TRUE(Diags.isIgnored(diag::warn_unused_function,379                              SM.getLocForStartOfFile(PresumedClangID)));380 381  // Pretend we have a clang-diagnostic pragma to enforce the warning. Make sure382  // suppressing mapping doesn't take over.383  Diags.setSeverity(diag::warn_unused_function, diag::Severity::Error,384                    SM.getLocForStartOfFile(ClangID));385  EXPECT_FALSE(Diags.isIgnored(diag::warn_unused_function,386                               SM.getLocForStartOfFile(ClangID)));387}388 389TEST_F(SuppressionMappingTest, ParsingRespectsOtherWarningOpts) {390  Diags.getDiagnosticOptions().DiagnosticSuppressionMappingsFile = "foo.txt";391  FS->addFile("foo.txt", /*ModificationTime=*/{},392              llvm::MemoryBuffer::getMemBuffer("[non-existing-warning]"));393  Diags.getDiagnosticOptions().Warnings.push_back("no-unknown-warning-option");394  clang::ProcessWarningOptions(Diags, Diags.getDiagnosticOptions(), *FS);395  EXPECT_THAT(diags(), IsEmpty());396}397} // namespace398