diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td --- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -1667,7 +1667,7 @@ def UnixAPIPortabilityChecker : Checker<"UnixAPI">, HelpText<"Finds implementation-defined behavior in UNIX/Posix functions">, Documentation; - + } // end optin.portability //===----------------------------------------------------------------------===// diff --git a/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp --- a/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp @@ -11,11 +11,12 @@ // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/Basic/TargetInfo.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/STLExtras.h" @@ -59,9 +60,11 @@ }; -class UnixAPIPortabilityChecker : public Checker< check::PreStmt > { +class UnixAPIPortabilityChecker + : public Checker, check::PostCall> { public: void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; private: mutable std::unique_ptr BT_mallocZero; @@ -493,6 +496,41 @@ CheckVallocZero(C, CE); } +void UnixAPIPortabilityChecker::checkPostCall(const CallEvent &Call, + CheckerContext &C) const { + + auto State = C.getState(); + + const IdentifierInfo *II = Call.getCalleeIdentifier(); + if (!II) + return; + if (!II->isStr("printf")) + return; + + if (!BT_mallocZero) + BT_mallocZero.reset(new BugType(this, "Call to printf", "Example checker")); + + for (unsigned int i = 0; i < Call.getNumArgs(); i++) { + const auto *Arg = Call.getArgExpr(i); + if (!Arg) + return; + const auto *LC = C.getLocationContext(); + auto Val = State->getSVal(Arg, LC); + if (Val.isZeroConstant()) { + ExplodedNode *N = C.generateErrorNode(); + + // Further better the diagnostic message by adding a bug report visitor + auto Report = std::make_unique( + *BT_mallocZero, + "Passing a null pointer to printf() is implementation dependant. " + "Portability warning.", + N); + Report->addRange(Arg->getSourceRange()); + C.emitReport(std::move(Report)); + } + } +} + //===----------------------------------------------------------------------===// // Registration. //===----------------------------------------------------------------------===// diff --git a/flang/docs/Extensions.md b/flang/docs/Extensions.md --- a/flang/docs/Extensions.md +++ b/flang/docs/Extensions.md @@ -242,6 +242,11 @@ compilers, so it is not supported. * f18 doesn't impose a limit on the number of continuation lines allowed for a single statement. +* When a type-bound procedure declaration statement has neither interface + nor attributes, the "::" before the bindings is optional, even + if a binding has renaming with "=> proc". + The colons are not necessary for an unambiguous parse, C768 + notwithstanding. ### Extensions supported when enabled by options diff --git a/flang/lib/Parser/Fortran-parsers.cpp b/flang/lib/Parser/Fortran-parsers.cpp --- a/flang/lib/Parser/Fortran-parsers.cpp +++ b/flang/lib/Parser/Fortran-parsers.cpp @@ -522,6 +522,9 @@ // R749 type-bound-procedure-stmt -> // PROCEDURE [[, bind-attr-list] ::] type-bound-proc-decl-list | // PROCEDURE ( interface-name ) , bind-attr-list :: binding-name-list +// The "::" is required by the standard (C768) in the first production if +// any type-bound-proc-decl has a "=>', but it's not strictly necessary to +// avoid a bad parse. TYPE_CONTEXT_PARSER("type bound PROCEDURE statement"_en_US, "PROCEDURE" >> (construct( @@ -531,6 +534,15 @@ "," >> nonemptyList(Parser{}), ok), localRecovery("expected list of binding names"_err_en_US, "::" >> listOfNames, SkipTo<'\n'>{}))) || + construct(construct< + TypeBoundProcedureStmt::WithoutInterface>( + pure>(), + nonemptyList( + "expected type bound procedure declarations"_err_en_US, + construct(name, + maybe(extension( + "type-bound procedure statement should have '::' if it has '=>'"_port_en_US, + "=>" >> name)))))) || construct( construct( optionalListBeforeColons(Parser{}), diff --git a/flang/test/Parser/missing-colons.f90 b/flang/test/Parser/missing-colons.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Parser/missing-colons.f90 @@ -0,0 +1,13 @@ +! RUN: %flang_fc1 -fsyntax-only -pedantic %s 2>&1 | FileCheck %s +module m + type t + contains +!CHECK: portability: type-bound procedure statement should have '::' if it has '=>' + procedure p => sub + end type + contains + subroutine sub(x) + class(t), intent(in) :: x + end subroutine +end module +