/*
 * Cppcheck - A tool for static C/C++ code analysis
 * Copyright (C) 2007-2012 Daniel Marjamäki and Cppcheck team.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */


#include "tokenize.h"
#include "testsuite.h"
#include "checkunusedfunctions.h"
#include <sstream>

extern std::ostringstream errout;

class TestUnusedFunctions : public TestFixture {
public:
    TestUnusedFunctions() : TestFixture("TestUnusedFunctions")
    { }

private:


    void run() {
        TEST_CASE(incondition);
        TEST_CASE(return1);
        TEST_CASE(return2);
        TEST_CASE(callback1);
        TEST_CASE(else1);
        TEST_CASE(functionpointer);
        TEST_CASE(template1);
        TEST_CASE(template2);
        TEST_CASE(throwIsNotAFunction);
        TEST_CASE(unusedError);
        TEST_CASE(unusedMain);
        TEST_CASE(initializationIsNotAFunction);
        TEST_CASE(operator1);   // #3195
        TEST_CASE(returnRef);

        TEST_CASE(multipleFiles);   // same function name in multiple files

        TEST_CASE(lineNumber); // Ticket 3059
    }

    void check(const char code[]) {
        // Clear the error buffer..
        errout.str("");

        Settings settings;
        settings.addEnabled("style");

        // Tokenize..
        Tokenizer tokenizer(&settings, this);
        std::istringstream istr(code);
        tokenizer.tokenize(istr, "test.cpp");

        // Check for unused functions..
        CheckUnusedFunctions checkUnusedFunctions(&tokenizer, &settings, this);
        checkUnusedFunctions.parseTokens(tokenizer);
        checkUnusedFunctions.check(this);
    }

    void incondition() {
        check("int f1()\n"
              "{\n"
              "    if (f1())\n"
              "    { }\n"
              "}\n");
        ASSERT_EQUALS("", errout.str());
    }

    void return1() {
        check("int f1()\n"
              "{\n"
              "    return f1();\n"
              "}\n");
        ASSERT_EQUALS("", errout.str());
    }

    void return2() {
        check("char * foo()\n"
              "{\n"
              "    return *foo();\n"
              "}\n");
        ASSERT_EQUALS("", errout.str());
    }

    void callback1() {
        check("void f1()\n"
              "{\n"
              "    void (*f)() = cond ? f1 : NULL;\n"
              "}\n");
        ASSERT_EQUALS("", errout.str());
    }

    void else1() {
        check("void f1()\n"
              "{\n"
              "    if (cond) ;\n"
              "    else f1();\n"
              "}\n");
        ASSERT_EQUALS("", errout.str());
    }

    void functionpointer() {
        check("namespace abc {\n"
              "void foo() { }\n"
              "};\n"
              "\n"
              "int main()\n"
              "{\n"
              "    f(&abc::foo);\n"
              "    return 0\n"
              "}\n");
        ASSERT_EQUALS("", errout.str());

        check("namespace abc {\n"
              "void foo() { }\n"
              "};\n"
              "\n"
              "int main()\n"
              "{\n"
              "    f = &abc::foo;\n"
              "    return 0\n"
              "}\n");
        ASSERT_EQUALS("", errout.str());

        check("namespace abc {\n"  // #3875
              "void foo() { }\n"
              "};\n"
              "\n"
              "int main()\n"
              "{\n"
              "    f(abc::foo);\n"
              "    return 0\n"
              "}\n");
        ASSERT_EQUALS("", errout.str());
    }

    void template1() {
        check("template<class T> void foo() { }\n"
              "\n"
              "int main()\n"
              "{\n"
              "    foo<int>();\n"
              "    return 0\n"
              "}\n");
        ASSERT_EQUALS("", errout.str());
    }

    void template2() {
        check("void f() { }\n"
              "\n"
              "template<class T> void g()\n"
              "{\n"
              "    f();\n"
              "}\n");
        ASSERT_EQUALS("", errout.str());
    }

    void throwIsNotAFunction() {
        check("struct A {void f() const throw () {}}; int main() {A a; a.f();}\n");
        ASSERT_EQUALS("", errout.str());
    }

    void unusedError() {
        check("void foo() {}\n"
              "int main()\n");
        ASSERT_EQUALS("[test.cpp:1]: (style) The function 'foo' is never used.\n", errout.str());

        check("void foo() const {}\n"
              "int main()\n");
        ASSERT_EQUALS("[test.cpp:1]: (style) The function 'foo' is never used.\n", errout.str());

        check("void foo() const throw() {}\n"
              "int main()\n");
        ASSERT_EQUALS("[test.cpp:1]: (style) The function 'foo' is never used.\n", errout.str());

        check("void foo() throw() {}\n"
              "int main()\n");
        ASSERT_EQUALS("[test.cpp:1]: (style) The function 'foo' is never used.\n", errout.str());
    }

    void unusedMain() {
        check("int main() { }\n");
        ASSERT_EQUALS("", errout.str());

        check("int _tmain() { }\n");
        ASSERT_EQUALS("", errout.str());

        check("int WinMain() { }\n");
        ASSERT_EQUALS("", errout.str());
    }

    void initializationIsNotAFunction() {
        check("struct B: N::A {\n"
              "  B(): N::A() {};\n"
              "};\n");
        ASSERT_EQUALS("", errout.str());
    }

    void operator1() {
        check("struct Foo { void operator()(int a) {} };");
        ASSERT_EQUALS("", errout.str());
    }

    void returnRef() {
        check("int& foo() {return x;}");
        ASSERT_EQUALS("[test.cpp:1]: (style) The function 'foo' is never used.\n", errout.str());
    }

    void multipleFiles() {
        CheckUnusedFunctions c;

        // Clear the error buffer..
        errout.str("");

        const char code[] = "static void f() { }";

        for (int i = 1; i <= 2; ++i) {
            std::ostringstream fname;
            fname << "test" << i << ".cpp";

            // Clear the error buffer..
            errout.str("");

            Settings settings;

            Tokenizer tokenizer(&settings, this);
            std::istringstream istr(code);
            tokenizer.tokenize(istr, fname.str().c_str());

            c.parseTokens(tokenizer);
        }

        // Check for unused functions..
        c.check(this);

        ASSERT_EQUALS("[test1.cpp:1]: (style) The function 'f' is never used.\n", errout.str());
    }

    void lineNumber() {
        check("void foo() {}\n"
              "void bar() {}\n"
              "int main()\n");
        ASSERT_EQUALS("[test.cpp:2]: (style) The function 'bar' is never used.\n"
                      "[test.cpp:1]: (style) The function 'foo' is never used.\n", errout.str());
    }
};

REGISTER_TEST(TestUnusedFunctions)
