diff --git a/.clang-tidy b/.clang-tidy index 8fa05aa4dae96..4c42ea906b772 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -6,6 +6,7 @@ # fix their errors or recategorise them as checks we don't care about. Checks: "\ bugprone-*,\ +cata-*,\ cert-*,\ -cert-err58-cpp,\ clang-diagnostic-*,\ diff --git a/.travis.yml b/.travis.yml index 5e0562eaff1c6..1aaca516a5538 100644 --- a/.travis.yml +++ b/.travis.yml @@ -105,12 +105,12 @@ jobs: osx_image: xcode10.1 compiler: clang - - env: CLANG=clang++-8 TILES=1 SOUND=1 CXXFLAGS=-Wno-unused-command-line-argument CMAKE=1 CATA_CLANG_TIDY=clang-tidy-8 + - env: CLANG=clang++-8 TILES=1 SOUND=1 CXXFLAGS=-Wno-unused-command-line-argument CMAKE=1 CATA_CLANG_TIDY=plugin name: "Clang-tidy CMake build with Tiles and Sound" compiler: clang addons: &clang8 apt: - packages: ["clang-8", "clang-tidy-8", "libsdl2-dev", "libsdl2-ttf-dev", "libsdl2-image-dev", "libsdl2-mixer-dev"] + packages: ["clang-8", "libclang-8-dev", "llvm-8-dev", "llvm-8-tools", "libsdl2-dev", "libsdl2-ttf-dev", "libsdl2-image-dev", "libsdl2-mixer-dev"] sources: [*apt_sources, llvm-toolchain-xenial-8] # Finally check the compiler variants diff --git a/CMakeLists.txt b/CMakeLists.txt index bddbe88bf006c..24d62f1df2b4f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,6 +8,8 @@ SET(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/CMakeModules ) +SET(CMAKE_TLS_VERIFY ON) + # Build options option(TILES "Build graphical tileset version." "OFF") option(CURSES "Build curses version." "ON" ) @@ -17,8 +19,11 @@ option(USE_HOME_DIR "Use user's home directory for save files." "ON" ) option(LOCALIZE "Support for language localizations. Also enable UTF support." "ON" ) option(LANGUAGES "Compile localization files for specified languages." "" ) option(DYNAMIC_LINKING "Use dynamic linking. Or use static to remove MinGW dependency instead." "ON") -option(GIT_BINARY "Git binary name or path." "") -OPTION(PREFIX "Location of Data & GFX directories" "") +option(CATA_CLANG_TIDY_PLUGIN "Build Cata's custom clang-tidy plugin" "OFF") +set(CATA_CLANG_TIDY_INCLUDE_DIR "" CACHE STRING "Path to internal clang-tidy headers required for plugin (e.g. ClangTidy.h)") +set(CATA_CHECK_CLANG_TIDY "" CACHE STRING "Path to check_clang_tidy.py for plugin tests") +set(GIT_BINARY "" CACHE STRING "Git binary name or path.") +set(PREFIX "" CACHE STRING "Location of Data & GFX directories") include(CTest) @@ -341,6 +346,9 @@ if (NOT MSVC) add_subdirectory(src/chkjson) endif() add_subdirectory(tests) +if (CATA_CLANG_TIDY_PLUGIN) + add_subdirectory(tools/clang-tidy-plugin) +endif() CONFIGURE_FILE( "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" diff --git a/Makefile b/Makefile index a2b335ea34ade..55607bf03a2ee 100644 --- a/Makefile +++ b/Makefile @@ -685,6 +685,8 @@ TESTSRC := $(wildcard tests/*.cpp) TESTHDR := $(wildcard tests/*.h) JSON_FORMATTER_SOURCES := tools/format/format.cpp src/json.cpp CHKJSON_SOURCES := src/chkjson/chkjson.cpp src/json.cpp +CLANG_TIDY_PLUGIN_SOURCES := \ + $(wildcard tools/clang-tidy-plugin/*.cpp tools/clang-tidy-plugin/*/*.cpp) TOOLHDR := $(wildcard tools/*/*.h) # Using sort here because it has the side-effect of deduplicating the list ASTYLE_SOURCES := $(sort \ @@ -694,6 +696,7 @@ ASTYLE_SOURCES := $(sort \ $(TESTHDR) \ $(JSON_FORMATTER_SOURCES) \ $(CHKJSON_SOURCES) \ + $(CLANG_TIDY_PLUGIN_SOURCES) \ $(TOOLHDR)) _OBJS = $(SOURCES:$(SRC_DIR)/%.cpp=%.o) diff --git a/build-scripts/build.sh b/build-scripts/build.sh index cc8da37a77a55..0bae0c3924196 100755 --- a/build-scripts/build.sh +++ b/build-scripts/build.sh @@ -41,6 +41,17 @@ then build_type=Debug fi + cmake_extra_opts= + + if [ "$CATA_CLANG_TIDY" = "plugin" ] + then + cmake_extra_opts="$cmake_extra_opts -DCATA_CLANG_TIDY_PLUGIN=ON" + # Need to specify the particular LLVM / Clang versions to use, lest it + # use the llvm-7 that comes by default on the Travis Xenial image. + cmake_extra_opts="$cmake_extra_opts -DLLVM_DIR=/usr/lib/llvm-8/lib/cmake/llvm" + cmake_extra_opts="$cmake_extra_opts -DClang_DIR=/usr/lib/llvm-8/lib/cmake/clang" + fi + mkdir build cd build cmake \ @@ -49,9 +60,25 @@ then -DCMAKE_BUILD_TYPE="$build_type" \ -DTILES=${TILES:-0} \ -DSOUND=${SOUND:-0} \ + $cmake_extra_opts \ .. if [ -n "$CATA_CLANG_TIDY" ] then + if [ "$CATA_CLANG_TIDY" = "plugin" ] + then + make -j$num_jobs CataAnalyzerPlugin + export PATH=$PWD/tools/clang-tidy-plugin/clang-tidy-plugin-support/bin:$PATH + if ! which FileCheck + then + ls -l tools/clang-tidy-plugin/clang-tidy-plugin-support/bin + ls -l /usr/bin + echo "Missing FileCheck" + exit 1 + fi + CATA_CLANG_TIDY=clang-tidy + lit -v tools/clang-tidy-plugin/test + fi + "$CATA_CLANG_TIDY" --version # Run clang-tidy analysis instead of regular build & test @@ -95,7 +122,7 @@ then analyze_files_in_random_order "$remaining_cpp_files" else # Regular build - make -j3 + make -j$num_jobs cd .. # Run regular tests [ -f "${bin_path}cata_test" ] && run_tests "${bin_path}cata_test" diff --git a/build-scripts/clang-tidy-wrapper.sh b/build-scripts/clang-tidy-wrapper.sh index 187248f004bb0..8ca192cedae43 100755 --- a/build-scripts/clang-tidy-wrapper.sh +++ b/build-scripts/clang-tidy-wrapper.sh @@ -15,5 +15,12 @@ then exit 0 fi +plugin=build/tools/clang-tidy-plugin/libCataAnalyzerPlugin.so + set -x -"$CATA_CLANG_TIDY" "$@" +if [ -f "$plugin" ] +then + LD_PRELOAD=$plugin "$CATA_CLANG_TIDY" "$@" +else + "$CATA_CLANG_TIDY" "$@" +fi diff --git a/build-scripts/requirements.sh b/build-scripts/requirements.sh index bfabcca6c602e..327463af1854a 100644 --- a/build-scripts/requirements.sh +++ b/build-scripts/requirements.sh @@ -30,7 +30,7 @@ if [ -n "${CODE_COVERAGE}" ]; then fi if [ -n "$CATA_CLANG_TIDY" ]; then - travis_retry pip install --user compiledb + travis_retry pip install --user compiledb lit fi # Influenced by https://github.com/zer0main/battleship/blob/master/build/windows/requirements.sh diff --git a/doc/DEVELOPER_TOOLING.md b/doc/DEVELOPER_TOOLING.md index da14ff0b69237..64d5e39087194 100644 --- a/doc/DEVELOPER_TOOLING.md +++ b/doc/DEVELOPER_TOOLING.md @@ -9,6 +9,56 @@ On Windows, there is an astyle extension for Visual Studio. See the [JSON style guide](JSON_STYLE.md). +## clang-tidy + +Cataclysm has a [clang-tidy configuration file](../.clang-tidy) and if you have +`clang-tidy` available you can run it to perform static analysis of the +codebase. We test with `clang-tidy` from LLVM 8.0.1 on Travis, so for the most +consistent results, you might want to use that version. + +### Custom clang-tidy plugin + +We have written our own clang-tidy checks in a custom plugin. Unfortunately, +`clang-tidy` as distributed by LLVM doesn't support plugins, so making this +work requires some extra steps. + +If you are on Ubuntu Xenial then you might be able to get it working the same +way Travis does. Add the LLVM 8 Xenial source [listed +here](https://apt.llvm.org/) to your `sources.list`, install the `clang-8 +libclang-8-dev llvm-8-dev llvm-8-tools` packages and build Cataclysm with CMake +adding `-DCATA_CLANG_TIDY_PLUGIN=ON`. + +On other distributions you will probably need to build `clang-tidy` yourself. +* Check out the `llvm`, `clang`, and `clang-tools-extra` repositories in the + required layout (as described for example + [here](https://quuxplusone.github.io/blog/2018/04/16/building-llvm-from-source/). +* Patch in plugin support for `clang-tidy` using [this + patch](https://github.com/jbytheway/clang-tidy-plugin-support/blob/master/plugin-support.patch). +* Configure LLVM using CMake, including the + `-DCMAKE_EXE_LINKER_FLAGS="-rdynamic"` option. +* Add the `build/bin` directory to your path so that `clang-tidy` and + `FileCheck` are found from there. + +Then you can use your locally build `clang-tidy` to compile Cataclysm. You'll +need to use the CMake version of the Cataclysm build rather than the `Makefile` +build. Add the following CMake options: +```sh +-DCATA_CLANG_TIDY_PLUGIN=ON +-DCATA_CLANG_TIDY_INCLUDE_DIR="$extra_dir/clang-tidy" +-DCATA_CHECK_CLANG_TIDY="$extra_dir/test/clang-tidy/check_clang_tidy.py" +``` +where `$extra_dir` is the location of your `clang-tools-extra` checkout. + +If you wish to run the tests for the custom clang-tidy plugin you will also +need `lit`. This will be built as part of `llvm`, or you can install it via +`pip` or your local package manager if you prefer. + +Then, assuming `build` is your Cataclysm build directory, you can run the tests +with +```sh +lit -v build/tools/clang-tidy-plugin/test +``` + ## include-what-you-use [include-what-you-use](https://github.com/include-what-you-use/include-what-you-use) diff --git a/tests/colony_test.cpp b/tests/colony_test.cpp index 0d0bc9d698498..f3c8eb5f17220 100644 --- a/tests/colony_test.cpp +++ b/tests/colony_test.cpp @@ -328,7 +328,7 @@ TEST_CASE( "insert and erase", "[colony]" ) } while( count < 15000 ); // Erase randomly till half-empty - CHECK( test_colony.size() == static_cast( 30000 - count ) ); + CHECK( test_colony.size() == static_cast( 30000 - count ) ); for( int i = 0; i < count; ++i ) { test_colony.insert( 1 ); @@ -497,7 +497,7 @@ TEST_CASE( "insert and erase", "[colony]" ) } // Multiple sequential small insert/erase commands - CHECK( test_colony.size() == static_cast( count ) ); + CHECK( test_colony.size() == static_cast( count ) ); } TEST_CASE( "range erase", "[colony]" ) diff --git a/tools/clang-tidy-plugin/CMakeLists.txt b/tools/clang-tidy-plugin/CMakeLists.txt new file mode 100644 index 0000000000000..30dab7b30abce --- /dev/null +++ b/tools/clang-tidy-plugin/CMakeLists.txt @@ -0,0 +1,54 @@ +include(ExternalProject) + +find_package(LLVM REQUIRED CONFIG) +find_package(Clang REQUIRED CONFIG) + +add_library( + CataAnalyzerPlugin MODULE + CataTidyModule.cpp NoLongCheck.cpp) + +target_include_directories( + CataAnalyzerPlugin SYSTEM PRIVATE + ${LLVM_INCLUDE_DIRS} ${CLANG_INCLUDE_DIRS}) + +if ("${CATA_CLANG_TIDY_INCLUDE_DIR}" STREQUAL "") + SET(ctps_releases + https://github.com/jbytheway/clang-tidy-plugin-support/releases/download) + SET(ctps_version llvm-8.0.1-r12) + SET(ctps_src + ${CMAKE_CURRENT_BINARY_DIR}/clang-tidy-plugin-support) + + ExternalProject_Add( + clang-tidy-plugin-support + URL ${ctps_releases}/${ctps_version}/clang-tidy-plugin-support-${ctps_version}.tar.xz + URL_HASH SHA256=00ffab0df11250f394830735514c62ae787bd2eb6eb9d5e97471206d270c54e2 + SOURCE_DIR ${ctps_src} + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + TEST_COMMAND "" + ) + + add_dependencies(CataAnalyzerPlugin clang-tidy-plugin-support) + target_include_directories( + CataAnalyzerPlugin SYSTEM PRIVATE ${ctps_src}/include) +else() + target_include_directories( + CataAnalyzerPlugin SYSTEM PRIVATE ${CATA_CLANG_TIDY_INCLUDE_DIR}) +endif() + +target_compile_definitions( + CataAnalyzerPlugin PRIVATE ${LLVM_DEFINITIONS}) + +# We need to turn off exceptions and RTTI to match the LLVM build. +# I feel there ought to be a way to extract these flags from the +# LLVMConfig.cmake as we have done for e.g. LLVM_INCLUDE_DIRS above, but I +# haven't found one. +if(MSVC) +else() + target_compile_options( + CataAnalyzerPlugin PRIVATE -fno-exceptions -fno-rtti) +endif() + +configure_file(test/lit.site.cfg.in test/lit.site.cfg @ONLY) +configure_file(test/.clang-tidy test/.clang-tidy COPYONLY) diff --git a/tools/clang-tidy-plugin/CataTidyModule.cpp b/tools/clang-tidy-plugin/CataTidyModule.cpp new file mode 100644 index 0000000000000..34e78038051c0 --- /dev/null +++ b/tools/clang-tidy-plugin/CataTidyModule.cpp @@ -0,0 +1,28 @@ +#include "ClangTidy.h" +#include "ClangTidyModule.h" +#include "ClangTidyModuleRegistry.h" +#include "NoLongCheck.h" + +namespace clang +{ +namespace tidy +{ +namespace cata +{ + +class CataModule : public ClangTidyModule +{ + public: + void addCheckFactories( ClangTidyCheckFactories &CheckFactories ) override { + CheckFactories.registerCheck( "cata-no-long" ); + } +}; + +} // namespace cata + +// Register the MiscTidyModule using this statically initialized variable. +static ClangTidyModuleRegistry::Add +X( "cata-module", "Adds Cataclysm-DDA checks." ); + +} // namespace tidy +} // namespace clang diff --git a/tools/clang-tidy-plugin/NoLongCheck.cpp b/tools/clang-tidy-plugin/NoLongCheck.cpp new file mode 100644 index 0000000000000..2113f98a57bdd --- /dev/null +++ b/tools/clang-tidy-plugin/NoLongCheck.cpp @@ -0,0 +1,157 @@ +#include "NoLongCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Frontend/CompilerInstance.h" + +using namespace clang::ast_matchers; + +namespace clang +{ +namespace tidy +{ +namespace cata +{ + +class NoLongMacrosCallbacks : public PPCallbacks +{ + public: + NoLongMacrosCallbacks( NoLongCheck *Check ) : + Check( Check ) {} + + void MacroExpands( const Token &MacroNameTok, + const MacroDefinition &, + SourceRange Range, + const MacroArgs * ) override { + StringRef MacroName = MacroNameTok.getIdentifierInfo()->getName(); + if( MacroName == "LONG_MIN" || MacroName == "LONG_MAX" || MacroName == "ULONG_MAX" ) { + Check->diag( Range.getBegin(), "Use of long-specific macro %0" ) << MacroName; + } + } + private: + NoLongCheck *Check; +}; + +void NoLongCheck::registerPPCallbacks( CompilerInstance &Compiler ) +{ + Compiler.getPreprocessor().addPPCallbacks( + llvm::make_unique( this ) ); +} + +void NoLongCheck::registerMatchers( MatchFinder *Finder ) +{ + using TypeMatcher = clang::ast_matchers::internal::Matcher; + const TypeMatcher isIntegerOrRef = + qualType( anyOf( isInteger(), references( isInteger() ) ), + unless( autoType() ), unless( references( autoType() ) ) ); + Finder->addMatcher( valueDecl( hasType( isIntegerOrRef ) ).bind( "decl" ), this ); + Finder->addMatcher( functionDecl( returns( isIntegerOrRef ) ).bind( "return" ), this ); + Finder->addMatcher( cxxStaticCastExpr( hasDestinationType( isIntegerOrRef ) ).bind( "cast" ), + this ); +} + +static std::string AlternativesFor( QualType Type ) +{ + Type = Type.getNonReferenceType(); + Type = Type.getLocalUnqualifiedType(); + std::string name = Type.getAsString(); + if( name == "long" ) { + return "Prefer int or int64_t to long"; + } else if( name == "unsigned long" ) { + return "Prefer unsigned int, size_t, or uint64_t to unsigned long"; + } else { + return {}; + } +} + +static void CheckDecl( NoLongCheck &Check, const MatchFinder::MatchResult &Result ) +{ + const ValueDecl *MatchedDecl = Result.Nodes.getNodeAs( "decl" ); + if( !MatchedDecl || !MatchedDecl->getLocation().isValid() ) { + return; + } + QualType Type = MatchedDecl->getType(); + std::string alternatives = AlternativesFor( Type ); + if( alternatives.empty() ) { + return; + } + if( MatchedDecl->getName().startswith( "__" ) ) { + // Can happen for e.g. compiler-generated code inside an implicitly + // generated function + return; + } + Decl::Kind contextKind = MatchedDecl->getDeclContext()->getDeclKind(); + if( contextKind == Decl::Function || contextKind == Decl::CXXMethod || + contextKind == Decl::CXXConstructor || contextKind == Decl::CXXConversion || + contextKind == Decl::CXXDestructor || contextKind == Decl::CXXDeductionGuide ) { + TemplateSpecializationKind tsk = + static_cast( + MatchedDecl->getDeclContext() )->getTemplateSpecializationKind(); + if( tsk == TSK_ImplicitInstantiation ) { + // This happens for e.g. a parameter 'T a' to an instantiated + // template function where T is long. We don't want to report such + // cases. + return; + } + } + Check.diag( + MatchedDecl->getLocation(), "Variable %0 declared as %1. %2." ) << + MatchedDecl << Type << alternatives; +} + +static void CheckReturn( NoLongCheck &Check, const MatchFinder::MatchResult &Result ) +{ + const FunctionDecl *MatchedDecl = Result.Nodes.getNodeAs( "return" ); + if( !MatchedDecl || !MatchedDecl->getLocation().isValid() ) { + return; + } + QualType Type = MatchedDecl->getDeclaredReturnType(); + std::string alternatives = AlternativesFor( Type ); + if( alternatives.empty() ) { + return; + } + if( MatchedDecl->isTemplateInstantiation() ) { + return; + } + + Decl::Kind contextKind = MatchedDecl->getDeclContext()->getDeclKind(); + if( contextKind == Decl::ClassTemplateSpecialization ) { + TemplateSpecializationKind tsk = + static_cast( + MatchedDecl->getDeclContext() )->getTemplateSpecializationKind(); + if( tsk == TSK_ImplicitInstantiation ) { + // This happens for e.g. a parameter 'T a' to an instantiated + // template function where T is long. We don't want to report such + // cases. + return; + } + } + Check.diag( + MatchedDecl->getLocation(), "Function %0 declared as returning %1. %2." ) << + MatchedDecl << Type << alternatives; +} + +static void CheckCast( NoLongCheck &Check, const MatchFinder::MatchResult &Result ) +{ + const CXXStaticCastExpr *MatchedDecl = Result.Nodes.getNodeAs( "cast" ); + if( !MatchedDecl ) { + return; + } + QualType Type = MatchedDecl->getType(); + std::string alternatives = AlternativesFor( Type ); + if( alternatives.empty() ) { + return; + } + SourceLocation location = MatchedDecl->getTypeInfoAsWritten()->getTypeLoc().getBeginLoc(); + Check.diag( location, "Static cast to %0. %1." ) << Type << alternatives; +} + +void NoLongCheck::check( const MatchFinder::MatchResult &Result ) +{ + CheckDecl( *this, Result ); + CheckReturn( *this, Result ); + CheckCast( *this, Result ); +} + +} // namespace cata +} // namespace tidy +} // namespace clang diff --git a/tools/clang-tidy-plugin/NoLongCheck.h b/tools/clang-tidy-plugin/NoLongCheck.h new file mode 100644 index 0000000000000..e6c6c489f2ae4 --- /dev/null +++ b/tools/clang-tidy-plugin/NoLongCheck.h @@ -0,0 +1,27 @@ +#ifndef CATA_TOOLS_CLANG_TIDY_NOLONGCHECK_H +#define CATA_TOOLS_CLANG_TIDY_NOLONGCHECK_H + +#include "ClangTidy.h" + +namespace clang +{ +namespace tidy +{ +namespace cata +{ + +class NoLongCheck : public ClangTidyCheck +{ + public: + NoLongCheck( StringRef Name, ClangTidyContext *Context ) + : ClangTidyCheck( Name, Context ) {} + void registerPPCallbacks( CompilerInstance &Compiler ) override; + void registerMatchers( ast_matchers::MatchFinder *Finder ) override; + void check( const ast_matchers::MatchFinder::MatchResult &Result ) override; +}; + +} // namespace cata +} // namespace tidy +} // namespace clang + +#endif // CATA_TOOLS_CLANG_TIDY_NOLONGCHECK_H diff --git a/tools/clang-tidy-plugin/test/.clang-tidy b/tools/clang-tidy-plugin/test/.clang-tidy new file mode 100644 index 0000000000000..6179a0c5c0836 --- /dev/null +++ b/tools/clang-tidy-plugin/test/.clang-tidy @@ -0,0 +1 @@ +WarningsAsErrors: '-*' diff --git a/tools/clang-tidy-plugin/test/lit.cfg b/tools/clang-tidy-plugin/test/lit.cfg new file mode 100644 index 0000000000000..bf660481acf60 --- /dev/null +++ b/tools/clang-tidy-plugin/test/lit.cfg @@ -0,0 +1,24 @@ +import sys +import os +import lit.formats + +config.name = 'clang-tidy-cata' +config.test_format = lit.formats.ShTest(True) +config.test_source_root = os.path.dirname(__file__) +config.test_exec_root = os.path.join( + config.plugin_build_root, 'test') + +config.suffixes = ['.cpp'] + +if config.cata_check_clang_tidy: + check_clang_tidy = config.cata_check_clang_tidy +else: + check_clang_tidy = os.path.join( + config.plugin_build_root, 'clang-tidy-plugin-support', 'bin', + 'check_clang_tidy.py') + +cata_plugin = os.path.join( + config.plugin_build_root, 'libCataAnalyzerPlugin.so') + +config.substitutions.append(('%check_clang_tidy', check_clang_tidy)) +config.substitutions.append(('%cata_plugin', cata_plugin)) diff --git a/tools/clang-tidy-plugin/test/lit.site.cfg.in b/tools/clang-tidy-plugin/test/lit.site.cfg.in new file mode 100644 index 0000000000000..aae4d01870d78 --- /dev/null +++ b/tools/clang-tidy-plugin/test/lit.site.cfg.in @@ -0,0 +1,7 @@ +import os + +config.plugin_build_root = "@CMAKE_CURRENT_BINARY_DIR@" +config.cata_check_clang_tidy = "@CATA_CHECK_CLANG_TIDY@" + +lit_config.load_config( + config, os.path.join("@CMAKE_CURRENT_SOURCE_DIR@", "test", "lit.cfg")) diff --git a/tools/clang-tidy-plugin/test/no-long.cpp b/tools/clang-tidy-plugin/test/no-long.cpp new file mode 100644 index 0000000000000..9ea4b1b0c9643 --- /dev/null +++ b/tools/clang-tidy-plugin/test/no-long.cpp @@ -0,0 +1,102 @@ +// RUN: %check_clang_tidy %s cata-no-long %t -- -plugins=%cata_plugin -- + +#include +#include + +long i1; +// CHECK-MESSAGES: warning: Variable 'i1' declared as 'long'. Prefer int or int64_t to long. [cata-no-long] + +unsigned long i2; +// CHECK-MESSAGES: warning: Variable 'i2' declared as 'unsigned long'. Prefer unsigned int, size_t, or uint64_t to unsigned long. [cata-no-long] + +const long i3 = 0; +// CHECK-MESSAGES: warning: Variable 'i3' declared as 'const long'. Prefer int or int64_t to long. [cata-no-long] + +long &i4 = i1; +// CHECK-MESSAGES: warning: Variable 'i4' declared as 'long &'. Prefer int or int64_t to long. [cata-no-long] + +long &&i5 = 0L; +// CHECK-MESSAGES: warning: Variable 'i5' declared as 'long &&'. Prefer int or int64_t to long. [cata-no-long] + +int64_t i6; +uint64_t i7; + +auto i8 = int64_t {}; +auto &i9 = i1; +const auto &i10 = i1; +//auto&& i11 = i1; // Shouldn't cause a warning but I can't fix it + +void f1( long e ); +// CHECK-MESSAGES: warning: Variable 'e' declared as 'long'. Prefer int or int64_t to long. [cata-no-long] + +long f2(); +// CHECK-MESSAGES: warning: Function 'f2' declared as returning 'long'. Prefer int or int64_t to long. [cata-no-long] + +int64_t f3(); +auto f4() -> decltype( 0L ); + +int c0 = static_cast( 0 ); +// CHECK-MESSAGES: warning: Static cast to 'long'. Prefer int or int64_t to long. [cata-no-long] +int c1 = static_cast( 0 ); + +template +T g0( T gp0, long gp1 = 0 ) +{ + // CHECK-MESSAGES: warning: Variable 'gp1' declared as 'long'. Prefer int or int64_t to long. [cata-no-long] + long gi0; + // CHECK-MESSAGES: warning: Variable 'gi0' declared as 'long'. Prefer int or int64_t to long. [cata-no-long] + T gi1; +} + +void h() +{ + g0( 0, 0 ); + // Would like to report an error here for the template argument, but have + // not found a way to do so. + + g0( LONG_MIN ); + // CHECK-MESSAGES: warning: Use of long-specific macro LONG_MIN [cata-no-long] + g0( LONG_MAX ); + // CHECK-MESSAGES: warning: Use of long-specific macro LONG_MAX [cata-no-long] + g0( ULONG_MAX ); + // CHECK-MESSAGES: warning: Use of long-specific macro ULONG_MAX [cata-no-long] +} + +template +struct A { + A(); + A( const A & ); + A( A && ); + A &operator=( const A & ); + A &operator=( A && ); + T Af0(); + long Af1(); + // CHECK-MESSAGES: warning: Function 'Af1' declared as returning 'long'. Prefer int or int64_t to long. [cata-no-long] + T Af2( long Af2i ); + // CHECK-MESSAGES: warning: Variable 'Af2i' declared as 'long'. Prefer int or int64_t to long. [cata-no-long] +}; + +A a; + +auto l0 = []( int64_t a ) +{ + return a; +}; + +template +struct B { + A BA[size][size]; +}; + +void Bf() +{ + B<12> b0; + B<12> b1; + // This exercises an obscure corner case where a defaulted operator= will + // cause the compiler to generate code involving an unsigned long variable. + b1 = static_cast < B<12> && >( b0 ); +} + +template +long g1( T g1p0 ); +// CHECK-MESSAGES: warning: Function 'g1' declared as returning 'long'. Prefer int or int64_t to long. [cata-no-long]