Charusso created this revision.
Charusso added reviewers: NoQ, xazax.hun, ravikandhadai, baloghadamsoftware, 
Szelethus.
Charusso added a project: clang.
Herald added subscribers: cfe-commits, dkrupp, donat.nagy, mikhail.ramalho, 
a.sidorin, rnkovacs, szepet, mgorny.

Options:

- `apiModeling.ReturnValue:Calls`

  (string) A semicolon separated list of tuples.
  Where each tuple consists of a call name,
  its truth value, and an optional class name
  of that method separated by colons: "call
  name : truth value : optional class name";"...".
  It ensures the boolean return value of each
  function call. (default: "")



- `apiModeling.ReturnValue:Projects`

  (string) A semicolon separated list of predefined
  projects. It adds to the "Calls" additional
  hard coded call-lists of the given projects.
  Possible value: "llvm". (default: "llvm")


Repository:
  rC Clang

https://reviews.llvm.org/D63915

Files:
  clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
  clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
  clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp
  clang/test/Analysis/analyzer-config.c
  clang/test/Analysis/return-value-guaranteed.cpp

Index: clang/test/Analysis/return-value-guaranteed.cpp
===================================================================
--- /dev/null
+++ clang/test/Analysis/return-value-guaranteed.cpp
@@ -0,0 +1,83 @@
+// RUN: %clang_analyze_cc1 \
+// RUN:  -analyzer-checker=core \
+// RUN:  -analyzer-checker=apiModeling.ReturnValue \
+// RUN:  -analyzer-config apiModeling.ReturnValue:Calls=" error : true " \
+// RUN:  -analyzer-checker=debug.ExprInspection \
+// RUN:  -verify %s
+// RUN: %clang_analyze_cc1 \
+// RUN:  -analyzer-checker=core \
+// RUN:  -analyzer-checker=apiModeling.ReturnValue \
+// RUN:  -analyzer-config apiModeling.ReturnValue:Calls="error:true;error:false:ErrorHandler" \
+// RUN:  -analyzer-checker=debug.ExprInspection \
+// RUN:  -verify %s
+
+// FIXME: expected-no-diagnostics
+
+void clang_analyzer_eval(bool);
+void clang_analyzer_warnIfReached();
+
+
+struct Foo { int Field; };
+bool error();
+bool problem();
+void doSomething();
+
+// We predefine the return value of 'error()' to 'true' and we cannot take the
+// false-branches where error would occur.
+namespace test_calls {
+bool parseFoo(Foo &F) {
+  if (problem())
+    return error();
+
+  F.Field = 0;
+  return false;
+}
+
+bool parseFile() {
+  clang_analyzer_eval(error() == true); // FIXME: xpected-warning{{TRUE}}
+
+  Foo F;
+  if (parseFoo(F))
+    return true;
+
+  if (F.Field == 0) {
+    // no-warning: "The left operand of '==' is a garbage value" was here.
+    doSomething();
+  }
+
+  return false;
+}
+} // namespace test_calls
+
+namespace test_classes {
+struct ErrorHandler {
+  static bool error();
+};
+
+bool parseFoo(Foo &F) {
+  if (problem())
+    return error();
+
+  F.Field = 0;
+  return ErrorHandler::error();
+}
+
+bool parseFile() {
+  Foo F;
+  if (parseFoo(F))
+    return true;
+
+  if (F.Field == 0) {
+    // no-warning: "The left operand of '==' is a garbage value" was here.
+    doSomething();
+  }
+
+  return false;
+}
+} // namespace test_classes
+
+void test_note() {
+  clang_analyzer_eval(error() == true); // FIXME: xpected-warning{{TRUE}}
+  if (error())
+    (void)(1 / !error());
+}
Index: clang/test/Analysis/analyzer-config.c
===================================================================
--- clang/test/Analysis/analyzer-config.c
+++ clang/test/Analysis/analyzer-config.c
@@ -9,6 +9,8 @@
 // CHECK-NEXT: alpha.clone.CloneChecker:ReportNormalClones = true
 // CHECK-NEXT: alpha.security.MmapWriteExec:MmapProtExec = 0x04
 // CHECK-NEXT: alpha.security.MmapWriteExec:MmapProtRead = 0x01
+// CHECK-NEXT: apiModeling.ReturnValue:Calls = ""
+// CHECK-NEXT: apiModeling.ReturnValue:Projects = "llvm"
 // CHECK-NEXT: avoid-suppressing-null-argument-paths = false
 // CHECK-NEXT: c++-allocator-inlining = true
 // CHECK-NEXT: c++-container-inlining = false
@@ -88,4 +90,4 @@
 // CHECK-NEXT: unroll-loops = false
 // CHECK-NEXT: widen-loops = false
 // CHECK-NEXT: [stats]
-// CHECK-NEXT: num-entries = 85
+// CHECK-NEXT: num-entries = 87
Index: clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp
===================================================================
--- /dev/null
+++ clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp
@@ -0,0 +1,156 @@
+//===- ReturnValueChecker - Applies guaranteed return values ----*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This defines ReturnValueChecker, which checks for calls with guaranteed
+// boolean return value. It ensures the return value of each function call.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Basic/IdentifierTable.h"
+#include "clang/Driver/DriverDiagnostic.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringSet.h"
+
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+struct CallTy {
+  StringRef Name;
+  bool TruthValue;
+  Optional<StringRef> Class;
+};
+}
+
+namespace {
+class ReturnValueChecker : public Checker<check::PostCall> {
+public:
+  void checkPostCall(const CallEvent &CE, CheckerContext &C) const;
+
+  void setCalls(StringRef Calls);
+  void setProjects(StringRef Projects);
+
+private:
+  SmallVector<CallTy, 13> CallVector;
+};
+} // namespace
+
+void ReturnValueChecker::checkPostCall(const CallEvent &CE,
+                                       CheckerContext &C) const {
+  // Match the calls by name.
+  bool IsInteresting = false;
+  const CallTy *Call;
+  if (const IdentifierInfo *II = CE.getCalleeIdentifier()) {
+    for (const auto &CurrentCall : CallVector) {
+      if (II->isStr(CurrentCall.Name)) {
+        Call = &CurrentCall;
+        IsInteresting = true;
+        break;
+      }
+    }
+  }
+
+  if (!IsInteresting)
+    return;
+
+  // Match the calls by class name.
+  if (Optional<StringRef> ClassName = Call->Class)
+    if (const auto *MD = dyn_cast<CXXMethodDecl>(CE.getDecl()))
+      if (!MD->getParent()->getName().equals(*ClassName))
+        return;
+
+  // Create a note.
+  const NoteTag *CallTag = C.getNoteTag([Call](BugReport &BR) -> std::string {
+    SmallString<128> Msg;
+    llvm::raw_svector_ostream Out(Msg);
+
+    Out << '\'';
+    if (Optional<StringRef> ClassName = Call->Class)
+      Out << *ClassName << "::";
+
+    Out << Call->Name << "' always return "
+        << (Call->TruthValue ? "true" : "false");
+
+    return Out.str();
+  });
+
+
+  // Set the return value with the note.
+  ProgramStateRef State = C.getState();
+  SVal RetV = CE.getReturnValue();
+  Optional<DefinedOrUnknownSVal> RetDV = RetV.getAs<DefinedOrUnknownSVal>();
+
+  State = State->assume(*RetDV, Call->TruthValue);
+  C.addTransition(State, CallTag);
+}
+
+void ReturnValueChecker::setCalls(StringRef Calls) {
+  if (Calls == "\"\"" || Calls.size() == 0)
+    return;
+
+  SmallVector<StringRef, 13> RawTupleVector;
+  Calls.split(RawTupleVector, ';');
+
+  for (const auto &RawTuple : RawTupleVector) {
+    SmallVector<StringRef, 13> Tuple;
+    RawTuple.split(Tuple, ':');
+
+    CallTy Call;
+    Call.Name = Tuple[0].trim();
+    Call.TruthValue = Tuple[1].trim().equals("true");
+    Call.Class = (Tuple.size() == 3 && Tuple[2].trim().size() > 0)
+                     ? Tuple[2].trim()
+                     : Optional<StringRef>();
+
+    CallVector.push_back(Call);
+  }
+}
+
+void ReturnValueChecker::setProjects(StringRef Projects) {
+  if (Projects == "\"\"" || Projects.size() == 0)
+    return;
+
+  const llvm::StringSet<> KnownProjects = {"llvm"};
+
+  SmallVector<StringRef, 13> RawProjectVector;
+  Projects.split(RawProjectVector, ';');
+
+  for (const auto &RawProject : RawProjectVector) {
+    StringRef Project = RawProject.trim().lower();
+
+    if (Project.equals("llvm")) {
+      setCalls("Error:true:MCAsmParser;"
+               "Error:true:LLLexer");
+    }
+  }
+}
+
+void ento::registerReturnValueChecker(CheckerManager &Mgr) {
+  ReturnValueChecker *RVC = Mgr.registerChecker<ReturnValueChecker>();
+
+  StringRef Projects =
+      Mgr.getAnalyzerOptions().getCheckerStringOption(RVC, "Projects");
+  StringRef Calls =
+      Mgr.getAnalyzerOptions().getCheckerStringOption(RVC, "Calls");
+
+  RVC->setProjects(Projects);
+  RVC->setCalls(Calls);
+}
+
+bool ento::shouldRegisterReturnValueChecker(const LangOptions &LO) {
+  return true;
+}
Index: clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
+++ clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
@@ -83,6 +83,7 @@
   RetainCountChecker/RetainCountDiagnostics.cpp
   ReturnPointerRangeChecker.cpp
   ReturnUndefChecker.cpp
