aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/Sema/SemaChecking.cpp
diff options
context:
space:
mode:
authorRichard Trieu <rtrieu@google.com>2016-12-05 23:41:46 +0000
committerRichard Trieu <rtrieu@google.com>2016-12-05 23:41:46 +0000
commit67c0071517e5e8702645efa9f6119e48ba0b62ed (patch)
tree8a74222725441b644d9f09fba0c7ba6fe6aec89d /clang/lib/Sema/SemaChecking.cpp
parentfe1094b8115ea8ded42d8d2c3dbcfa9ef32f0a76 (diff)
downloadllvm-67c0071517e5e8702645efa9f6119e48ba0b62ed.zip
llvm-67c0071517e5e8702645efa9f6119e48ba0b62ed.tar.gz
llvm-67c0071517e5e8702645efa9f6119e48ba0b62ed.tar.bz2
Warn on unsigned zero in call to std::max
New default warning that triggers when an unsigned zero is used in a call to std::max. For unsigned values, zero is the minimum value, so any call to std::max is always equal to the other value. A common pattern was to take the max of zero and the difference of two unsigned values, not taking into account that unsigned values wrap around below zero. This warning also emits a note with a fixit hint to remove the zero and call to std::max. llvm-svn: 288732
Diffstat (limited to 'clang/lib/Sema/SemaChecking.cpp')
-rw-r--r--clang/lib/Sema/SemaChecking.cpp87
1 files changed, 87 insertions, 0 deletions
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 9e16554..bb68853 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -2518,6 +2518,8 @@ bool Sema::CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall,
return false;
CheckAbsoluteValueFunction(TheCall, FDecl, FnInfo);
+ CheckMinZero(TheCall, FDecl, FnInfo);
+
if (getLangOpts().ObjC1)
DiagnoseCStringFormatDirectiveInCFAPI(*this, FDecl, Args, NumArgs);
@@ -6767,6 +6769,91 @@ void Sema::CheckAbsoluteValueFunction(const CallExpr *Call,
Call->getCallee()->getSourceRange(), NewAbsKind, ArgType);
}
+//===--- CHECK: Warn on use of std::max and unsigned zero. r---------------===//
+static bool IsFunctionStdMax(const FunctionDecl *FDecl) {
+ if (!FDecl)
+ return false;
+
+ if (!FDecl->getIdentifier() || !FDecl->getIdentifier()->isStr("max"))
+ return false;
+
+ const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(FDecl->getDeclContext());
+
+ while (ND && ND->isInlineNamespace()) {
+ ND = dyn_cast<NamespaceDecl>(ND->getDeclContext());
+ }
+
+ if (!ND || !ND->getIdentifier() || !ND->getIdentifier()->isStr("std"))
+ return false;
+
+ if (!isa<TranslationUnitDecl>(ND->getDeclContext()))
+ return false;
+
+ return true;
+}
+
+void Sema::CheckMinZero(const CallExpr *Call, const FunctionDecl *FDecl,
+ IdentifierInfo *FnInfo) {
+ if (!Call || !FDecl) return;
+
+ // Ignore template specializations and macros.
+ if (!ActiveTemplateInstantiations.empty()) return;
+ if (Call->getExprLoc().isMacroID()) return;
+
+ // Only care about the one template argument, two function parameter std::max
+ if (Call->getNumArgs() != 2) return;
+ if (!IsFunctionStdMax(FDecl)) return;
+ const auto * ArgList = FDecl->getTemplateSpecializationArgs();
+ if (!ArgList) return;
+ if (ArgList->size() != 1) return;
+
+ // Check that template type argument is unsigned integer.
+ const auto& TA = ArgList->get(0);
+ if (TA.getKind() != TemplateArgument::Type) return;
+ QualType ArgType = TA.getAsType();
+ if (!ArgType->isUnsignedIntegerType()) return;
+
+ // See if either argument is a literal zero.
+ auto IsLiteralZeroArg = [](const Expr* E) -> bool {
+ const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(E);
+ if (!MTE) return false;
+ const auto *Num = dyn_cast<IntegerLiteral>(MTE->GetTemporaryExpr());
+ if (!Num) return false;
+ if (Num->getValue() != 0) return false;
+ return true;
+ };
+
+ const Expr *FirstArg = Call->getArg(0);
+ const Expr *SecondArg = Call->getArg(1);
+ const bool IsFirstArgZero = IsLiteralZeroArg(FirstArg);
+ const bool IsSecondArgZero = IsLiteralZeroArg(SecondArg);
+
+ // Only warn when exactly one argument is zero.
+ if (IsFirstArgZero == IsSecondArgZero) return;
+
+ SourceRange FirstRange = FirstArg->getSourceRange();
+ SourceRange SecondRange = SecondArg->getSourceRange();
+
+ SourceRange ZeroRange = IsFirstArgZero ? FirstRange : SecondRange;
+
+ Diag(Call->getExprLoc(), diag::warn_max_unsigned_zero)
+ << IsFirstArgZero << Call->getCallee()->getSourceRange() << ZeroRange;
+
+ // Deduce what parts to remove so that "std::max(0u, foo)" becomes "(foo)".
+ SourceRange RemovalRange;
+ if (IsFirstArgZero) {
+ RemovalRange = SourceRange(FirstRange.getBegin(),
+ SecondRange.getBegin().getLocWithOffset(-1));
+ } else {
+ RemovalRange = SourceRange(getLocForEndOfToken(FirstRange.getEnd()),
+ SecondRange.getEnd());
+ }
+
+ Diag(Call->getExprLoc(), diag::note_remove_max_call)
+ << FixItHint::CreateRemoval(Call->getCallee()->getSourceRange())
+ << FixItHint::CreateRemoval(RemovalRange);
+}
+
//===--- CHECK: Standard memory functions ---------------------------------===//
/// \brief Takes the expression passed to the size_t parameter of functions