piotrdz created this revision.
piotrdz added a subscriber: cfe-commits.
This is another patch from my tool colobot-lint.
This adds a new check misc-old-style-function, which checks for instances of
functions written in legacy C style.
As before, I hope I did everything according to LLVM style, including testcases
and documentation.
If time allows, I will post another patch to extend this check with FixIt hints
to localize variable declarations.
http://reviews.llvm.org/D12473
Files:
clang-tidy/misc/CMakeLists.txt
clang-tidy/misc/MiscTidyModule.cpp
clang-tidy/misc/OldStyleFunctionCheck.cpp
clang-tidy/misc/OldStyleFunctionCheck.h
docs/clang-tidy/checks/list.rst
docs/clang-tidy/checks/misc-old-style-function.rst
test/clang-tidy/misc-old-style-function.cpp
Index: test/clang-tidy/misc-old-style-function.cpp
===================================================================
--- test/clang-tidy/misc-old-style-function.cpp
+++ test/clang-tidy/misc-old-style-function.cpp
@@ -0,0 +1,86 @@
+// RUN: %python %S/check_clang_tidy.py %s misc-old-style-function %t -config="{CheckOptions: [{key: misc-old-style-function.DeclarationAndFirstUseDistanceThreshold, value: 4}]}" --
+
+int fooInt();
+
+// CHECK-MESSAGES: :[[@LINE+1]]:6: warning: function 'oldStyleFunctionOneIntVariable' seems to be written in legacy C style: it has an uninitialized POD type variable declared far from its point of use: 'x' [misc-old-style-function]
+void oldStyleFunctionOneIntVariable() {
+ int x;
+ //
+ //
+ //
+ x = fooInt();
+}
+
+// CHECK-MESSAGES: :[[@LINE+1]]:6: warning: function 'oldStyleFunctionTwoIntVariables' seems to be written in legacy C style: it has 2 uninitialized POD type variables declared far from their point of use: 'x', 'y' [misc-old-style-function]
+void oldStyleFunctionTwoIntVariables() {
+ int x, y;
+ //
+ //
+ //
+ x = fooInt();
+ y = x + 2;
+}
+
+// CHECK-MESSAGES: :[[@LINE+1]]:6: warning: function 'oldStyleFunctionManyIntVariables' seems to be written in legacy C style: it has 7 uninitialized POD type variables declared far from their point of use: 'a', 'b', 'c', 'd', 'e'... [misc-old-style-function]
+void oldStyleFunctionManyIntVariables() {
+ int a, b, c, d, e, f, g;
+ //
+ //
+ //
+ a = b = c = d = e = f = g = fooInt();
+}
+
+struct Pod {
+ int a;
+};
+
+Pod fooPod();
+
+// CHECK-MESSAGES: :[[@LINE+1]]:6: warning: function 'oldStyleFunctionWithUserDefinedPodTypeVariable' seems to be written in legacy C style: it has an uninitialized POD type variable declared far from its point of use: 'x' [misc-old-style-function]
+void oldStyleFunctionWithUserDefinedPodTypeVariable() {
+ Pod x;
+ //
+ //
+ //
+ x = fooPod();
+}
+
+void functionWithDeclarationAndFirstUseBelowThreshold() {
+ int x;
+ //
+ //
+ x = fooInt();
+}
+
+void functionWithInitializedVariables() {
+ int x = 0, y = 0;
+ //
+ //
+ //
+ x = fooInt();
+ y = x + 2;
+}
+
+struct NonPod {
+ int a = 0;
+};
+
+NonPod fooNonPod();
+
+void functionWithNonPodTypeVariables() {
+ NonPod x, y;
+ //
+ //
+ //
+ x = fooNonPod();
+ y = fooNonPod();
+}
+
+void ignoreFunctionParameters(int& x, int &y) {
+ //
+ //
+ //
+ x = fooInt();
+ y = x + 2;
+}
+
Index: docs/clang-tidy/checks/misc-old-style-function.rst
===================================================================
--- docs/clang-tidy/checks/misc-old-style-function.rst
+++ docs/clang-tidy/checks/misc-old-style-function.rst
@@ -0,0 +1,24 @@
+misc-old-style-function
+=======================
+
+
+This check finds instances of functions, which use legacy C style of declaring
+all variables in a function at the beginning of function scope.
+
+Example:
+
+.. code:: c++
+
+ void foo() {
+ int a, x;
+ // after many lines in between, first use of variables:
+ a = bar();
+ for (i = 0; i < 10; i++) { /*...*/ }
+ }
+
+A given variable is considered written in legacy style if:
+ - it is a local variable of POD type
+ - its declaration does not have initialization
+ - its declaration and first use in function are separated by more than
+ a certain number of lines of code; this number is configured by
+ parameter `DeclarationAndFirstUseDistanceThreshold` with default vaule of 3
\ No newline at end of file
Index: docs/clang-tidy/checks/list.rst
===================================================================
--- docs/clang-tidy/checks/list.rst
+++ docs/clang-tidy/checks/list.rst
@@ -31,6 +31,7 @@
misc-macro-repeated-side-effects
misc-move-constructor-init
misc-noexcept-move-constructor
+ misc-old-style-function
misc-static-assert
misc-swapped-arguments
misc-undelegated-constructor
Index: clang-tidy/misc/OldStyleFunctionCheck.h
===================================================================
--- clang-tidy/misc/OldStyleFunctionCheck.h
+++ clang-tidy/misc/OldStyleFunctionCheck.h
@@ -0,0 +1,63 @@
+//===--- OldStyleFunctionCheck.h - clang-tidy--------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_OLD_STYLE_FUNCTION_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_OLD_STYLE_FUNCTION_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+/// \brief Check for functions written in legacy C style
+///
+/// This check finds instances of functions, which use legacy C style
+/// of declaring all variables in a function at the beginning of function
+/// scope, for example:
+/// \code
+/// void foo()
+/// {
+/// int a, x;
+/// // after many lines in between, first use of variables:
+/// a = bar();
+/// for (i = 0; i < 10; i++) { /*...*/ }
+/// }
+/// \endcode
+///
+/// A given variable is considered written in legacy style if:
+/// - it is a local variable of POD type
+/// - its declaration does not have initialization
+/// - its declaration and first use in function are separated by more than a
+/// certain number of lines of code; this number is configured by parameter
+/// DeclarationAndFirstUseDistanceThreshold with default vaule of 3
+///
+/// FIXME: this check can be extended with FixIt hints to automatically refactor
+/// code so that variable declarations are moved to the place of first use,
+/// thus localizing the variable declaration.
+///
+class OldStyleFunctionCheck : public ClangTidyCheck {
+public:
+ OldStyleFunctionCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ DeclarationAndFirstUseDistanceThreshold(Options.get<int>("DeclarationAndFirstUseDistanceThreshold", 3))
+ {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ int DeclarationAndFirstUseDistanceThreshold;
+};
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_OLD_STYLE_FUNCTION_H
+
Index: clang-tidy/misc/OldStyleFunctionCheck.cpp
===================================================================
--- clang-tidy/misc/OldStyleFunctionCheck.cpp
+++ clang-tidy/misc/OldStyleFunctionCheck.cpp
@@ -0,0 +1,189 @@
+//===--- OldStyleFunctionCheck.cpp - clang-tidy----------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "OldStyleFunctionCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+#include <unordered_set>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace misc {
+
+namespace {
+
+class OldStyleDeclarationFinder : public RecursiveASTVisitor<OldStyleDeclarationFinder> {
+public:
+ OldStyleDeclarationFinder(ASTContext* Context, int DeclarationAndFirstUseDistanceThreshold);
+
+ bool VisitStmt(Stmt* Statement);
+
+ int getOldStyleDeclarationsCount() const;
+ std::string getFewFirstOldStyleDeclarations() const;
+
+private:
+ bool isInteresting(const VarDecl* VariableDeclaration);
+ bool isUninitializedPodVariable(const VarDecl* VariableDeclaration, ASTContext* Context);
+ bool hasImplicitOrDefaultedInitialization(const VarDecl* VariableDeclaration);
+
+ ASTContext* Context;
+ int DeclarationAndFirstUseDistanceThreshold;
+
+ static constexpr int MINIMUM_SET_SIZE = 10;
+ llvm::SmallSet<StringRef, MINIMUM_SET_SIZE> OldStyleDeclarations;
+ llvm::SmallSet<StringRef, MINIMUM_SET_SIZE> CorrectStyleDeclarations;
+
+ static constexpr int MAX_NAMES_IN_DESCRIPTION = 5;
+ llvm::SmallVector<StringRef, MAX_NAMES_IN_DESCRIPTION> FewFirstOldStyleDeclarations;
+};
+
+} // anonymous namespace
+
+void OldStyleFunctionCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(functionDecl(
+ unless(anyOf(isExpansionInSystemHeader(),
+ isImplicit())),
+ isDefinition())
+ .bind("functionDefinition"),
+ this);
+}
+
+void OldStyleFunctionCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto* FunctionDefinition = Result.Nodes.getNodeAs<FunctionDecl>("functionDefinition");
+ auto* FunctionBody = FunctionDefinition->getBody();
+ if (FunctionDefinition == nullptr || FunctionBody == nullptr)
+ return;
+
+ OldStyleDeclarationFinder Finder(Result.Context, DeclarationAndFirstUseDistanceThreshold);
+ Finder.TraverseStmt(FunctionBody);
+
+ auto OldStyleDeclarationCount = Finder.getOldStyleDeclarationsCount();
+ if (OldStyleDeclarationCount == 0)
+ return;
+
+ diag(FunctionDefinition->getLocation(),
+ "function '%0' seems to be written in legacy C style: "
+ "it has %select{an|%1}2 uninitialized POD type variable%s1 declared far from %select{its|their}2 point of use: %3")
+ << FunctionDefinition->getQualifiedNameAsString()
+ << OldStyleDeclarationCount
+ << (OldStyleDeclarationCount == 1 ? 0 : 1)
+ << Finder.getFewFirstOldStyleDeclarations();
+}
+
+////////////////////////////
+
+OldStyleDeclarationFinder::OldStyleDeclarationFinder(ASTContext* Context, int DeclarationAndFirstUseDistanceThreshold)
+ : Context(Context), DeclarationAndFirstUseDistanceThreshold(DeclarationAndFirstUseDistanceThreshold)
+{}
+
+bool OldStyleDeclarationFinder::VisitStmt(Stmt* Statement) {
+ const auto* DeclarationRef = dyn_cast_or_null<const DeclRefExpr>(Statement);
+ if (DeclarationRef == nullptr)
+ return true;
+
+ const auto* VariableDeclaration = dyn_cast_or_null<const VarDecl>(DeclarationRef->getDecl());
+ if (! isInteresting(VariableDeclaration))
+ return true;
+
+ auto& SM = Context->getSourceManager();
+
+ auto DeclarationLineNumber = SM.getPresumedLineNumber(VariableDeclaration->getLocation());
+ auto FirstUseLineNumber = SM.getPresumedLineNumber(Statement->getLocStart());
+ auto Name = VariableDeclaration->getName();
+
+ if (FirstUseLineNumber < DeclarationLineNumber + DeclarationAndFirstUseDistanceThreshold) {
+ CorrectStyleDeclarations.insert(Name);
+ }
+ else {
+ OldStyleDeclarations.insert(Name);
+ if (FewFirstOldStyleDeclarations.size() < MAX_NAMES_IN_DESCRIPTION)
+ FewFirstOldStyleDeclarations.push_back(Name);
+ }
+
+ return true;
+}
+
+bool OldStyleDeclarationFinder::isInteresting(const VarDecl* VariableDeclaration) {
+ // we're only interested in local, POD type variables that don't have proper initialization
+ if (VariableDeclaration == nullptr ||
+ !VariableDeclaration->hasLocalStorage() ||
+ VariableDeclaration->isImplicit() ||
+ ParmVarDecl::classof(VariableDeclaration) ||
+ !isUninitializedPodVariable(VariableDeclaration, Context)) {
+ return false;
+ }
+
+ auto Name = VariableDeclaration->getName();
+
+ // already visited?
+ if (OldStyleDeclarations.count(Name) > 0 ||
+ CorrectStyleDeclarations.count(Name) > 0) {
+ return false;
+ }
+
+ return true;
+}
+
+bool OldStyleDeclarationFinder::isUninitializedPodVariable(const VarDecl* VariableDeclaration, ASTContext* Context) {
+ auto VariableType = VariableDeclaration->getType();
+ if (! VariableType.isPODType(*Context))
+ return false;
+
+ if (VariableType->isRecordType() && VariableDeclaration->hasInit())
+ return hasImplicitOrDefaultedInitialization(VariableDeclaration);
+
+ return !VariableDeclaration->hasInit();
+}
+
+bool OldStyleDeclarationFinder::hasImplicitOrDefaultedInitialization(const VarDecl* VariableDeclaration) {
+ const auto* InitConstructExpr = llvm::dyn_cast_or_null<const CXXConstructExpr>(VariableDeclaration->getInit());
+ if (InitConstructExpr == nullptr)
+ return false;
+
+ const auto* TypeConstructorDecl = InitConstructExpr->getConstructor();
+ if (TypeConstructorDecl == nullptr)
+ return false;
+
+ return TypeConstructorDecl->isImplicit() ||
+ TypeConstructorDecl->getCanonicalDecl()->isDefaulted();
+}
+
+int OldStyleDeclarationFinder::getOldStyleDeclarationsCount() const {
+ return OldStyleDeclarations.size();
+}
+
+std::string OldStyleDeclarationFinder::getFewFirstOldStyleDeclarations() const {
+ std::string result;
+
+ bool first = true;
+ for (const auto& declaration : FewFirstOldStyleDeclarations) {
+ if (!first)
+ result += ", ";
+ else
+ first = false;
+
+ result += "'";
+ result += declaration.str();
+ result += "'";
+ }
+
+ if (OldStyleDeclarations.size() > FewFirstOldStyleDeclarations.size())
+ result += "...";
+
+ return result;
+}
+
+} // namespace misc
+} // namespace tidy
+} // namespace clang
+
Index: clang-tidy/misc/MiscTidyModule.cpp
===================================================================
--- clang-tidy/misc/MiscTidyModule.cpp
+++ clang-tidy/misc/MiscTidyModule.cpp
@@ -20,6 +20,7 @@
#include "MacroRepeatedSideEffectsCheck.h"
#include "MoveConstructorInitCheck.h"
#include "NoexceptMoveConstructorCheck.h"
+#include "OldStyleFunctionCheck.h"
#include "StaticAssertCheck.h"
#include "SwappedArgumentsCheck.h"
#include "UndelegatedConstructor.h"
@@ -55,6 +56,8 @@
"misc-move-constructor-init");
CheckFactories.registerCheck<NoexceptMoveConstructorCheck>(
"misc-noexcept-move-constructor");
+ CheckFactories.registerCheck<OldStyleFunctionCheck>(
+ "misc-old-style-function");
CheckFactories.registerCheck<StaticAssertCheck>(
"misc-static-assert");
CheckFactories.registerCheck<SwappedArgumentsCheck>(
Index: clang-tidy/misc/CMakeLists.txt
===================================================================
--- clang-tidy/misc/CMakeLists.txt
+++ clang-tidy/misc/CMakeLists.txt
@@ -12,6 +12,7 @@
MiscTidyModule.cpp
MoveConstructorInitCheck.cpp
NoexceptMoveConstructorCheck.cpp
+ OldStyleFunctionCheck.cpp
StaticAssertCheck.cpp
SwappedArgumentsCheck.cpp
UndelegatedConstructor.cpp
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits