11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/Analysis/Analyses/FormatString.h"
14 #include "llvm/ADT/StringSwitch.h"
17 using namespace clang::ast_matchers;
23 void StrToNumCheck::registerMatchers(MatchFinder *
Finder) {
28 callee(functionDecl(anyOf(
29 functionDecl(hasAnyName(
"::atoi",
"::atof",
"::atol",
"::atoll"))
31 functionDecl(hasAnyName(
"::scanf",
"::sscanf",
"::fscanf",
32 "::vfscanf",
"::vscanf",
"::vsscanf"))
33 .bind(
"formatted")))))
39 enum class ConversionKind {
52 ConversionKind ClassifyConversionFunc(
const FunctionDecl *FD) {
53 return llvm::StringSwitch<ConversionKind>(FD->getName())
54 .Cases(
"atoi",
"atol", ConversionKind::ToInt)
55 .Case(
"atoll", ConversionKind::ToLongInt)
56 .Case(
"atof", ConversionKind::ToDouble)
57 .Default(ConversionKind::None);
60 ConversionKind ClassifyFormatString(StringRef Fmt,
const LangOptions &LO,
61 const TargetInfo &TI) {
66 class Handler :
public analyze_format_string::FormatStringHandler {
69 bool HandleScanfSpecifier(
const analyze_scanf::ScanfSpecifier &FS,
70 const char *startSpecifier,
71 unsigned specifierLen)
override {
74 if (!FS.consumesDataArgument())
79 analyze_scanf::ScanfConversionSpecifier SCS = FS.getConversionSpecifier();
81 switch (FS.getLengthModifier().getKind()) {
82 case analyze_scanf::LengthModifier::AsLongLong:
83 CK = ConversionKind::ToLongInt;
85 case analyze_scanf::LengthModifier::AsIntMax:
86 CK = ConversionKind::ToIntMax;
89 CK = ConversionKind::ToInt;
92 }
else if (SCS.isUIntArg()) {
93 switch (FS.getLengthModifier().getKind()) {
94 case analyze_scanf::LengthModifier::AsLongLong:
95 CK = ConversionKind::ToLongUInt;
97 case analyze_scanf::LengthModifier::AsIntMax:
98 CK = ConversionKind::ToUIntMax;
101 CK = ConversionKind::ToUInt;
104 }
else if (SCS.isDoubleArg()) {
105 switch (FS.getLengthModifier().getKind()) {
106 case analyze_scanf::LengthModifier::AsLongDouble:
107 CK = ConversionKind::ToLongDouble;
109 case analyze_scanf::LengthModifier::AsLong:
110 CK = ConversionKind::ToDouble;
113 CK = ConversionKind::ToFloat;
119 return CK == ConversionKind::None;
123 Handler() : CK(ConversionKind::None) {}
125 ConversionKind
get()
const {
return CK; }
129 analyze_format_string::ParseScanfString(H, Fmt.begin(), Fmt.end(), LO, TI);
134 StringRef ClassifyConversionType(ConversionKind K) {
136 case ConversionKind::None:
137 assert(
false &&
"Unexpected conversion kind");
138 case ConversionKind::ToInt:
139 case ConversionKind::ToLongInt:
140 case ConversionKind::ToIntMax:
141 return "an integer value";
142 case ConversionKind::ToUInt:
143 case ConversionKind::ToLongUInt:
144 case ConversionKind::ToUIntMax:
145 return "an unsigned integer value";
146 case ConversionKind::ToFloat:
147 case ConversionKind::ToDouble:
148 case ConversionKind::ToLongDouble:
149 return "a floating-point value";
151 llvm_unreachable(
"Unknown conversion kind");
154 StringRef ClassifyReplacement(ConversionKind K) {
156 case ConversionKind::None:
157 assert(
false &&
"Unexpected conversion kind");
158 case ConversionKind::ToInt:
160 case ConversionKind::ToUInt:
162 case ConversionKind::ToIntMax:
164 case ConversionKind::ToLongInt:
166 case ConversionKind::ToLongUInt:
168 case ConversionKind::ToUIntMax:
170 case ConversionKind::ToFloat:
172 case ConversionKind::ToDouble:
174 case ConversionKind::ToLongDouble:
177 llvm_unreachable(
"Unknown conversion kind");
181 void StrToNumCheck::check(
const MatchFinder::MatchResult &Result) {
182 const auto *Call = Result.Nodes.getNodeAs<CallExpr>(
"expr");
183 const FunctionDecl *FuncDecl =
nullptr;
184 ConversionKind Conversion;
186 if (
const auto *ConverterFunc =
187 Result.Nodes.getNodeAs<FunctionDecl>(
"converter")) {
189 FuncDecl = ConverterFunc;
190 Conversion = ClassifyConversionFunc(ConverterFunc);
191 }
else if (
const auto *FFD =
192 Result.Nodes.getNodeAs<FunctionDecl>(
"formatted")) {
198 (FFD->getName() ==
"scanf" || FFD->getName() ==
"vscanf") ? 0 : 1;
202 if (Call->getNumArgs() < Idx)
205 if (
const Expr *Arg = Call->getArg(Idx)->IgnoreParenImpCasts()) {
206 if (
const auto *SL = dyn_cast<StringLiteral>(Arg)) {
207 FmtStr = SL->getString();
217 Conversion = ClassifyFormatString(FmtStr, getLangOpts(),
218 Result.Context->getTargetInfo());
219 if (Conversion != ConversionKind::None)
226 diag(Call->getExprLoc(),
227 "%0 used to convert a string to %1, but function will not report "
228 "conversion errors; consider using '%2' instead")
229 << FuncDecl << ClassifyConversionType(Conversion)
230 << ClassifyReplacement(Conversion);
std::unique_ptr< ast_matchers::MatchFinder > Finder