forked from qt-creator/qt-creator
		
	
		
			
				
	
	
		
			352 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			352 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/****************************************************************************
 | 
						|
**
 | 
						|
** Copyright (C) 2019 The Qt Company Ltd.
 | 
						|
** Contact: https://www.qt.io/licensing/
 | 
						|
**
 | 
						|
** This file is part of Qt Creator.
 | 
						|
**
 | 
						|
** Commercial License Usage
 | 
						|
** Licensees holding valid commercial Qt licenses may use this file in
 | 
						|
** accordance with the commercial license agreement provided with the
 | 
						|
** Software or, alternatively, in accordance with the terms contained in
 | 
						|
** a written agreement between you and The Qt Company. For licensing terms
 | 
						|
** and conditions see https://www.qt.io/terms-conditions. For further
 | 
						|
** information use the contact form at https://www.qt.io/contact-us.
 | 
						|
**
 | 
						|
** GNU General Public License Usage
 | 
						|
** Alternatively, this file may be used under the terms of the GNU
 | 
						|
** General Public License version 3 as published by the Free Software
 | 
						|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
 | 
						|
** included in the packaging of this file. Please review the following
 | 
						|
** information to ensure the GNU General Public License requirements will
 | 
						|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
 | 
						|
**
 | 
						|
****************************************************************************/
 | 
						|
 | 
						|
#include "googletest.h"
 | 
						|
 | 
						|
#include <clangtools/clangtoolslogfilereader.h>
 | 
						|
 | 
						|
#include <utils/fileutils.h>
 | 
						|
#include <utils/qtcassert.h>
 | 
						|
 | 
						|
#define TESTDATA TESTDATA_DIR "/clangtools/"
 | 
						|
 | 
						|
using namespace ClangTools::Internal;
 | 
						|
using Debugger::DiagnosticLocation;
 | 
						|
 | 
						|