+  ReturnValueChecker.cpp
   RunLoopAutoreleaseLeakChecker.cpp
   SimpleStreamChecker.cpp
   SmartPtrModeling.cpp
Index: clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
===================================================================
--- clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -95,10 +95,10 @@
 def LLVM : Package<"llvm">;
 def LLVMAlpha : Package<"llvm">, ParentPackage<Alpha>;
 
-// The APIModeling package is for checkers that model APIs and don't perform
-// any diagnostics. These checkers are always turned on; this package is
-// intended for API modeling that is not controlled by the target triple.
-def APIModeling : Package<"apiModeling">, Hidden;
+// The APIModeling package is for checkers that model APIs. These checkers are
+// always turned on; this package is intended for API modeling that is not
+// controlled by the target triple.
+def APIModeling : Package<"apiModeling">;
 def GoogleAPIModeling : Package<"google">, ParentPackage<APIModeling>, Hidden;
 
 def Debug : Package<"debug">, Hidden;
@@ -274,6 +274,28 @@
 
 let ParentPackage = APIModeling in {
 
+def ReturnValueChecker : Checker<"ReturnValue">,
+  HelpText<"Model the guaranteed boolean return value of function calls">,
+  CheckerOptions<[
+    CmdLineOption<String,
+                  "Calls",
+                  "A semicolon separated list of tuples. Where each tuple "
+                  "consists of a call name, its truth value, and an optional "
+                  "class name of that method separated by colons: "
+                  "\"call name : truth value : optional class name\";\"...\". "
+                  "It ensures the boolean return value of each function call.",
+                  "\"\"",
+                  Released>,
+    CmdLineOption<String,
+                  "Projects",
+                  "A semicolon separated list of predefined projects. It adds "
+                  "to the \"Calls\" additional hard coded call-lists of the "
+                  "given projects. Possible value: \"llvm\".",
+                  "\"llvm\"",
+                  Released>
+  ]>,
+  Documentation<HasDocumentation>;
+
 def StdCLibraryFunctionsChecker : Checker<"StdCLibraryFunctions">,
   HelpText<"Improve modeling of the C standard library functions">,
   Documentation<NotDocumented>;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to