Skip to content

Commit

Permalink
[clang-tidy] Check for suspicious string assignments.
Browse files Browse the repository at this point in the history
It is possible to assign arbitrary integer types to strings.
Sometimes it is the result of missing to_string call or apostrophes.

Reviewers: alexfh

Differential Revision: http://reviews.llvm.org/D15411

llvm-svn: 255630
  • Loading branch information
Xazax-hun committed Dec 15, 2015
1 parent 6015f5c commit 454564a
Show file tree
Hide file tree
Showing 7 changed files with 208 additions and 0 deletions.
1 change: 1 addition & 0 deletions clang-tools-extra/clang-tidy/misc/CMakeLists.txt
Expand Up @@ -17,6 +17,7 @@ add_clang_library(clangTidyMiscModule
NonCopyableObjects.cpp
SizeofContainerCheck.cpp
StaticAssertCheck.cpp
StringIntegerAssignmentCheck.cpp
SwappedArgumentsCheck.cpp
ThrowByValueCatchByReferenceCheck.cpp
UndelegatedConstructor.cpp
Expand Down
3 changes: 3 additions & 0 deletions clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp
Expand Up @@ -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"
Expand Down Expand Up @@ -68,6 +69,8 @@ class MiscModule : public ClangTidyModule {
CheckFactories.registerCheck<SizeofContainerCheck>("misc-sizeof-container");
CheckFactories.registerCheck<StaticAssertCheck>(
"misc-static-assert");
CheckFactories.registerCheck<StringIntegerAssignmentCheck>(
"misc-string-integer-assignment");
CheckFactories.registerCheck<SwappedArgumentsCheck>(
"misc-swapped-arguments");
CheckFactories.registerCheck<ThrowByValueCatchByReferenceCheck>(
Expand Down
85 changes: 85 additions & 0 deletions clang-tools-extra/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>("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<QualType>("type");
bool IsWideCharType = CharType->isWideCharType();
if (!CharType->isCharType() && !IsWideCharType)
return;
bool IsOneDigit = false;
bool IsLiteral = false;
if (const auto *Literal = dyn_cast<IntegerLiteral>(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
34 changes: 34 additions & 0 deletions clang-tools-extra/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

1 change: 1 addition & 0 deletions clang-tools-extra/docs/clang-tidy/checks/list.rst
Expand Up @@ -48,6 +48,7 @@ List of clang-tidy Checks
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
Expand Down
@@ -0,0 +1,31 @@
misc-string-integer-assignment
==============================

The check finds assignments of an integer to ``std::basic_string<CharT>``
(``std::string``, ``std::wstring``, etc.). The source of the problem is the
following assignment operator of ``std::basic_string<CharT>``:

.. 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<char>(6);
@@ -0,0 +1,53 @@
// RUN: %check_clang_tidy %s misc-string-integer-assignment %t

namespace std {
template<typename T>
struct basic_string {
basic_string& operator=(T);
basic_string& operator=(basic_string);
basic_string& operator+=(T);
basic_string& operator+=(basic_string);
};

typedef basic_string<char> string;
typedef basic_string<wchar_t> 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<char>(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<MyArcaneChar> as;
as = 6;
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: an integer is interpreted as a chara
// CHECK-FIXES: {{^}} as = 6;{{$}}

}

0 comments on commit 454564a

Please sign in to comment.