mgartmann updated this revision to Diff 344711. mgartmann added a comment. Re-add description for this check in `ReleaseNotes.rst`. Adjust `AvoidStdIoOutsideMainCheck.cpp` third matcher to call `hasAnyName()` with a vector.
Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D99646/new/ https://reviews.llvm.org/D99646 Files: clang-tools-extra/clang-tidy/misc/AvoidStdIoOutsideMainCheck.cpp clang-tools-extra/clang-tidy/misc/AvoidStdIoOutsideMainCheck.h clang-tools-extra/clang-tidy/misc/CMakeLists.txt clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp clang-tools-extra/docs/ReleaseNotes.rst clang-tools-extra/docs/clang-tidy/checks/list.rst clang-tools-extra/docs/clang-tidy/checks/misc-avoid-std-io-outside-main.rst clang-tools-extra/test/clang-tidy/checkers/misc-avoid-std-io-outside-main.cpp
Index: clang-tools-extra/test/clang-tidy/checkers/misc-avoid-std-io-outside-main.cpp =================================================================== --- /dev/null +++ clang-tools-extra/test/clang-tidy/checkers/misc-avoid-std-io-outside-main.cpp @@ -0,0 +1,138 @@ +// RUN: %check_clang_tidy %s misc-avoid-std-io-outside-main %t + +namespace std { +struct string { + string(const char *); + ~string(); +}; + +struct Ostream { + Ostream &operator<<(string Message); +}; + +struct Istream { + Istream &operator>>(string Message); +}; + +Ostream cout{}, wcout{}, cerr{}, wcerr{}; +Istream cin{}, wcin{}; + +int printf(const char *Format, ...); +int vprintf(const char *const, ...); +int puts(const char *Str); +int putchar(int Character); +int scanf(const char *Format, ...); +int getchar(void); +char *gets(char *Str); +} // namespace std + +int printf(const char *Format, ...); +int vprintf(const char *const, ...); +int puts(const char *Str); +int putchar(int Character); +int scanf(const char *Format, ...); +int getchar(void); +char *gets(char *Str); + +namespace arbitrary_namespace { +std::Ostream cout{}; +std::Istream cin{}; +} // namespace arbitrary_namespace + +void anyNonMainFunction() { + // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: predefined standard stream objects should not be used outside the main function [misc-avoid-std-io-outside-main] + std::cout << "This should trigger the check"; + + // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: predefined standard stream objects should not be used outside the main function [misc-avoid-std-io-outside-main] + std::wcout << "This should trigger the check"; + + // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: predefined standard stream objects should not be used outside the main function [misc-avoid-std-io-outside-main] + std::cerr << "This should trigger the check"; + + // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: predefined standard stream objects should not be used outside the main function [misc-avoid-std-io-outside-main] + std::wcerr << "This should trigger the check"; + + arbitrary_namespace::cout << "This should not trigger the check"; // OK + + std::string Foo{"bar"}; + + // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: predefined standard stream objects should not be used outside the main function [misc-avoid-std-io-outside-main] + std::cin >> Foo; + + // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: predefined standard stream objects should not be used outside the main function [misc-avoid-std-io-outside-main] + std::wcin >> Foo; + + arbitrary_namespace::cin >> Foo; // OK + + // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: cstdio functions should not be used outside the main function [misc-avoid-std-io-outside-main] + std::printf("This should trigger the check"); + // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: cstdio functions should not be used outside the main function [misc-avoid-std-io-outside-main] + printf("This should trigger the check"); + + // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: cstdio functions should not be used outside the main function [misc-avoid-std-io-outside-main] + std::puts("This should trigger the check"); + // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: cstdio functions should not be used outside the main function [misc-avoid-std-io-outside-main] + puts("This should trigger the check"); + + // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: cstdio functions should not be used outside the main function [misc-avoid-std-io-outside-main] + std::putchar('m'); + // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: cstdio functions should not be used outside the main function [misc-avoid-std-io-outside-main] + putchar('m'); + + char Input[5]; + // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: cstdio functions should not be used outside the main function [misc-avoid-std-io-outside-main] + std::scanf("%s", Input); + // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: cstdio functions should not be used outside the main function [misc-avoid-std-io-outside-main] + scanf("%s", Input); + + // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: cstdio functions should not be used outside the main function [misc-avoid-std-io-outside-main] + std::getchar(); + // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: cstdio functions should not be used outside the main function [misc-avoid-std-io-outside-main] + getchar(); + + // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: cstdio functions should not be used outside the main function [misc-avoid-std-io-outside-main] + std::gets(Input); + // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: cstdio functions should not be used outside the main function [misc-avoid-std-io-outside-main] + gets(Input); + + // CHECK-MESSAGES: :[[@LINE+1]]:8: warning: cstdio functions should not be used outside the main function [misc-avoid-std-io-outside-main] + std::vprintf("This should trigger the check %d", 1); + // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: cstdio functions should not be used outside the main function [misc-avoid-std-io-outside-main] + vprintf("This should trigger the check %d", 1); + + // CHECK-MESSAGES: :[[@LINE+1]]:17: warning: cstdio functions should not be used outside the main function [misc-avoid-std-io-outside-main] + auto Print = &puts; + // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: cstdio functions should not be used outside the main function [misc-avoid-std-io-outside-main] + Print("This should trigger the check"); +} + +int main() { + std::cout << "This should not trigger the check"; // OK + std::wcout << "This should not trigger the check"; // OK + std::cerr << "This should not trigger the check"; // OK + std::wcerr << "This should not trigger the check"; // OK + arbitrary_namespace::cout << "This should not trigger the check"; // OK + + std::string Foo{"bar"}; + std::cin >> Foo; // OK + std::wcin >> Foo; // OK + arbitrary_namespace::cin >> Foo; // OK + + char Input[5]; + std::printf("This should not trigger the check"); // OK + std::puts("This should not trigger the check"); // OK + std::putchar('m'); // OK + std::scanf("%s", Input); // OK + std::getchar(); // OK + std::gets(Input); // OK + std::vprintf("This should not trigger the check %d", 1); // OK + printf("This should not trigger the check"); // OK + puts("This should not trigger the check"); // OK + putchar('m'); // OK + scanf("%s", Input); // OK + getchar(); // OK + gets(Input); // OK + vprintf("This should not trigger the check %d", 1); // OK + auto Print = &puts; // OK + Print("This should not trigger the check"); // OK +} Index: clang-tools-extra/docs/clang-tidy/checks/misc-avoid-std-io-outside-main.rst =================================================================== --- /dev/null +++ clang-tools-extra/docs/clang-tidy/checks/misc-avoid-std-io-outside-main.rst @@ -0,0 +1,53 @@ +.. title:: clang-tidy - misc-avoid-std-io-outside-main + +misc-avoid-std-io-outside-main +============= + +Diagnoses if a predefined standard stream object (``cin``, ``wcin``, +``cout``, ``wcout``, ``cerr`` or ``wcerr``) or a ``cstdio``/``stdio.h`` +function is used outside the ``main`` function. + +For instance, in the following code, the use of ``std::cout`` and ``printf()`` +outside of ``main()`` would get flagged whereas the use of them inside +``main()`` is not flagged: + +.. code-block:: c++ + + #include <iostream> + #include <cstdio> + + void some_function() { + std::cout << "This triggers the check."; + ~~~~ + std::printf("This triggers the check."); + ~~~~~ + } + + int main() { + std::cout << "This does not trigger the check."; + std::printf("This does not trigger the check."); + } + +Since the predefined standard stream objects are global objects, their use +outside of ``main()`` worsens a program's testability and is thus discouraged. +Instead, those objects should only be used inside ``main()``. They can then be +passed as arguments to other functions like so: + +.. code-block:: c++ + + #include <iostream> + + void some_function(std::istream & in, std::ostream & out) { + out << "This does not trigger the check."; + + int i{0}; + in >> i; + } + + int main() { + some_function(std::cin, std::cout); + } + +In contrast to using ``std::cin`` and ``std::cout`` directly, in the above +example, it is possible to inject mocked stream objects into +``some_function()`` during testing. Index: clang-tools-extra/docs/clang-tidy/checks/list.rst =================================================================== --- clang-tools-extra/docs/clang-tidy/checks/list.rst +++ clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -202,6 +202,7 @@ `llvmlibc-callee-namespace <llvmlibc-callee-namespace.html>`_, `llvmlibc-implementation-in-namespace <llvmlibc-implementation-in-namespace.html>`_, `llvmlibc-restrict-system-libc-headers <llvmlibc-restrict-system-libc-headers.html>`_, "Yes" + `misc-misc-avoid-std-io-outside-main <misc-avoid-std-io-outside-main.html>`_, `misc-definitions-in-headers <misc-definitions-in-headers.html>`_, "Yes" `misc-misplaced-const <misc-misplaced-const.html>`_, `misc-new-delete-overloads <misc-new-delete-overloads.html>`_, Index: clang-tools-extra/docs/ReleaseNotes.rst =================================================================== --- clang-tools-extra/docs/ReleaseNotes.rst +++ clang-tools-extra/docs/ReleaseNotes.rst @@ -95,6 +95,14 @@ Finds member initializations in the constructor body which can be placed into the initialization list instead. +- New :doc:`misc-avoid-std-io-outside-main + <clang-tidy/checks/misc-avoid-std-io-outside-main>` check. + + Finds predefined standard stream objects (``cin``, ``wcin``, ``cout``, + ``wcout``, ``cerr`` or ``wcerr``) that are used outside the ``main`` + function. It also finds uses of ``cstdio``/``stdio.h`` functions like + ``printf()`` outside the ``main`` function. + New check aliases ^^^^^^^^^^^^^^^^^ Index: clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp =================================================================== --- clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp +++ clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp @@ -9,6 +9,7 @@ #include "../ClangTidy.h" #include "../ClangTidyModule.h" #include "../ClangTidyModuleRegistry.h" +#include "AvoidStdIoOutsideMainCheck.h" #include "DefinitionsInHeadersCheck.h" #include "MisplacedConstCheck.h" #include "NewDeleteOverloadsCheck.h" @@ -31,6 +32,8 @@ class MiscModule : public ClangTidyModule { public: void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { + CheckFactories.registerCheck<AvoidStdIoOutsideMainCheck>( + "misc-avoid-std-io-outside-main"); CheckFactories.registerCheck<DefinitionsInHeadersCheck>( "misc-definitions-in-headers"); CheckFactories.registerCheck<MisplacedConstCheck>("misc-misplaced-const"); Index: clang-tools-extra/clang-tidy/misc/CMakeLists.txt =================================================================== --- clang-tools-extra/clang-tidy/misc/CMakeLists.txt +++ clang-tools-extra/clang-tidy/misc/CMakeLists.txt @@ -4,6 +4,7 @@ ) add_clang_library(clangTidyMiscModule + AvoidStdIoOutsideMainCheck.cpp DefinitionsInHeadersCheck.cpp MiscTidyModule.cpp MisplacedConstCheck.cpp Index: clang-tools-extra/clang-tidy/misc/AvoidStdIoOutsideMainCheck.h =================================================================== --- /dev/null +++ clang-tools-extra/clang-tidy/misc/AvoidStdIoOutsideMainCheck.h @@ -0,0 +1,49 @@ +//===--- AvoidStdIoOutsideMainCheck.h - clang-tidy --------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_AVOIDSTDIOOUTSIDEMAINCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_AVOIDSTDIOOUTSIDEMAINCHECK_H + +#include "../ClangTidyCheck.h" +#include <string> + +namespace clang { +namespace tidy { +namespace misc { + +/// Diagnoses if a predefined standard stream object (`cin`, `wcin`, +/// `cout`, `wcout`, `cerr` or `wcerr`) is used outside the `main` function. +/// It also flags uses of uses of `cstdio`/`stdio.h` functions like `printf()` +/// outside the `main` function. +/// +/// For the user-facing documentation and examples see: +/// http://clang.llvm.org/extra/clang-tidy/checks/misc-avoid-std-io-outside-main.html +class AvoidStdIoOutsideMainCheck : public ClangTidyCheck { +public: + AvoidStdIoOutsideMainCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } + + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + const std::vector<std::string> StdIOStreams = {"cin", "wcin", "cout", + "wcout", "cerr", "wcerr"}; + const std::vector<std::string> CLikeIOFunctions = { + "printf", "vprintf", "puts", "putchar", "scanf", "getchar", "gets"}; +}; + +} // namespace misc +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_AVOIDSTDIOOUTSIDEMAINCHECK_H Index: clang-tools-extra/clang-tidy/misc/AvoidStdIoOutsideMainCheck.cpp =================================================================== --- /dev/null +++ clang-tools-extra/clang-tidy/misc/AvoidStdIoOutsideMainCheck.cpp @@ -0,0 +1,70 @@ +//===--- AvoidStdIoOutsideMainCheck.cpp - clang-tidy ----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "AvoidStdIoOutsideMainCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace misc { + +void AvoidStdIoOutsideMainCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher( + declRefExpr(to(varDecl(hasAnyName(SmallVector<StringRef>( + StdIOStreams.begin(), StdIOStreams.end())), + isInStdNamespace())), + unless(forFunction(isMain()))) + .bind("StdStreamObject"), + this); + + Finder->addMatcher( + declRefExpr(hasDeclaration(functionDecl(hasAnyName(SmallVector<StringRef>( + CLikeIOFunctions.begin(), CLikeIOFunctions.end())))), + unless(forFunction(isMain()))) + .bind("CLibFunction"), + this); + + /// Matcher for indirect stdio uses: + /// \code + /// auto Print = &puts; + /// Print("This is using stdio"); + /// \endcode + Finder->addMatcher( + declRefExpr( + hasDeclaration(varDecl(hasDescendant(declRefExpr( + hasDeclaration(functionDecl(hasAnyName(SmallVector<StringRef>( + CLikeIOFunctions.begin(), CLikeIOFunctions.end())))))))), + unless(forFunction(isMain()))) + .bind("CLibFunction"), + this); +} + +void AvoidStdIoOutsideMainCheck::check(const MatchFinder::MatchResult &Result) { + + if (const auto *MatchedStreamObj = + Result.Nodes.getNodeAs<DeclRefExpr>("StdStreamObject")) { + diag(MatchedStreamObj->getLocation(), + "predefined standard stream objects should " + "not be used outside the main function"); + return; + } + + if (const auto *MatchedCLibFunc = + Result.Nodes.getNodeAs<DeclRefExpr>("CLibFunction")) { + diag(MatchedCLibFunc->getLocation(), + "cstdio functions should not be used outside the main function"); + return; + } +} + +} // namespace misc +} // namespace tidy +} // namespace clang
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits