clang-tools 6.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
clang::ast_matchers
Definition: ClangTidyDiagnosticConsumer.h:27
clang::tidy::misc::ignoreNoOpCasts
static const Expr * ignoreNoOpCasts(const Expr *E)
Look through lvalue to rvalue and nop casts.
Definition: SwappedArgumentsCheck.cpp:29
SwappedArgumentsCheck.h
clang::tidy::misc::isImplicitCastCandidate
static bool isImplicitCastCandidate(const CastExpr *Cast)
Restrict the warning to implicit casts that are most likely accidental.
Definition: SwappedArgumentsCheck.cpp:40
clang
Definition: AndroidTidyModule.cpp:28