[cfe-commits] [patch] Added a suggestion when a std::string is passed to printf()
David Blaikie
dblaikie at gmail.com
Wed Jun 6 17:17:21 PDT 2012
On Wed, Jun 6, 2012 at 4:56 PM, Sam Panzer <panzer at google.com> wrote:
> This new patch addresses Chandler's points.
>
> On Wed, Jun 6, 2012 at 2:59 PM, Chandler Carruth <chandlerc at google.com>
> wrote:
>>
>> On Wed, Jun 6, 2012 at 2:35 PM, Sam Panzer <panzer at google.com> wrote:
>>>
>>> And here is another update that replaces the default non-POD error with a
>>> smarter (printf-specific) one, rather than just tacking on an extra warning.
>>
>>
>> Thanks, I really love this. =] Comments on the patch. A lot of this is
>> just style nit-picking to help you get used to the coding conventions in
>> Clang.
>>
>
> Style nitpicking is important - otherwise I won't learn :)
>
>>
>>
>>
>> +def warn_non_pod_string_passed_to_printf : Warning<
>>
>> This warning name is a bit misleading now... How about:
>> warn_non_pod_vararg_with_format_string?
>
>
> Done.
>
>>
>> -
>> +def note_printf_c_str: Note< "did you mean to call .c_str()?">;
>>
>> No space before the "
>
>
> Done.
>
>>
>>
>> Also, you should probably pass 'c_str' from the name of the method so that
>> we can generalize this later if we would like (str, dunno, maybe some
>> interesting suggestions for ObjectiveC types).
>>
>
> Also done. I was wondering about ObjectiveC strings...
>
>>
>> I would probably use more of a prose form rather than potentially getting
>> the syntax wrong: "did you mean to call the %0 method?"
>>
>>
>> + bool IsPrintfLike(FunctionDecl *FDecl, Expr **Args, unsigned NumArgs);
>>
>> I would prefer 'hasFormatStringArgument'. I think that's more accurate.
>
>
> Done.
>
>>
>>
>> +// Given a variadic function, decides whether it looks like we have a
>> +// printf-style format string and arguments)
>> +// This doesn't work for member functions.
>>
>> Why not?
>
>
> It had to do with the offsets of the implicit `this` parameter. It's been
> fixed!
>
>>
>>
>> +bool Sema::IsPrintfLike(FunctionDecl *FDecl, Expr **Args, unsigned
>> NumArgs) {
>> + // Anonymous and lambda functions will be assumed not to be printf.
>> + if (!FDecl)
>> + return false;
>> +
>> + for (specific_attr_iterator<FormatAttr>
>> + i = FDecl->specific_attr_begin<FormatAttr>(),
>>
>> Prevailing style you should use in new patches is for iterator loop
>> variables to use capital letters: I and E.
>
>
> Done.
>
>>
>>
>> + e = FDecl->specific_attr_end<FormatAttr>(); i != e ; ++i) {
>> + const FormatAttr *Format = *i;
>> + unsigned format_idx = Format->getFormatIdx() - 1;
>>
>> I think you can just use 'I->' here.
>
>
> Unfortunately, that doesn't compile ("member reference type is not a
> pointer"), probably because smallPtrSetIterator doesn't overload operator->.
Hooray, another not-quite-iterator. A perfect opportunity to add an
op-> overload, if you like.
>
>>
>> +
>> + if (format_idx >= NumArgs) {
>>
>> Avoid curlies on one-line ifs.
>
>
> Done.
>
>>
>>
>> + // There must be some other format
>> + continue;
>> + }
>> +
>> + return true;
>>
>> Reversing the logic would be shorter:
>>
>> if (format_idx < NumArgs)
>> return true;
>
>
> Fixed. I shouldn't be missing this sort of thing :)
>
>>
>>
>> + bool CheckFormatExpr(const analyze_printf::PrintfSpecifier &FS,
>> + const char *startSpecifier,
>>
>> Parameter names should be CamelCased. Function (and method) names should
>> be camelCased.
>>
>
> Done, and applied elsewhere in this patch.
>
>>
>> + unsigned specifierLen,
>> + const Expr *Ex);
>>
>> 'E' is the more common name for an Expr parameter.
>
>
> Done.
>
>>
>>
>> +// Determines if the specified is a C++ class or struct containing
>> +// a member with the specified name and kind (e.g. a CXXMethodDecl named
>> +// "c_str()"
>> +template<typename FieldKind>
>> +static llvm::SmallPtrSet<FieldKind*, 1>
>> +CXXRecordMembersNamed(StringRef Name, Sema &S, QualType Ty) {
>> + const RecordType *RT = Ty->getAs<RecordType>();
>> + llvm::SmallPtrSet<FieldKind*, 1> Results;
>> +
>> + if (!RT)
>> + return Results;
>> + const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(RT->getDecl());
>> + if (!RD)
>> + return Results;
>> +
>> + LookupResult R(S, &S.PP.getIdentifierTable().get(Name),
>> SourceLocation(),
>> + Sema::LookupMemberName);
>> +
>> + // We just need to include all members of the right kind turned up by
>> the
>> + // filter, at this point.
>> + if (S.LookupQualifiedName(R, RT->getDecl())) {
>> + LookupResult::Filter filter = R.makeFilter();
>> + while (filter.hasNext()) {
>> + NamedDecl *decl = filter.next()->getUnderlyingDecl();
>> + if (FieldKind *FK = dyn_cast<FieldKind>(decl)) {
>> + Results.insert(FK);
>> + }
>> + }
>> + filter.done();
>> + }
>> + return Results;
>> +}
>> +
>> +// Check if a (w)string was passed when a (w)char* was needed, and offer
>> a
>> +// better diagnostic if so. ATR is assumed to be valid.
>> +// Returns true when a c_str() conversion method is found.
>> +bool
>>
>> Please try not to break after return types unless you have to for
>> 80-columns.
>
>
> Done.
>
>>
>> +CheckPrintfHandler::CheckForCStrMembers(
>> + const analyze_printf::ArgTypeResult &ATR, const Expr *Ex,
>> + const CharSourceRange &CSR) {
>> + typedef llvm::SmallPtrSet<CXXMethodDecl*, 1> MethodSet;
>> + MethodSet Results =
>> + CXXRecordMembersNamed<CXXMethodDecl>("c_str", S, Ex->getType());
>> +
>> + for (MethodSet::iterator it = Results.begin(), end = Results.end();
>>
>> I and E here rather than it and end.
>
>
> Done.
>
>>
>>
>> + it != end; ++it) {
>> + const CXXMethodDecl *Method = (*it);
>>
>> Don't use extraneous parentheses please. Also, I suspect you can directly
>> use 'I->' below rather than creating a separate variable...
>
>
> Parens removed, though operator-> is unavailable here. For future reference,
> is Clang averse to extra variable declarations in favor of extra method
> calls?
I wouldn't really think of op->/op* as function calls for the purpose
of writing code like this. Though as for prevailing style, there are
many loops in Clang/LLVM that follow your style of immediately
declaring a local variable of the iterator's value_type - personally
I'd rather just name the iterator better and use op -> (which, since
the prevailing opinion is that it's OK to have op-> on an iterator
who's value_type is a pointer, means strictly less code - since you're
going to have to use -> on the pointer you declare anyway, and you
could just use it on the iterator directly).
>
>>
>>
>> + if (Method->getNumParams() == 0 &&
>> + ATR.matchesType(S.Context, Method->getResultType())) {
>> +
>> + S.Diag(Ex->getLocStart(), diag::note_printf_c_str)
>> + << FixItHint::CreateInsertion(Ex->getLocEnd(), ".c_str()");
>> + return true;
>> + }
>> + }
>> +
>> + return false;
>> +}
>> +
>> bool
>> CheckPrintfHandler::HandlePrintfSpecifier(const
>> analyze_printf::PrintfSpecifier
>> &FS,
>> const char *startSpecifier,
>> unsigned specifierLen) {
>> -
>>
>> Please don't change unrelated whitespace.
>
>
> Undone.
>
>>
>>
>> + // This should really be identical to the checks in SemaExpr.cpp.
>>
>> Can you hoist them into a helper function, and call them from here?
>
>
> Done. This required a bit of refactoring in
> DefaultVariadicArgumentPromotion.
>
>>
>> ExprResult Sema::DefaultVariadicArgumentPromotion(Expr *E,
>> VariadicCallType CT,
>> - FunctionDecl *FDecl) {
>> + FunctionDecl *FDecl,
>> + bool DeferErrors) {
>>
>> DeferErrors doesn't really clarify much for me... maybe "HasFormatString"?
>
>
> Done.
>
>>
>>
>> @@ -585,9 +586,14 @@ ExprResult
>> Sema::DefaultVariadicArgumentPromotion(Expr *E, VariadicCallType CT,
>> getLangOpts().ObjCAutoRefCount &&
>> E->getType()->isObjCLifetimeType())
>> TrivialEnough = true;
>> -
>> +
>>
>> You'll probably have to fix your editor to avoid unrelated whitespace
>> changes.
>
>
> I'll also start telling diff to ignore whitespace changes.
The opposite, really - you want diff to tell you when you have
whitespace changes so you can avoid committing those whitespace
changes that are unrelated to your actual change.
- David
>
>>
>>
>> if (TrivialEnough) {
>> // Nothing to diagnose. This is okay.
>> + } else if (DeferErrors) {
>> + // In case the function looks sufficiently like printf,
>> + // try to fix non-POD arguments (e.g. an std::string passed rather
>> than
>> + // a char *).
>> + // This is handled in SemaChecking, so we skip the error here.
>>
>> I just would say: "If there is a format string argument, non-POD argument
>> checking is handled along with the format string checking".
>
>
> Done.
>
>>
>>
>> + // Decide if we're probably inspecting a printf-like function
>>
>> I'm hopeful that with the function and variable name changes you can drop
>> this comment. It should be obvious from the code.
>>
>
> True, and done.
>
>>
>> +
>> + bool DeferErrors = IsPrintfLike(FDecl, Args, NumArgs);
>>
>
>
> _______________________________________________
> cfe-commits mailing list
> cfe-commits at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits
>
More information about the cfe-commits
mailing list