10#include "clang/AST/ASTContext.h"
11#include "clang/AST/FormatString.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13#include "llvm/ADT/StringSwitch.h"
25 callee(functionDecl(anyOf(
26 functionDecl(hasAnyName(
"::atoi",
"::atof",
"::atol",
"::atoll"))
28 functionDecl(hasAnyName(
"::scanf",
"::sscanf",
"::fscanf",
29 "::vfscanf",
"::vscanf",
"::vsscanf"))
30 .bind(
"formatted")))))
36enum class ConversionKind {
49ConversionKind classifyConversionFunc(
const FunctionDecl *FD) {
50 return llvm::StringSwitch<ConversionKind>(FD->getName())
51 .Cases(
"atoi",
"atol", ConversionKind::ToInt)
52 .Case(
"atoll", ConversionKind::ToLongInt)
53 .Case(
"atof", ConversionKind::ToDouble)
54 .Default(ConversionKind::None);
57ConversionKind classifyFormatString(StringRef Fmt,
const LangOptions &LO,
58 const TargetInfo &TI) {
64 ConversionKind CK = ConversionKind::None;
66 bool HandleScanfSpecifier(
const analyze_scanf::ScanfSpecifier &FS,
67 const char *StartSpecifier,
68 unsigned SpecifierLen)
override {
71 if (!FS.consumesDataArgument())
76 analyze_scanf::ScanfConversionSpecifier SCS = FS.getConversionSpecifier();
78 switch (FS.getLengthModifier().getKind()) {
79 case analyze_scanf::LengthModifier::AsLongLong:
80 CK = ConversionKind::ToLongInt;
82 case analyze_scanf::LengthModifier::AsIntMax:
83 CK = ConversionKind::ToIntMax;
86 CK = ConversionKind::ToInt;
89 }
else if (SCS.isUIntArg()) {
90 switch (FS.getLengthModifier().getKind()) {
91 case analyze_scanf::LengthModifier::AsLongLong:
92 CK = ConversionKind::ToLongUInt;
94 case analyze_scanf::LengthModifier::AsIntMax:
95 CK = ConversionKind::ToUIntMax;
98 CK = ConversionKind::ToUInt;
101 }
else if (SCS.isDoubleArg()) {
102 switch (FS.getLengthModifier().getKind()) {
103 case analyze_scanf::LengthModifier::AsLongDouble:
104 CK = ConversionKind::ToLongDouble;
106 case analyze_scanf::LengthModifier::AsLong:
107 CK = ConversionKind::ToDouble;
110 CK = ConversionKind::ToFloat;
116 return CK == ConversionKind::None;
122 ConversionKind get()
const {
return CK; }
126 analyze_format_string::ParseScanfString(H, Fmt.begin(), Fmt.end(), LO, TI);
131StringRef classifyConversionType(ConversionKind
K) {
133 case ConversionKind::None:
134 llvm_unreachable(
"Unexpected conversion kind");
135 case ConversionKind::ToInt:
136 case ConversionKind::ToLongInt:
137 case ConversionKind::ToIntMax:
138 return "an integer value";
139 case ConversionKind::ToUInt:
140 case ConversionKind::ToLongUInt:
141 case ConversionKind::ToUIntMax:
142 return "an unsigned integer value";
143 case ConversionKind::ToFloat:
144 case ConversionKind::ToDouble:
145 case ConversionKind::ToLongDouble:
146 return "a floating-point value";
148 llvm_unreachable(
"Unknown conversion kind");
151StringRef classifyReplacement(ConversionKind
K) {
153 case ConversionKind::None:
154 llvm_unreachable(
"Unexpected conversion kind");
155 case ConversionKind::ToInt:
157 case ConversionKind::ToUInt:
159 case ConversionKind::ToIntMax:
161 case ConversionKind::ToLongInt:
163 case ConversionKind::ToLongUInt:
165 case ConversionKind::ToUIntMax:
167 case ConversionKind::ToFloat:
169 case ConversionKind::ToDouble:
171 case ConversionKind::ToLongDouble:
174 llvm_unreachable(
"Unknown conversion kind");
179 const auto *Call = Result.Nodes.getNodeAs<CallExpr>(
"expr");
180 const FunctionDecl *
FuncDecl =
nullptr;
181 ConversionKind Conversion = ConversionKind::None;
183 if (
const auto *ConverterFunc =
184 Result.Nodes.getNodeAs<FunctionDecl>(
"converter")) {
187 Conversion = classifyConversionFunc(ConverterFunc);
188 }
else if (
const auto *FFD =
189 Result.Nodes.getNodeAs<FunctionDecl>(
"formatted")) {
195 (FFD->getName() ==
"scanf" || FFD->getName() ==
"vscanf") ? 0 : 1;
199 if (Call->getNumArgs() < Idx)
202 if (
const Expr *Arg = Call->getArg(Idx)->IgnoreParenImpCasts()) {
203 if (
const auto *SL = dyn_cast<StringLiteral>(Arg)) {
204 FmtStr = SL->getString();
214 Conversion = classifyFormatString(FmtStr,
getLangOpts(),
215 Result.Context->getTargetInfo());
216 if (Conversion != ConversionKind::None)
223 diag(Call->getExprLoc(),
224 "%0 used to convert a string to %1, but function will not report "
225 "conversion errors; consider using '%2' instead")
226 <<
FuncDecl << classifyConversionType(Conversion)
227 << classifyReplacement(Conversion);
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
const LangOptions & getLangOpts() const
Returns the language options from the context.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
static constexpr const char FuncDecl[]