namespace {
 | 
						|
 | 
						|
class ReadExportedDiagnostics : public ::testing::Test
 | 
						|
{
 | 
						|
protected:
 | 
						|
    void SetUp() override
 | 
						|
    {
 | 
						|
        ASSERT_TRUE(temporaryDir.isValid());
 | 
						|
    }
 | 
						|
 | 
						|
    // Replace FILE_PATH with a real absolute file path in the *.yaml files.
 | 
						|
    QString createFile(const QString &yamlFilePath, const QString &filePathToInject)
 | 
						|
    {
 | 
						|
        QTC_ASSERT(QDir::isAbsolutePath(filePathToInject), return QString());
 | 
						|
        const QString newFileName = temporaryDir.filePath(QFileInfo(yamlFilePath).fileName());
 | 
						|
 | 
						|
        Utils::FileReader reader;
 | 
						|
        if (QTC_GUARD(reader.fetch(Utils::FilePath::fromString(yamlFilePath),
 | 
						|
                                   QIODevice::ReadOnly | QIODevice::Text))) {
 | 
						|
            QByteArray contents = reader.data();
 | 
						|
            contents.replace("FILE_PATH", filePathToInject.toLocal8Bit());
 | 
						|
 | 
						|
            Utils::FileSaver fileSaver(Utils::FilePath::fromString(newFileName),
 | 
						|
                                       QIODevice::WriteOnly | QIODevice::Text);
 | 
						|
            QTC_CHECK(fileSaver.write(contents));
 | 
						|
            QTC_CHECK(fileSaver.finalize());
 | 
						|
        }
 | 
						|
 | 
						|
        return newFileName;
 | 
						|
    }
 | 
						|
 | 
						|
protected:
 | 
						|
    QString errorMessage;
 | 
						|
    Utils::TemporaryDirectory temporaryDir{"clangtools-tests-XXXXXX"};
 | 
						|
};
 | 
						|
 | 
						|
TEST_F(ReadExportedDiagnostics, NotExistingFile)
 | 
						|
{
 | 
						|
    Diagnostics diags = readExportedDiagnostics(Utils::FilePath::fromString("notExistingFile.yaml"),
 | 
						|
                                                {},
 | 
						|
                                                &errorMessage);
 | 
						|
 | 
						|
    ASSERT_THAT(diags, IsEmpty());
 | 
						|
    ASSERT_FALSE(errorMessage.isEmpty());
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(ReadExportedDiagnostics, EmptyFile)
 | 
						|
{
 | 
						|
    Diagnostics diags = readExportedDiagnostics(Utils::FilePath::fromString(TESTDATA "empty.yaml"),
 | 
						|
                                                {},
 | 
						|
                                                &errorMessage);
 | 
						|
 | 
						|
    ASSERT_THAT(diags, IsEmpty());
 | 
						|
    ASSERT_TRUE(errorMessage.isEmpty());
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(ReadExportedDiagnostics, UnexpectedFileContents)
 | 
						|
{
 | 
						|
    const QString sourceFile = TESTDATA "tidy.modernize-use-nullptr.cpp";
 | 
						|
 | 
						|
    Diagnostics diags = readExportedDiagnostics(Utils::FilePath::fromString(sourceFile),
 | 
						|
                                                {},
 | 
						|
                                                &errorMessage);
 | 
						|
 | 
						|
    ASSERT_FALSE(errorMessage.isEmpty());
 | 
						|
    ASSERT_THAT(diags, IsEmpty());
 | 
						|
}
 | 
						|
 | 
						|
static QString appendYamlSuffix(const char *filePathFragment)
 | 
						|
{
 | 
						|
    const QString yamlSuffix = QLatin1String(Utils::HostOsInfo::isWindowsHost()
 | 
						|
                                             ? "_win.yaml" : ".yaml");
 | 
						|
    return filePathFragment + yamlSuffix;
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(ReadExportedDiagnostics, Tidy)
 | 
						|
{
 | 
						|
    const Utils::FilePath sourceFile = Utils::FilePath::fromString(
 | 
						|
        TESTDATA "tidy.modernize-use-nullptr.cpp");
 | 
						|
    const QString exportedFile = createFile(appendYamlSuffix(TESTDATA "tidy.modernize-use-nullptr"),
 | 
						|
                                            sourceFile.toString());
 | 
						|
    Diagnostic expectedDiag;
 | 
						|
    expectedDiag.name = "modernize-use-nullptr";
 | 
						|
    expectedDiag.location = {sourceFile, 2, 25};
 | 
						|
    expectedDiag.description = "use nullptr [modernize-use-nullptr]";
 | 
						|
    expectedDiag.type = "warning";
 | 
						|
    expectedDiag.hasFixits = true;
 | 
						|
    expectedDiag.explainingSteps = {
 | 
						|
        ExplainingStep{"nullptr",
 | 
						|
                       expectedDiag.location,
 | 
						|
                       {expectedDiag.location, {sourceFile, 2, 26}},
 | 
						|
                       true}};
 | 
						|
 | 
						|
    Diagnostics diags = readExportedDiagnostics(Utils::FilePath::fromString(exportedFile),
 | 
						|
                                                {},
 | 
						|
                                                &errorMessage);
 | 
						|
 | 
						|
    ASSERT_TRUE(errorMessage.isEmpty());
 | 
						|
    ASSERT_THAT(diags, ElementsAre(expectedDiag));
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(ReadExportedDiagnostics, AcceptDiagsFromFilePaths_None)
 | 
						|
{
 | 
						|
    const QString sourceFile = TESTDATA "tidy.modernize-use-nullptr.cpp";
 | 
						|
    const QString exportedFile = createFile(TESTDATA "tidy.modernize-use-nullptr.yaml", sourceFile);
 | 
						|
    const auto acceptNone = [](const Utils::FilePath &) { return false; };
 | 
						|
 | 
						|
    Diagnostics diags = readExportedDiagnostics(Utils::FilePath::fromString(exportedFile),
 | 
						|
                                                acceptNone,
 | 
						|
                                                &errorMessage);
 | 
						|
 | 
						|
    ASSERT_TRUE(errorMessage.isEmpty());
 | 
						|
    ASSERT_THAT(diags, IsEmpty());
 | 
						|
}
 | 
						|
 | 
						|
// Diagnostics from clang (static) analyzer passed through via clang-tidy
 | 
						|
TEST_F(ReadExportedDiagnostics, Tidy_ClangAnalyzer)
 | 
						|
{
 | 
						|
    const Utils::FilePath sourceFile = Utils::FilePath::fromString(TESTDATA
 | 
						|
                                                                   "clang-analyzer.dividezero.cpp");
 | 
						|
    const QString exportedFile = createFile(appendYamlSuffix(TESTDATA "clang-analyzer.dividezero"),
 | 
						|
                                            sourceFile.toString());
 | 
						|
    Diagnostic expectedDiag;
 | 
						|
    expectedDiag.name = "clang-analyzer-core.DivideZero";
 | 
						|
    expectedDiag.location = {sourceFile, 4, 15};
 | 
						|
    expectedDiag.description = "Division by zero [clang-analyzer-core.DivideZero]";
 | 
						|
    expectedDiag.type = "warning";
 | 
						|
    expectedDiag.hasFixits = false;
 | 
						|
    expectedDiag.explainingSteps = {
 | 
						|
        ExplainingStep{"Assuming 'z' is equal to 0",
 | 
						|
                       {sourceFile, 3, 7},
 | 
						|
                       {},
 | 
						|
                       false,
 | 
						|
        },
 | 
						|
        ExplainingStep{"Taking true branch",
 | 
						|
                       {sourceFile, 3, 3},
 | 
						|
                       {},
 | 
						|
                       false,
 | 
						|
        },
 | 
						|
        ExplainingStep{"Division by zero",
 | 
						|
                       {sourceFile, 4, 15},
 | 
						|
                       {},
 | 
						|
                       false,
 | 
						|
        },
 | 
						|
    };
 | 
						|
 | 
						|
    Diagnostics diags = readExportedDiagnostics(Utils::FilePath::fromString(exportedFile),
 | 
						|
                                                {},
 | 
						|
                                                &errorMessage);
 | 
						|
 | 
						|
    ASSERT_TRUE(errorMessage.isEmpty());
 | 
						|
    ASSERT_THAT(diags, ElementsAre(expectedDiag));
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(ReadExportedDiagnostics, Clazy)
 | 
						|
{
 | 
						|
    const Utils::FilePath sourceFile = Utils::FilePath::fromString(TESTDATA "clazy.qgetenv.cpp");
 | 
						|
    const QString exportedFile = createFile(appendYamlSuffix(TESTDATA "clazy.qgetenv"),
 | 
						|
                                            sourceFile.toString());
 | 
						|
    Diagnostic expectedDiag;
 | 
						|
    expectedDiag.name = "clazy-qgetenv";
 | 
						|
    expectedDiag.location = {sourceFile, 7, 5};
 | 
						|
    expectedDiag.description = "qgetenv().isEmpty() allocates. Use qEnvironmentVariableIsEmpty() instead [clazy-qgetenv]";
 | 
						|
    expectedDiag.type = "warning";
 | 
						|
    expectedDiag.hasFixits = true;
 | 
						|
    expectedDiag.explainingSteps = {
 | 
						|
        ExplainingStep{"qEnvironmentVariableIsEmpty",
 | 
						|
                       expectedDiag.location,
 | 
						|
                       {expectedDiag.location, {sourceFile, 7, 12}},
 | 
						|
                       true
 | 
						|
        },
 | 
						|
        ExplainingStep{")",
 | 
						|
                       {sourceFile, 7, 18},
 | 
						|
                       {{sourceFile, 7, 18}, {sourceFile, 7, 29}},
 | 
						|
                       true},
 | 
						|
    };
 | 
						|
 | 
						|
    Diagnostics diags = readExportedDiagnostics(Utils::FilePath::fromString(exportedFile),
 | 
						|
                                                {},
 | 
						|
                                                &errorMessage);
 | 
						|
 | 
						|
    ASSERT_TRUE(errorMessage.isEmpty());
 | 
						|
    ASSERT_THAT(diags, ElementsAre(expectedDiag));
 | 
						|
}
 | 
						|
 | 
						|
class ByteOffsetInUtf8TextToLineColumn : public ::testing::Test
 | 
						|
{
 | 
						|
protected:
 | 
						|
    const char *empty = "";
 | 
						|
    const char *asciiWord = "FOO";
 | 
						|
    const char *asciiMultiLine = "FOO\nBAR";
 | 
						|
    const char *asciiMultiLine_dos = "FOO\r\nBAR";
 | 
						|
    const char *asciiEmptyMultiLine = "\n\n";
 | 
						|
    // U+00FC  - 2 code units in UTF8, 1 in UTF16 - LATIN SMALL LETTER U WITH DIAERESIS
 | 
						|
    // U+4E8C  - 3 code units in UTF8, 1 in UTF16 - CJK UNIFIED IDEOGRAPH-4E8C
 | 
						|
    // U+10302 - 4 code units in UTF8, 2 in UTF16 - OLD ITALIC LETTER KE
 | 
						|
    const char *nonAsciiMultiLine = "\xc3\xbc" "\n"
 | 
						|
                                    "\xe4\xba\x8c" "\n"
 | 
						|
                                    "\xf0\x90\x8c\x82" "X";
 | 
						|
 | 
						|
    // Convenience
 | 
						|
    const char *text = nullptr;
 | 
						|
};
 | 
						|
 | 
						|
TEST_F(ByteOffsetInUtf8TextToLineColumn, InvalidText)
 | 
						|
{
 | 
						|
    ASSERT_FALSE(byteOffsetInUtf8TextToLineColumn(nullptr, 0));
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(ByteOffsetInUtf8TextToLineColumn, InvalidOffset_EmptyInput)
 | 
						|
{
 | 
						|
    ASSERT_FALSE(byteOffsetInUtf8TextToLineColumn(empty, 0));
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(ByteOffsetInUtf8TextToLineColumn, InvalidOffset_Before)
 | 
						|
{
 | 
						|
    ASSERT_FALSE(byteOffsetInUtf8TextToLineColumn(asciiWord, -1));
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(ByteOffsetInUtf8TextToLineColumn, InvalidOffset_After)
 | 
						|
{
 | 
						|
    ASSERT_FALSE(byteOffsetInUtf8TextToLineColumn(asciiWord, 3));
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(ByteOffsetInUtf8TextToLineColumn, InvalidOffset_NotFirstByteOfMultiByte)
 | 
						|
{
 | 
						|
    ASSERT_FALSE(byteOffsetInUtf8TextToLineColumn(nonAsciiMultiLine, 1));
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(ByteOffsetInUtf8TextToLineColumn, StartOfFirstLine)
 | 
						|
{
 | 
						|
    auto info = byteOffsetInUtf8TextToLineColumn(asciiWord, 0);
 | 
						|
 | 
						|
    ASSERT_TRUE(info);
 | 
						|
    ASSERT_THAT(info->line, Eq(1));
 | 
						|
    ASSERT_THAT(info->column, Eq(1));
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(ByteOffsetInUtf8TextToLineColumn, EndOfFirstLine)
 | 
						|
{
 | 
						|
    auto info = byteOffsetInUtf8TextToLineColumn(asciiWord, 2);
 | 
						|
 | 
						|
    ASSERT_TRUE(info);
 | 
						|
    ASSERT_THAT(info->line, Eq(1));
 | 
						|
    ASSERT_THAT(info->column, Eq(3));
 | 
						|
}
 | 
						|
 | 
						|
// The invocation
 | 
						|
//
 | 
						|
//   clang-tidy "-checks=-*,readability-braces-around-statements" /path/to/file
 | 
						|
//
 | 
						|
// for the code
 | 
						|
//
 | 
						|
//   void f(bool b)
 | 
						|
//   {
 | 
						|
//       if (b)
 | 
						|
//           f(b);
 | 
						|
//   }
 | 
						|
//
 | 
						|
// emits
 | 
						|
//
 | 
						|
//   3:11: warning: statement should be inside braces [readability-braces-around-statements]
 | 
						|
//
 | 
						|
// The new line in the if-line is considered as column 11, which is normally not visible in the
 | 
						|
// editor.
 | 
						|
TEST_F(ByteOffsetInUtf8TextToLineColumn, OffsetPointingToLineSeparator_unix)
 | 
						|
{
 | 
						|
    auto info = byteOffsetInUtf8TextToLineColumn(asciiMultiLine, 3);
 | 
						|
 | 
						|
    ASSERT_TRUE(info);
 | 
						|
    ASSERT_THAT(info->line, Eq(1));
 | 
						|
    ASSERT_THAT(info->column, Eq(4));
 | 
						|
}
 | 
						|
 | 
						|
// For a file with dos style line endings ("\r\n"), clang-tidy points to '\r'.
 | 
						|
TEST_F(ByteOffsetInUtf8TextToLineColumn, OffsetPointingToLineSeparator_dos)
 | 
						|
{
 | 
						|
    auto info = byteOffsetInUtf8TextToLineColumn(asciiMultiLine_dos, 3);
 | 
						|
 | 
						|
    ASSERT_TRUE(info);
 | 
						|
    ASSERT_THAT(info->line, Eq(1));
 | 
						|
    ASSERT_THAT(info->column, Eq(4));
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(ByteOffsetInUtf8TextToLineColumn, StartOfSecondLine)
 | 
						|
{
 | 
						|
    auto info = byteOffsetInUtf8TextToLineColumn(asciiMultiLine, 4);
 | 
						|
 | 
						|
    ASSERT_TRUE(info);
 | 
						|
    ASSERT_THAT(info->line, Eq(2));
 | 
						|
    ASSERT_THAT(info->column, Eq(1));
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(ByteOffsetInUtf8TextToLineColumn, MultiByteCodePoint1)
 | 
						|
{
 | 
						|
    auto info = byteOffsetInUtf8TextToLineColumn(nonAsciiMultiLine, 3);
 | 
						|
 | 
						|
    ASSERT_TRUE(info);
 | 
						|
    ASSERT_THAT(info->line, Eq(2));
 | 
						|
    ASSERT_THAT(info->column, Eq(1));
 | 
						|
}
 | 
						|
 | 
						|
TEST_F(ByteOffsetInUtf8TextToLineColumn, MultiByteCodePoint2)
 | 
						|
{
 | 
						|
    auto info = byteOffsetInUtf8TextToLineColumn(nonAsciiMultiLine, 11);
 | 
						|
 | 
						|
    ASSERT_TRUE(info);
 | 
						|
    ASSERT_THAT(info->line, Eq(3));
 | 
						|
    ASSERT_THAT(info->column, Eq(2));
 | 
						|
}
 | 
						|
 | 
						|
} // namespace
 | 
						|
 | 
						|
#undef TESTDATA
 |