clang-tools  4.0.0
SwappedArgumentsCheck.cpp
Go to the documentation of this file.
1 //===--- SwappedArgumentsCheck.cpp - clang-tidy ---------------------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "SwappedArgumentsCheck.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/Lex/Lexer.h"
13 #include "clang/Tooling/FixIt.h"
14 #include "llvm/ADT/SmallPtrSet.h"
15 
16 using namespace clang::ast_matchers;
17 
18 namespace clang {
19 namespace tidy {
20 namespace misc {
21 
22 void SwappedArgumentsCheck::registerMatchers(MatchFinder *Finder) {
23  Finder->addMatcher(callExpr().bind("call"), this);
24 }
25 
26 /// \brief Look through lvalue to rvalue and nop casts. This filters out
27 /// implicit conversions that have no effect on the input but block our view for
28 /// other implicit casts.
29 static const Expr *ignoreNoOpCasts(const Expr *E) {
30  if (auto *Cast = dyn_cast<CastExpr>(E))
31  if (Cast->getCastKind() == CK_LValueToRValue ||
32  Cast->getCastKind() == CK_NoOp)
33  return ignoreNoOpCasts(Cast->getSubExpr());
34  return E;
35 }
36 
37 /// \brief Restrict the warning to implicit casts that are most likely
38 /// accidental. User defined or integral conversions fit in this category,
39 /// lvalue to rvalue or derived to base does not.
40 static bool isImplicitCastCandidate(const CastExpr *Cast) {
41  return Cast->getCastKind() == CK_UserDefinedConversion ||
42  Cast->getCastKind() == CK_FloatingToBoolean ||
43  Cast->getCastKind() == CK_FloatingToIntegral ||
44  Cast->getCastKind() == CK_IntegralToBoolean ||
45  Cast->getCastKind() == CK_IntegralToFloating ||
46  Cast->getCastKind() == CK_MemberPointerToBoolean ||
47  Cast->getCastKind() == CK_PointerToBoolean;
48 }
49 
50 void SwappedArgumentsCheck::check(const MatchFinder::MatchResult &Result) {
51  const ASTContext &Ctx = *Result.Context;
52  const auto *Call = Result.Nodes.getNodeAs<CallExpr>("call");
53 
54  llvm::SmallPtrSet<const Expr *, 4> UsedArgs;
55  for (unsigned I = 1, E = Call->getNumArgs(); I < E; ++I) {
56  const Expr *LHS = Call->getArg(I - 1);
57  const Expr *RHS = Call->getArg(I);
58 
59  // Only need to check RHS, as LHS has already been covered. We don't want to
60  // emit two warnings for a single argument.
61  if (UsedArgs.count(RHS))
62  continue;
63 
64  const auto *LHSCast = dyn_cast<ImplicitCastExpr>(ignoreNoOpCasts(LHS));
65  const auto *RHSCast = dyn_cast<ImplicitCastExpr>(ignoreNoOpCasts(RHS));
66 
67  // Look if this is a potentially swapped argument pair. First look for
68  // implicit casts.
69  if (!LHSCast || !RHSCast || !isImplicitCastCandidate(LHSCast) ||
70  !isImplicitCastCandidate(RHSCast))
71  continue;
72 
73  // If the types that go into the implicit casts match the types of the other
74  // argument in the declaration there is a high probability that the
75  // arguments were swapped.
76  // TODO: We could make use of the edit distance between the argument name
77  // and the name of the passed variable in addition to this type based
78  // heuristic.
79  const Expr *LHSFrom = ignoreNoOpCasts(LHSCast->getSubExpr());
80  const Expr *RHSFrom = ignoreNoOpCasts(RHSCast->getSubExpr());
81  if (LHS->getType() == RHS->getType() ||
82  LHS->getType() != RHSFrom->getType() ||
83  RHS->getType() != LHSFrom->getType())
84  continue;
85 
86  // Emit a warning and fix-its that swap the arguments.
87  diag(Call->getLocStart(), "argument with implicit conversion from %0 "
88  "to %1 followed by argument converted from "
89  "%2 to %3, potentially swapped arguments.")
90  << LHS->getType() << LHSFrom->getType() << RHS->getType()
91  << RHSFrom->getType()
92  << tooling::fixit::createReplacement(*LHS, *RHS, Ctx)
93  << tooling::fixit::createReplacement(*RHS, *LHS, Ctx);
94 
95  // Remember that we emitted a warning for this argument.
96  UsedArgs.insert(RHSCast);
97  }
98 }
99 
100 } // namespace misc
101 } // namespace tidy
102 } // namespace clang
static const Expr * ignoreNoOpCasts(const Expr *E)
Look through lvalue to rvalue and nop casts.
std::unique_ptr< ast_matchers::MatchFinder > Finder
Definition: ClangTidy.cpp:262
static bool isImplicitCastCandidate(const CastExpr *Cast)
Restrict the warning to implicit casts that are most likely accidental.
const NamedDecl * Result
Definition: USRFinder.cpp:162