Index: clang-tools-extra/trunk/clang-tidy/misc/CMakeLists.txt =================================================================== --- clang-tools-extra/trunk/clang-tidy/misc/CMakeLists.txt +++ clang-tools-extra/trunk/clang-tidy/misc/CMakeLists.txt @@ -17,6 +17,7 @@ NonCopyableObjects.cpp SizeofContainerCheck.cpp StaticAssertCheck.cpp + StringIntegerAssignmentCheck.cpp SwappedArgumentsCheck.cpp ThrowByValueCatchByReferenceCheck.cpp UndelegatedConstructor.cpp Index: clang-tools-extra/trunk/clang-tidy/misc/MiscTidyModule.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/misc/MiscTidyModule.cpp +++ clang-tools-extra/trunk/clang-tidy/misc/MiscTidyModule.cpp @@ -25,6 +25,7 @@ #include "NonCopyableObjects.h" #include "SizeofContainerCheck.h" #include "StaticAssertCheck.h" +#include "StringIntegerAssignmentCheck.h" #include "SwappedArgumentsCheck.h" #include "ThrowByValueCatchByReferenceCheck.h" #include "UndelegatedConstructor.h" @@ -68,6 +69,8 @@ CheckFactories.registerCheck("misc-sizeof-container"); CheckFactories.registerCheck( "misc-static-assert"); + CheckFactories.registerCheck( + "misc-string-integer-assignment"); CheckFactories.registerCheck( "misc-swapped-arguments"); CheckFactories.registerCheck( Index: clang-tools-extra/trunk/clang-tidy/misc/StringIntegerAssignmentCheck.h =================================================================== --- clang-tools-extra/trunk/clang-tidy/misc/StringIntegerAssignmentCheck.h +++ clang-tools-extra/trunk/clang-tidy/misc/StringIntegerAssignmentCheck.h @@ -0,0 +1,34 @@ +//===--- StringIntegerAssignmentCheck.h - clang-tidy-------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_STRING_INTEGER_ASSIGNMENT_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_STRING_INTEGER_ASSIGNMENT_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { + +/// Finds instances where an integer is assigned to a string. +/// +/// For more details see: +/// http://clang.llvm.org/extra/clang-tidy/checks/misc-string-assignment.html +class StringIntegerAssignmentCheck : public ClangTidyCheck { +public: + StringIntegerAssignmentCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_STRING_INTEGER_ASSIGNMENT_H + Index: clang-tools-extra/trunk/clang-tidy/misc/StringIntegerAssignmentCheck.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/misc/StringIntegerAssignmentCheck.cpp +++ clang-tools-extra/trunk/clang-tidy/misc/StringIntegerAssignmentCheck.cpp @@ -0,0 +1,85 @@ +//===--- StringIntegerAssignmentCheck.cpp - clang-tidy---------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "StringIntegerAssignmentCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Lex/Lexer.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { + +void StringIntegerAssignmentCheck::registerMatchers(MatchFinder *Finder) { + if (!getLangOpts().CPlusPlus) + return; + Finder->addMatcher( + cxxOperatorCallExpr( + anyOf(hasOverloadedOperatorName("="), + hasOverloadedOperatorName("+=")), + callee(cxxMethodDecl(ofClass(classTemplateSpecializationDecl( + hasName("::std::basic_string"), + hasTemplateArgument(0, refersToType(qualType().bind("type"))))))), + hasArgument(1, + ignoringImpCasts(expr(hasType(isInteger()), + unless(hasType(isAnyCharacter()))) + .bind("expr"))), + unless(isInTemplateInstantiation())), + this); +} + +void StringIntegerAssignmentCheck::check( + const MatchFinder::MatchResult &Result) { + const auto *Argument = Result.Nodes.getNodeAs("expr"); + SourceLocation Loc = Argument->getLocStart(); + + auto Diag = + diag(Loc, "an integer is interpreted as a character code when assigning " + "it to a string; if this is intended, cast the integer to the " + "appropriate character type; if you want a string " + "representation, use the appropriate conversion facility"); + + if (Loc.isMacroID()) + return; + + auto CharType = *Result.Nodes.getNodeAs("type"); + bool IsWideCharType = CharType->isWideCharType(); + if (!CharType->isCharType() && !IsWideCharType) + return; + bool IsOneDigit = false; + bool IsLiteral = false; + if (const auto *Literal = dyn_cast(Argument)) { + IsOneDigit = Literal->getValue().getLimitedValue() < 10; + IsLiteral = true; + } + + SourceLocation EndLoc = Lexer::getLocForEndOfToken( + Argument->getLocEnd(), 0, *Result.SourceManager, + Result.Context->getLangOpts()); + if (IsOneDigit) { + Diag << FixItHint::CreateInsertion(Loc, IsWideCharType ? "L'" : "'") + << FixItHint::CreateInsertion(EndLoc, "'"); + return; + } + if (IsLiteral) { + Diag << FixItHint::CreateInsertion(Loc, IsWideCharType ? "L\"" : "\"") + << FixItHint::CreateInsertion(EndLoc, "\""); + return; + } + + if (getLangOpts().CPlusPlus11) { + Diag << FixItHint::CreateInsertion(Loc, IsWideCharType ? "std::to_wstring(" + : "std::to_string(") + << FixItHint::CreateInsertion(EndLoc, ")"); + } +} + +} // namespace tidy +} // namespace clang Index: clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst =================================================================== --- clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst +++ clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst @@ -48,6 +48,7 @@ misc-non-copyable-objects misc-sizeof-container misc-static-assert + misc-string-integer-assignment misc-swapped-arguments misc-throw-by-value-catch-by-reference misc-undelegated-constructor Index: clang-tools-extra/trunk/docs/clang-tidy/checks/misc-string-integer-assignment.rst =================================================================== --- clang-tools-extra/trunk/docs/clang-tidy/checks/misc-string-integer-assignment.rst +++ clang-tools-extra/trunk/docs/clang-tidy/checks/misc-string-integer-assignment.rst @@ -0,0 +1,31 @@ +misc-string-integer-assignment +============================== + +The check finds assignments of an integer to ``std::basic_string`` +(``std::string``, ``std::wstring``, etc.). The source of the problem is the +following assignment operator of ``std::basic_string``: + +.. code:: c++ + basic_string& operator=( CharT ch ); + +Numeric types can be implicity casted to character types. + +.. code:: c++ + std::string s; + int x = 5965; + s = 6; + s = x; + +Use the appropriate conversion functions or character literals. + +.. code:: c++ + std::string s; + int x = 5965; + s = '6'; + s = std::to_string(x); + +In order to suppress false positives, use an explicit cast. + +.. code:: c++ + std::string s; + s = static_cast(6); Index: clang-tools-extra/trunk/test/clang-tidy/misc-string-integer-assignment.cpp =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/misc-string-integer-assignment.cpp +++ clang-tools-extra/trunk/test/clang-tidy/misc-string-integer-assignment.cpp @@ -0,0 +1,53 @@ +// RUN: %check_clang_tidy %s misc-string-integer-assignment %t + +namespace std { +template +struct basic_string { + basic_string& operator=(T); + basic_string& operator=(basic_string); + basic_string& operator+=(T); + basic_string& operator+=(basic_string); +}; + +typedef basic_string string; +typedef basic_string wstring; +} + +typedef int MyArcaneChar; + +int main() { + std::string s; + std::wstring ws; + int x = 5; + + s = 6; +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: an integer is interpreted as a character code when assigning {{.*}} [misc-string-integer-assignment] +// CHECK-FIXES: {{^}} s = '6';{{$}} + s = 66; +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: an integer is interpreted as a chara +// CHECK-FIXES: {{^}} s = "66";{{$}} + s = x; +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: an integer is interpreted as a chara +// CHECK-FIXES: {{^}} s = std::to_string(x);{{$}} + s = 'c'; + s = static_cast(6); + +// += + ws += 6; +// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: an integer is interpreted as a chara +// CHECK-FIXES: {{^}} ws += L'6';{{$}} + ws += 66; +// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: an integer is interpreted as a chara +// CHECK-FIXES: {{^}} ws += L"66";{{$}} + ws += x; +// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: an integer is interpreted as a chara +// CHECK-FIXES: {{^}} ws += std::to_wstring(x);{{$}} + ws += L'c'; + ws += (wchar_t)6; + + std::basic_string as; + as = 6; +// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: an integer is interpreted as a chara +// CHECK-FIXES: {{^}} as = 6;{{$}} + +}