SlideShare uma empresa Scribd logo
1 de 56
Baixar para ler offline
Clang-tidy:
путешествие внутрь AST С++
Юрий Ефимочев
Обо мне
Solarwinds
Lead Developer
MSP Backup & Recovery
Что такое clang-tidy?
Инструмент для статического анализа кода и
поиска типичных ошибок программирования
Встроенные правила(~200)
● ClangAnalyzer
● Readability/Modernize/Performance
● CppCoreGuidelines
● LLVM/Google-style
Пример запуска
class TestClass
{
public:
TestClass() :
m_B(),
m_C()
{
}
private:
int m_A;
int m_B;
int m_C;
};
Пример запуска
$ clang-tidy -checks="cppcoreguidelines-*" ./test.cpp
1 warning generated.
./test.cpp:4:5: warning: constructor does not initialize these fields: m_A
[cppcoreguidelines-pro-type-member-init]
TestClass() :
^
m_A(),
Code review
Цели code review
● Соответствие реализации задаче
● Поиск ошибок и неоптимальностей реализации
● Соответствие guidelines и best practices
Clang-tidy и code review?
Платформа для построения собственных
инструментов статического анализа кода
Расширяемость
● Полный доступ к AST и препроцессору
● Модульная архитектура
● Инфраструктура для разработки и
тестирования
Что необходимо для использования?
● Код должен собираться clang
● Необходима compilation database
Алгоритм разработки правила
1. Подготовить пример
2. Посмотреть в AST
3. ???
4. Profit
Пример 1
#include <iostream>
class TestClass
{
public:
static int const Constant;
int m_public;
private:
int m_private;
};
struct TestStruct
{
int Field;
};
// style: class public field
Clang-check: визуализация AST
$ clang-check -ast-dump -ast-dump-filter="Test" ./test.cpp
Dumping TestClass:
CXXRecordDecl 0xaf665da8 <test.cpp:3:1, line:12:1> line:3:7 class TestClass definition
|-CXXRecordDecl 0xaf665ec0 <col:1, col:7> col:7 implicit class TestClass
|-AccessSpecDecl 0xaf665f50 <line:5:1, col:7> col:1 public
|-VarDecl 0xaf665f88 <line:6:5, col:22> col:22 Constant 'const int' static
|-FieldDecl 0xaf665ff8 <line:8:5, col:9> col:9 m_public 'int'
|-AccessSpecDecl 0xaf666040 <line:10:1, col:8> col:1 private
`-FieldDecl 0xaf666078 <line:11:5, col:9> col:9 m_private 'int'
Dumping TestStruct:
CXXRecordDecl 0xaf6660c0 <test.cpp:14:1, line:17:1> line:14:8 struct TestStruct definition
|-CXXRecordDecl 0xaf6661d0 <col:1, col:8> col:8 implicit struct TestStruct
`-FieldDecl 0xaf666270 <line:16:5, col:9> col:9 Field 'int'
AST Nodes
● Decl
● Stmt
● Type
AST Nodes
● Decl
CXXRecordDecl
CXXMethodDecl
VarDecl
● Stmt
● Type
AST Nodes
● Decl
● Stmt
ifStmt
CXXTryStmt
BinaryOperator
● Type
AST Nodes
● Decl
● Stmt
● Type
PointerType
ReferenceType
LValueReferenceType
Пример 1: AST
$ clang-check -ast-dump -ast-dump-filter="Test" ./test.cpp
Dumping TestClass:
CXXRecordDecl 0xaf665da8 <test.cpp:3:1, line:12:1> line:3:7 class TestClass definition
|-CXXRecordDecl 0xaf665ec0 <col:1, col:7> col:7 implicit class TestClass
|-AccessSpecDecl 0xaf665f50 <line:5:1, col:7> col:1 public
|-VarDecl 0xaf665f88 <line:6:5, col:22> col:22 Constant 'const int' static
|-FieldDecl 0xaf665ff8 <line:8:5, col:9> col:9 m_public 'int'
|-AccessSpecDecl 0xaf666040 <line:10:1, col:8> col:1 private
`-FieldDecl 0xaf666078 <line:11:5, col:9> col:9 m_private 'int'
Dumping TestStruct:
CXXRecordDecl 0xaf6660c0 <test.cpp:14:1, line:17:1> line:14:8 struct TestStruct definition
|-CXXRecordDecl 0xaf6661d0 <col:1, col:8> col:8 implicit struct TestStruct
`-FieldDecl 0xaf666270 <line:16:5, col:9> col:9 Field 'int'
AST Matchers
● Node Matchers
cxxRecordDecl, cxxMethodDecl, namespaceDecl, ifStmt, ...
● Narrowing Matchers
isConstant, isFinal, hasName, matchesName, unless, ...
● Traversal Matchers
hasDescendant, hasParent, hasBody, ...
cxxMethodDecl(matchesName(“Get.*”), hasParent(cxxRecordDecl(isStruct()))
Пример 1
clang-query> match fieldDecl()
Match #1:
/home/yury/Projects/test.cpp:8:5: note: "root" binds here
int m_public;
^~~~~~~~~~~~
Match #2:
/home/yury/Projects/test.cpp:11:5: note: "root" binds here
int m_private;
^~~~~~~~~~~~~
Match #3:
/home/yury/Projects/test.cpp:16:5: note: "root" binds here
int Field;
^~~~~~~~~
3 matches.
Пример 1
clang-query> match fieldDecl(isPublic())
Match #1:
/home/yury/Projects/test.cpp:8:5: note: "root" binds here
int m_public;
^~~~~~~~~~~~
Match #2:
/home/yury/Projects/test.cpp:16:5: note: "root" binds here
int Field;
^~~~~~~~~
2 matches.
Пример 1
clang-query> match fieldDecl(isPublic(), hasParent(cxxRecordDecl(isClass())))
Match #1:
/home/yury/Projects/test.cpp:8:5: note: "root" binds here
int m_public;
^~~~~~~~~~~~
1 match.
Добавление правила
$ ./add_new_check.py misc field-visibility
Updating ./misc/CMakeLists.txt...
Creating ./misc/FieldVisibilityCheck.h...
Creating ./misc/FieldVisibilityCheck.cpp...
Updating ./misc/MiscTidyModule.cpp...
Creating ../test/clang-tidy/misc-field-visibility.cpp...
Creating ../docs/clang-tidy/checks/misc-field-visibility.rst...
Updating ../docs/clang-tidy/checks/list.rst...
Done. Now it's your turn!
Пример 1: шаблон реализации
class FieldVisibilityCheck : public ClangTidyCheck
{
public:
FieldVisibilityCheck(StringRef name, ClangTidyContext* context) :
ClangTidyCheck(name, context)
{
}
private:
void registerMatchers(ast_matchers::MatchFinder* finder) override
{
}
void check(ast_matchers::MatchFinder::MatchResult const& result) override
{
}
};
Пример 1: реализация
class FieldVisibilityCheck : public ClangTidyCheck
{
public:
FieldVisibilityCheck(StringRef name, ClangTidyContext* context) :
ClangTidyCheck(name, context)
{
}
private:
void registerMatchers(ast_matchers::MatchFinder* finder) override
{
finder->addMatcher(fieldDecl(isPublic(), hasParent(cxxRecordDecl(isClass()))).bind("field"), this);
}
void check(ast_matchers::MatchFinder::MatchResult const& result) override
{
FieldDecl const& field = *result.Nodes.getNodeAs<FieldDecl const>("field");
diag(field.getLocStart(), "Class field should be private");
}
};
Пример 1: результат
$ clang-tidy -checks="misc-field-visibility-check" ./test.cpp
1 warning generated.
./test.cpp:8:5: warning: Class field should be private [misc-field-visibility-check]
int m_public;
^
Пример 2
int Function(int a)
{
// ...
a = something;
// ...
return a;
}
// style: mutable parameter
Пример 2
struct Test
{
virtual int VirtualMethod(int a) = 0;
int Method(int a, int const b, int& c, int* d)
{
a = b;
return a;
}
};
Пример 2: AST
Dumping Test:
CXXRecordDecl 0x46b1b20 <test.cpp:1:1, line:9:1> line:1:8 struct Test definition
|-CXXRecordDecl 0x46b1c30 <col:1, col:8> col:8 implicit struct Test
|-CXXMethodDecl 0x46b1d90 <line:3:5, col:32> col:9 VirtualMethod 'int (int)'
| `-ParmVarDecl 0x46b1cd0 <col:23, col:27> col:27 a 'int'
`-CXXMethodDecl 0x46b2140 <line:4:5, line:8:5> line:4:9 Method 'int (int, const int, int &, int *)'
|-ParmVarDecl 0x46b1e50 <col:16, col:20> col:20 used a 'int'
|-ParmVarDecl 0x46b1ec0 <col:23, col:33> col:33 used b 'const int'
|-ParmVarDecl 0x46b1f60 <col:36, col:41> col:41 c 'int &'
|-ParmVarDecl 0x46b2000 <col:44, col:49> col:49 d 'int *'
`-CompoundStmt 0x46b2320 <line:5:5, line:8:5>
|-BinaryOperator 0x46b22a0 <line:6:9, col:13> 'int' lvalue '='
| |-DeclRefExpr 0x46b2238 <col:9> 'int' lvalue ParmVar 0x46b1e50 'a' 'int'
| `-ImplicitCastExpr 0x46b2288 <col:13> 'int' <LValueToRValue>
| `-DeclRefExpr 0x46b2260 <col:13> 'const int' lvalue ParmVar 0x46b1ec0 'b' 'const int'
`-ReturnStmt 0x46b2308 <line:7:9, col:16>
`-ImplicitCastExpr 0x46b22f0 <col:16> 'int' <LValueToRValue>
`-DeclRefExpr 0x46b22c8 <col:16> 'int' lvalue ParmVar 0x46b1e50 'a' 'int'
Пример 2: реализация
void ImmutableParamsCheck::registerMatchers(MatchFinder* finder)
{
finder->addMatcher(
parmVarDecl(
hasAncestor(functionDecl(hasBody(stmt()))),
unless(anyOf(
hasType(isConstQualified()),
hasType(referenceType()),
hasType(pointerType())))).bind("parameter"), this);
}
void ImmutableParamsCheck::check(MatchFinder::MatchResult const& result)
{
ParmVarDecl const& parameter = *result.Nodes.getNodeAs<ParmVarDecl const>("parameter");
SourceLocation const location = parameter.getSourceRange().getEnd();
diag(location, "Consider making constant") <<
FixItHint::CreateInsertion(location, "const ");
}
Пример 2: результат
$ clang-tidy -checks="misc-immutable-parameters-check" -fix ./test.cpp
2 warnings generated.
./test.cpp:4:20: warning: Consider making constant [iaso-immutable-params]
int Method(int a, int const b, int& c, int* d)
^
const
./test.cpp:4:20: note: FIX-IT applied suggested code changes
int Method(int a, int const b, int& c, int* d)
^
clang-tidy applied 1 of 1 suggested fixes.
Пример 2: результат
struct Test
{
virtual int VirtualMethod(int a) = 0;
int Method(int a, int const b, int& c, int* d)
{
a = b;
return a;
}
};
Пример 2: результат
struct Test
{
virtual int VirtualMethod(int a) = 0;
int Method(int const a, int const b, int& c, int* d)
{
a = b;
return a;
}
};
Пример 3
// cxxRecordDecl(unless(matchesName("::[A-Z][a-zA-Z0-9]*$")))
class TestClass
{
public:
// cxxMethodDecl(unless(matchesName("::[A-Z][a-zA-Z0-9]*$")))
// parmVarDecl(unless(matchesName("::[a-z][a-zA-Z0-9]*$")))
void Method(int arg);
private:
// fieldDecl(unless(matchesName("::m_[a-z][a-zA-Z0-9]*$")),
// hasParent(cxxRecordDecl(isClass())))
int m_field;
};
struct TestStruct
{
// fieldDecl(unless(matchesName("::[A-Z][a-zA-Z0-9]*$")),
// hasParent(cxxRecordDecl(isStruct())))
int Field;
};
Пример 3
// varDecl(hasLocalStorage(), unless(matchesName("::[a-z][a-zA-Z0-9]*$")))
int localVariable;
// varDecl(hasGlobalStorage(),
// unless(anyOf(matchesName("::s_[a-z][a-zA-Z0-9]*$"), hasType(isConstQualified()))))
static int s_staticVariable;
// varDecl(hasGlobalStorage(),
// unless(anyOf(matchesName("::[A-Z][a-zA-Z0-9]*$"), unless(hasType(isConstQualified())))))
static int const Constant = 42;
Пример 4
Можно ли бросать исключение из
деструктора?
Пример 4
class Test
{
public:
~Test()
{
throw Exception();
}
};
// c++11: terminate
Пример 4
class Test
{
public:
~Test() noexcept(true)
{
throw Exception();
}
};
// c++11: terminate
Пример 4
class Test
{
public:
~Test() noexcept(false)
{
throw Exception();
}
};
Пример 4
class Test
{
public:
~Test() noexcept(false)
{
throw Exception();
}
};
int main()
{
std::unique_ptr<Test> test(new Test());
test.reset();
}
// c++11: terminate
Пример 4
class Parent
{
public:
~Parent() noexcept(false);
};
class Child : public Parent
{
public:
~Child()
{
}
};
int main()
{
std::unique_ptr<Child> test(new Child());
test.reset();
}
// c++11: terminate
Пример 4
class Parent
{
public:
~Parent() noexcept(false);
};
class Child : public Parent
{
public:
~Child() noexcept(true)
{
}
};
int main()
{
std::unique_ptr<Child> test(new Child());
test.reset();
}
// c++11: terminate
Пример 4
● Не бросать исключения из деструкторов
● Обозначить бросающие деструкторы
‘noexcept(false)’
● Не использовать ‘noexcept(true)’ для
деструкторов
Пример 4: реализация
void ExplicitThrowSpecificationCheck::registerMatchers(MatchFinder* finder)
{
finder->addMatcher(cxxDestructorDecl().bind("destructor"), this);
}
void ExplicitThrowSpecificationCheck::check(MatchFinder::MatchResult const& result)
{
FunctionDecl const& declaration = *result.Nodes.getNodeAs<FunctionDecl>("destructor");
FunctionProtoType const& prototype = *declaration.getType()->getAs<FunctionProtoType>();
bool const destructorCanThrow = prototype.getNoexceptSpec(*result.Context) == FunctionProtoType::NR_Throw;
bool const isSpecificationExplicit = declaration.getExceptionSpecSourceRange().isValid();
if (destructorCanThrow && !isSpecificationExplicit)
{
diag(declaration.getSourceRange().getEnd(), "This destructor should be marked with 'noexcept(false)'");
}
if (!destructorCanThrow && isSpecificationExplicit)
{
diag(declaration.getSourceRange().getEnd(), "Do not mark destructor with 'noexcept(true)'");
}
}
Пример 4: результат
class Parent
{
public:
~Parent() noexcept(false);
};
class Child : public Parent
{
public:
~Child() noexcept(true)
{
}
};
// do not use ‘noexept(true)’
Пример 4: результат
class Parent
{
public:
~Parent() noexcept(false);
};
class Child : public Parent
{
public:
~Child()
{
}
};
// can throw, mark with ‘noexcept(false)’
Пример 4: результат
class Parent
{
public:
~Parent() noexcept(false);
};
class Child : public Parent
{
public:
~Child() noexcept(false)
{
}
};
Пример 4
void Function();
struct Object
{
Object();
bool operator<(Object const& right) const;
};
struct Test
{
Test()
{
Object a, b;
if (a < b)
{
Function();
}
int* p = new int(42);
}
};
Пример 4: реализация
void ThrowFromDestructorCheck::registerMatchers(MatchFinder* finder)
{
finder->addMatcher(expr(hasAncestor(cxxDestructorDecl().bind("destructor"))).bind("expression"), this);
}
void ThrowFromDestructorCheck::check(MatchFinder::MatchResult const& result)
{
FunctionDecl const& declaration = *result.Nodes.getNodeAs<FunctionDecl>("destructor");
FunctionProtoType const& prototype = *declaration.getType()->getAs<FunctionProtoType>();
bool const destructorCanThrow = prototype.getNoexceptSpec(*result.Context) == FunctionProtoType::NR_Throw;
if (destructorCanThrow)
{
return;
}
Expr const& expression = *result.Nodes.getNodeAs<Expr>("expression");
bool const expressionCanThrow = m_compiler->getSema().canThrow(&expression) != CT_Cannot;
if (expressionCanThrow)
{
diag(expression.getExprLoc(), "Expression can throw exception");
}
}
Пример 4: результат
void Function();
struct Object
{
Object();
bool operator<(Object const& right) const;
};
struct Test
{
Test()
{
Object a, b;
if (a < b)
{
Function();
}
int* p = new int(42);
}
};
// can throw: constructor
// can throw: operator <
// can throw: Function call
// can throw: operator new
Пример 4: результат
void Function() noexcept(true);
struct Object
{
Object() noexcept(true);
bool operator<(Object const& right) const noexcept(true);
};
struct Test
{
Test()
{
Object a, b;
if (a < b)
{
Function();
}
}
};
Clang-tidy: итоги
Clang-tidy: итоги
● Простая реализация сложных проверок
● Автоматизация рутинных проверок
● Отличный способ лучше узнать C++
Полезные ссылки
http://clang.llvm.org/extra/clang-tidy
http://clang.llvm.org/docs/LibASTMatchersReference.html
http://clang.llvm.org/docs/IntroductionToTheClangAST.html
?
efimyury@gmail.com
yury.efimochev@solarwinds.com

Mais conteúdo relacionado

Mais procurados

Continuous integration with teamcity
Continuous integration with teamcityContinuous integration with teamcity
Continuous integration with teamcity
Eugene Sheretov
 
Linux Kernel Image
Linux Kernel ImageLinux Kernel Image
Linux Kernel Image
艾鍗科技
 

Mais procurados (20)

Continuous integration with teamcity
Continuous integration with teamcityContinuous integration with teamcity
Continuous integration with teamcity
 
Micro-frontends with Angular 10 (Modern Web 2020)
Micro-frontends with Angular 10 (Modern Web 2020)Micro-frontends with Angular 10 (Modern Web 2020)
Micro-frontends with Angular 10 (Modern Web 2020)
 
Jenkins
JenkinsJenkins
Jenkins
 
GNU ld的linker script簡介
GNU ld的linker script簡介GNU ld的linker script簡介
GNU ld的linker script簡介
 
[GuideDoc] Deploy EKS thru eksctl - v1.22_v0.105.0.pdf
[GuideDoc] Deploy EKS thru eksctl - v1.22_v0.105.0.pdf[GuideDoc] Deploy EKS thru eksctl - v1.22_v0.105.0.pdf
[GuideDoc] Deploy EKS thru eksctl - v1.22_v0.105.0.pdf
 
Postman과 Newman을 이용한 RestAPI 테스트 자동화 가이드
Postman과 Newman을 이용한 RestAPI 테스트 자동화 가이드 Postman과 Newman을 이용한 RestAPI 테스트 자동화 가이드
Postman과 Newman을 이용한 RestAPI 테스트 자동화 가이드
 
Xilinx Vitis FreeRTOS Hello World
Xilinx Vitis FreeRTOS Hello WorldXilinx Vitis FreeRTOS Hello World
Xilinx Vitis FreeRTOS Hello World
 
왜 쿠버네티스는 systemd로 cgroup을 관리하려고 할까요
왜 쿠버네티스는 systemd로 cgroup을 관리하려고 할까요왜 쿠버네티스는 systemd로 cgroup을 관리하려고 할까요
왜 쿠버네티스는 systemd로 cgroup을 관리하려고 할까요
 
The Internals of "Hello World" Program
The Internals of "Hello World" ProgramThe Internals of "Hello World" Program
The Internals of "Hello World" Program
 
Browsing Linux Kernel Source
Browsing Linux Kernel SourceBrowsing Linux Kernel Source
Browsing Linux Kernel Source
 
BKK16-208 EAS
BKK16-208 EASBKK16-208 EAS
BKK16-208 EAS
 
CMake - Introduction and best practices
CMake - Introduction and best practicesCMake - Introduction and best practices
CMake - Introduction and best practices
 
Q2.12: Debugging with GDB
Q2.12: Debugging with GDBQ2.12: Debugging with GDB
Q2.12: Debugging with GDB
 
Working Remotely (via SSH) Rocks!
Working Remotely (via SSH) Rocks!Working Remotely (via SSH) Rocks!
Working Remotely (via SSH) Rocks!
 
Linux Kernel Image
Linux Kernel ImageLinux Kernel Image
Linux Kernel Image
 
Continuous integration
Continuous integrationContinuous integration
Continuous integration
 
What is Jenkins | Jenkins Tutorial for Beginners | Edureka
What is Jenkins | Jenkins Tutorial for Beginners | EdurekaWhat is Jenkins | Jenkins Tutorial for Beginners | Edureka
What is Jenkins | Jenkins Tutorial for Beginners | Edureka
 
Ansible Introduction
Ansible IntroductionAnsible Introduction
Ansible Introduction
 
Understanding eBPF in a Hurry!
Understanding eBPF in a Hurry!Understanding eBPF in a Hurry!
Understanding eBPF in a Hurry!
 
Linux 4.x Tracing: Performance Analysis with bcc/BPF
Linux 4.x Tracing: Performance Analysis with bcc/BPFLinux 4.x Tracing: Performance Analysis with bcc/BPF
Linux 4.x Tracing: Performance Analysis with bcc/BPF
 

Semelhante a Clang tidy

Semelhante a Clang tidy (20)

Story of static code analyzer development
Story of static code analyzer developmentStory of static code analyzer development
Story of static code analyzer development
 
Analysis of Haiku Operating System (BeOS Family) by PVS-Studio. Part 2
Analysis of Haiku Operating System (BeOS Family) by PVS-Studio. Part 2Analysis of Haiku Operating System (BeOS Family) by PVS-Studio. Part 2
Analysis of Haiku Operating System (BeOS Family) by PVS-Studio. Part 2
 
The Unicorn's Travel to the Microcosm
The Unicorn's Travel to the MicrocosmThe Unicorn's Travel to the Microcosm
The Unicorn's Travel to the Microcosm
 
Analysis of Microsoft Code Contracts
Analysis of Microsoft Code ContractsAnalysis of Microsoft Code Contracts
Analysis of Microsoft Code Contracts
 
The operation principles of PVS-Studio static code analyzer
The operation principles of PVS-Studio static code analyzerThe operation principles of PVS-Studio static code analyzer
The operation principles of PVS-Studio static code analyzer
 
How Data Flow analysis works in a static code analyzer
How Data Flow analysis works in a static code analyzerHow Data Flow analysis works in a static code analyzer
How Data Flow analysis works in a static code analyzer
 
The CppCat Analyzer Checks TortoiseGit
The CppCat Analyzer Checks TortoiseGitThe CppCat Analyzer Checks TortoiseGit
The CppCat Analyzer Checks TortoiseGit
 
Nyc open-data-2015-andvanced-sklearn-expanded
Nyc open-data-2015-andvanced-sklearn-expandedNyc open-data-2015-andvanced-sklearn-expanded
Nyc open-data-2015-andvanced-sklearn-expanded
 
Basic c++ 11/14 for python programmers
Basic c++ 11/14 for python programmersBasic c++ 11/14 for python programmers
Basic c++ 11/14 for python programmers
 
Alexey Tsoy Meta Programming in C++ 16.11.17
Alexey Tsoy Meta Programming in C++ 16.11.17Alexey Tsoy Meta Programming in C++ 16.11.17
Alexey Tsoy Meta Programming in C++ 16.11.17
 
Let's talks about string operations in C++17
Let's talks about string operations in C++17Let's talks about string operations in C++17
Let's talks about string operations in C++17
 
Chainer-Compiler 動かしてみた
Chainer-Compiler 動かしてみたChainer-Compiler 動かしてみた
Chainer-Compiler 動かしてみた
 
PVS-Studio team experience: checking various open source projects, or mistake...
PVS-Studio team experience: checking various open source projects, or mistake...PVS-Studio team experience: checking various open source projects, or mistake...
PVS-Studio team experience: checking various open source projects, or mistake...
 
Tesseract. Recognizing Errors in Recognition Software
Tesseract. Recognizing Errors in Recognition SoftwareTesseract. Recognizing Errors in Recognition Software
Tesseract. Recognizing Errors in Recognition Software
 
Manipulators in c++
Manipulators in c++Manipulators in c++
Manipulators in c++
 
C language
C languageC language
C language
 
Anomalies in X-Ray Engine
Anomalies in X-Ray EngineAnomalies in X-Ray Engine
Anomalies in X-Ray Engine
 
Basic C++ 11/14 for Python Programmers
Basic C++ 11/14 for Python ProgrammersBasic C++ 11/14 for Python Programmers
Basic C++ 11/14 for Python Programmers
 
Semmle Codeql
Semmle Codeql Semmle Codeql
Semmle Codeql
 
Checking the Cross-Platform Framework Cocos2d-x
Checking the Cross-Platform Framework Cocos2d-xChecking the Cross-Platform Framework Cocos2d-x
Checking the Cross-Platform Framework Cocos2d-x
 

Último

UNIT-V FMM.HYDRAULIC TURBINE - Construction and working
UNIT-V FMM.HYDRAULIC TURBINE - Construction and workingUNIT-V FMM.HYDRAULIC TURBINE - Construction and working
UNIT-V FMM.HYDRAULIC TURBINE - Construction and working
rknatarajan
 
Call for Papers - Educational Administration: Theory and Practice, E-ISSN: 21...
Call for Papers - Educational Administration: Theory and Practice, E-ISSN: 21...Call for Papers - Educational Administration: Theory and Practice, E-ISSN: 21...
Call for Papers - Educational Administration: Theory and Practice, E-ISSN: 21...
Christo Ananth
 
Call Girls in Ramesh Nagar Delhi 💯 Call Us 🔝9953056974 🔝 Escort Service
Call Girls in Ramesh Nagar Delhi 💯 Call Us 🔝9953056974 🔝 Escort ServiceCall Girls in Ramesh Nagar Delhi 💯 Call Us 🔝9953056974 🔝 Escort Service
Call Girls in Ramesh Nagar Delhi 💯 Call Us 🔝9953056974 🔝 Escort Service
9953056974 Low Rate Call Girls In Saket, Delhi NCR
 
VIP Call Girls Ankleshwar 7001035870 Whatsapp Number, 24/07 Booking
VIP Call Girls Ankleshwar 7001035870 Whatsapp Number, 24/07 BookingVIP Call Girls Ankleshwar 7001035870 Whatsapp Number, 24/07 Booking
VIP Call Girls Ankleshwar 7001035870 Whatsapp Number, 24/07 Booking
dharasingh5698
 
Call for Papers - African Journal of Biological Sciences, E-ISSN: 2663-2187, ...
Call for Papers - African Journal of Biological Sciences, E-ISSN: 2663-2187, ...Call for Papers - African Journal of Biological Sciences, E-ISSN: 2663-2187, ...
Call for Papers - African Journal of Biological Sciences, E-ISSN: 2663-2187, ...
Christo Ananth
 

Último (20)

Booking open Available Pune Call Girls Pargaon 6297143586 Call Hot Indian Gi...
Booking open Available Pune Call Girls Pargaon  6297143586 Call Hot Indian Gi...Booking open Available Pune Call Girls Pargaon  6297143586 Call Hot Indian Gi...
Booking open Available Pune Call Girls Pargaon 6297143586 Call Hot Indian Gi...
 
Glass Ceramics: Processing and Properties
Glass Ceramics: Processing and PropertiesGlass Ceramics: Processing and Properties
Glass Ceramics: Processing and Properties
 
Java Programming :Event Handling(Types of Events)
Java Programming :Event Handling(Types of Events)Java Programming :Event Handling(Types of Events)
Java Programming :Event Handling(Types of Events)
 
UNIT-V FMM.HYDRAULIC TURBINE - Construction and working
UNIT-V FMM.HYDRAULIC TURBINE - Construction and workingUNIT-V FMM.HYDRAULIC TURBINE - Construction and working
UNIT-V FMM.HYDRAULIC TURBINE - Construction and working
 
ONLINE FOOD ORDER SYSTEM PROJECT REPORT.pdf
ONLINE FOOD ORDER SYSTEM PROJECT REPORT.pdfONLINE FOOD ORDER SYSTEM PROJECT REPORT.pdf
ONLINE FOOD ORDER SYSTEM PROJECT REPORT.pdf
 
(INDIRA) Call Girl Aurangabad Call Now 8617697112 Aurangabad Escorts 24x7
(INDIRA) Call Girl Aurangabad Call Now 8617697112 Aurangabad Escorts 24x7(INDIRA) Call Girl Aurangabad Call Now 8617697112 Aurangabad Escorts 24x7
(INDIRA) Call Girl Aurangabad Call Now 8617697112 Aurangabad Escorts 24x7
 
Coefficient of Thermal Expansion and their Importance.pptx
Coefficient of Thermal Expansion and their Importance.pptxCoefficient of Thermal Expansion and their Importance.pptx
Coefficient of Thermal Expansion and their Importance.pptx
 
data_management_and _data_science_cheat_sheet.pdf
data_management_and _data_science_cheat_sheet.pdfdata_management_and _data_science_cheat_sheet.pdf
data_management_and _data_science_cheat_sheet.pdf
 
Intze Overhead Water Tank Design by Working Stress - IS Method.pdf
Intze Overhead Water Tank  Design by Working Stress - IS Method.pdfIntze Overhead Water Tank  Design by Working Stress - IS Method.pdf
Intze Overhead Water Tank Design by Working Stress - IS Method.pdf
 
UNIT-II FMM-Flow Through Circular Conduits
UNIT-II FMM-Flow Through Circular ConduitsUNIT-II FMM-Flow Through Circular Conduits
UNIT-II FMM-Flow Through Circular Conduits
 
BSides Seattle 2024 - Stopping Ethan Hunt From Taking Your Data.pptx
BSides Seattle 2024 - Stopping Ethan Hunt From Taking Your Data.pptxBSides Seattle 2024 - Stopping Ethan Hunt From Taking Your Data.pptx
BSides Seattle 2024 - Stopping Ethan Hunt From Taking Your Data.pptx
 
The Most Attractive Pune Call Girls Manchar 8250192130 Will You Miss This Cha...
The Most Attractive Pune Call Girls Manchar 8250192130 Will You Miss This Cha...The Most Attractive Pune Call Girls Manchar 8250192130 Will You Miss This Cha...
The Most Attractive Pune Call Girls Manchar 8250192130 Will You Miss This Cha...
 
Call Girls Pimpri Chinchwad Call Me 7737669865 Budget Friendly No Advance Boo...
Call Girls Pimpri Chinchwad Call Me 7737669865 Budget Friendly No Advance Boo...Call Girls Pimpri Chinchwad Call Me 7737669865 Budget Friendly No Advance Boo...
Call Girls Pimpri Chinchwad Call Me 7737669865 Budget Friendly No Advance Boo...
 
Call for Papers - Educational Administration: Theory and Practice, E-ISSN: 21...
Call for Papers - Educational Administration: Theory and Practice, E-ISSN: 21...Call for Papers - Educational Administration: Theory and Practice, E-ISSN: 21...
Call for Papers - Educational Administration: Theory and Practice, E-ISSN: 21...
 
Call Girls Walvekar Nagar Call Me 7737669865 Budget Friendly No Advance Booking
Call Girls Walvekar Nagar Call Me 7737669865 Budget Friendly No Advance BookingCall Girls Walvekar Nagar Call Me 7737669865 Budget Friendly No Advance Booking
Call Girls Walvekar Nagar Call Me 7737669865 Budget Friendly No Advance Booking
 
Call Girls in Ramesh Nagar Delhi 💯 Call Us 🔝9953056974 🔝 Escort Service
Call Girls in Ramesh Nagar Delhi 💯 Call Us 🔝9953056974 🔝 Escort ServiceCall Girls in Ramesh Nagar Delhi 💯 Call Us 🔝9953056974 🔝 Escort Service
Call Girls in Ramesh Nagar Delhi 💯 Call Us 🔝9953056974 🔝 Escort Service
 
Thermal Engineering Unit - I & II . ppt
Thermal Engineering  Unit - I & II . pptThermal Engineering  Unit - I & II . ppt
Thermal Engineering Unit - I & II . ppt
 
VIP Call Girls Ankleshwar 7001035870 Whatsapp Number, 24/07 Booking
VIP Call Girls Ankleshwar 7001035870 Whatsapp Number, 24/07 BookingVIP Call Girls Ankleshwar 7001035870 Whatsapp Number, 24/07 Booking
VIP Call Girls Ankleshwar 7001035870 Whatsapp Number, 24/07 Booking
 
Call for Papers - African Journal of Biological Sciences, E-ISSN: 2663-2187, ...
Call for Papers - African Journal of Biological Sciences, E-ISSN: 2663-2187, ...Call for Papers - African Journal of Biological Sciences, E-ISSN: 2663-2187, ...
Call for Papers - African Journal of Biological Sciences, E-ISSN: 2663-2187, ...
 
Booking open Available Pune Call Girls Koregaon Park 6297143586 Call Hot Ind...
Booking open Available Pune Call Girls Koregaon Park  6297143586 Call Hot Ind...Booking open Available Pune Call Girls Koregaon Park  6297143586 Call Hot Ind...
Booking open Available Pune Call Girls Koregaon Park 6297143586 Call Hot Ind...
 

Clang tidy

  • 3. Что такое clang-tidy? Инструмент для статического анализа кода и поиска типичных ошибок программирования
  • 4. Встроенные правила(~200) ● ClangAnalyzer ● Readability/Modernize/Performance ● CppCoreGuidelines ● LLVM/Google-style
  • 5. Пример запуска class TestClass { public: TestClass() : m_B(), m_C() { } private: int m_A; int m_B; int m_C; };
  • 6. Пример запуска $ clang-tidy -checks="cppcoreguidelines-*" ./test.cpp 1 warning generated. ./test.cpp:4:5: warning: constructor does not initialize these fields: m_A [cppcoreguidelines-pro-type-member-init] TestClass() : ^ m_A(),
  • 8. Цели code review ● Соответствие реализации задаче ● Поиск ошибок и неоптимальностей реализации ● Соответствие guidelines и best practices
  • 9. Clang-tidy и code review? Платформа для построения собственных инструментов статического анализа кода
  • 10. Расширяемость ● Полный доступ к AST и препроцессору ● Модульная архитектура ● Инфраструктура для разработки и тестирования
  • 11. Что необходимо для использования? ● Код должен собираться clang ● Необходима compilation database
  • 12. Алгоритм разработки правила 1. Подготовить пример 2. Посмотреть в AST 3. ??? 4. Profit
  • 13. Пример 1 #include <iostream> class TestClass { public: static int const Constant; int m_public; private: int m_private; }; struct TestStruct { int Field; }; // style: class public field
  • 14. Clang-check: визуализация AST $ clang-check -ast-dump -ast-dump-filter="Test" ./test.cpp Dumping TestClass: CXXRecordDecl 0xaf665da8 <test.cpp:3:1, line:12:1> line:3:7 class TestClass definition |-CXXRecordDecl 0xaf665ec0 <col:1, col:7> col:7 implicit class TestClass |-AccessSpecDecl 0xaf665f50 <line:5:1, col:7> col:1 public |-VarDecl 0xaf665f88 <line:6:5, col:22> col:22 Constant 'const int' static |-FieldDecl 0xaf665ff8 <line:8:5, col:9> col:9 m_public 'int' |-AccessSpecDecl 0xaf666040 <line:10:1, col:8> col:1 private `-FieldDecl 0xaf666078 <line:11:5, col:9> col:9 m_private 'int' Dumping TestStruct: CXXRecordDecl 0xaf6660c0 <test.cpp:14:1, line:17:1> line:14:8 struct TestStruct definition |-CXXRecordDecl 0xaf6661d0 <col:1, col:8> col:8 implicit struct TestStruct `-FieldDecl 0xaf666270 <line:16:5, col:9> col:9 Field 'int'
  • 15. AST Nodes ● Decl ● Stmt ● Type
  • 17. AST Nodes ● Decl ● Stmt ifStmt CXXTryStmt BinaryOperator ● Type
  • 18. AST Nodes ● Decl ● Stmt ● Type PointerType ReferenceType LValueReferenceType
  • 19. Пример 1: AST $ clang-check -ast-dump -ast-dump-filter="Test" ./test.cpp Dumping TestClass: CXXRecordDecl 0xaf665da8 <test.cpp:3:1, line:12:1> line:3:7 class TestClass definition |-CXXRecordDecl 0xaf665ec0 <col:1, col:7> col:7 implicit class TestClass |-AccessSpecDecl 0xaf665f50 <line:5:1, col:7> col:1 public |-VarDecl 0xaf665f88 <line:6:5, col:22> col:22 Constant 'const int' static |-FieldDecl 0xaf665ff8 <line:8:5, col:9> col:9 m_public 'int' |-AccessSpecDecl 0xaf666040 <line:10:1, col:8> col:1 private `-FieldDecl 0xaf666078 <line:11:5, col:9> col:9 m_private 'int' Dumping TestStruct: CXXRecordDecl 0xaf6660c0 <test.cpp:14:1, line:17:1> line:14:8 struct TestStruct definition |-CXXRecordDecl 0xaf6661d0 <col:1, col:8> col:8 implicit struct TestStruct `-FieldDecl 0xaf666270 <line:16:5, col:9> col:9 Field 'int'
  • 20. AST Matchers ● Node Matchers cxxRecordDecl, cxxMethodDecl, namespaceDecl, ifStmt, ... ● Narrowing Matchers isConstant, isFinal, hasName, matchesName, unless, ... ● Traversal Matchers hasDescendant, hasParent, hasBody, ... cxxMethodDecl(matchesName(“Get.*”), hasParent(cxxRecordDecl(isStruct()))
  • 21. Пример 1 clang-query> match fieldDecl() Match #1: /home/yury/Projects/test.cpp:8:5: note: "root" binds here int m_public; ^~~~~~~~~~~~ Match #2: /home/yury/Projects/test.cpp:11:5: note: "root" binds here int m_private; ^~~~~~~~~~~~~ Match #3: /home/yury/Projects/test.cpp:16:5: note: "root" binds here int Field; ^~~~~~~~~ 3 matches.
  • 22. Пример 1 clang-query> match fieldDecl(isPublic()) Match #1: /home/yury/Projects/test.cpp:8:5: note: "root" binds here int m_public; ^~~~~~~~~~~~ Match #2: /home/yury/Projects/test.cpp:16:5: note: "root" binds here int Field; ^~~~~~~~~ 2 matches.
  • 23. Пример 1 clang-query> match fieldDecl(isPublic(), hasParent(cxxRecordDecl(isClass()))) Match #1: /home/yury/Projects/test.cpp:8:5: note: "root" binds here int m_public; ^~~~~~~~~~~~ 1 match.
  • 24. Добавление правила $ ./add_new_check.py misc field-visibility Updating ./misc/CMakeLists.txt... Creating ./misc/FieldVisibilityCheck.h... Creating ./misc/FieldVisibilityCheck.cpp... Updating ./misc/MiscTidyModule.cpp... Creating ../test/clang-tidy/misc-field-visibility.cpp... Creating ../docs/clang-tidy/checks/misc-field-visibility.rst... Updating ../docs/clang-tidy/checks/list.rst... Done. Now it's your turn!
  • 25. Пример 1: шаблон реализации class FieldVisibilityCheck : public ClangTidyCheck { public: FieldVisibilityCheck(StringRef name, ClangTidyContext* context) : ClangTidyCheck(name, context) { } private: void registerMatchers(ast_matchers::MatchFinder* finder) override { } void check(ast_matchers::MatchFinder::MatchResult const& result) override { } };
  • 26. Пример 1: реализация class FieldVisibilityCheck : public ClangTidyCheck { public: FieldVisibilityCheck(StringRef name, ClangTidyContext* context) : ClangTidyCheck(name, context) { } private: void registerMatchers(ast_matchers::MatchFinder* finder) override { finder->addMatcher(fieldDecl(isPublic(), hasParent(cxxRecordDecl(isClass()))).bind("field"), this); } void check(ast_matchers::MatchFinder::MatchResult const& result) override { FieldDecl const& field = *result.Nodes.getNodeAs<FieldDecl const>("field"); diag(field.getLocStart(), "Class field should be private"); } };
  • 27. Пример 1: результат $ clang-tidy -checks="misc-field-visibility-check" ./test.cpp 1 warning generated. ./test.cpp:8:5: warning: Class field should be private [misc-field-visibility-check] int m_public; ^
  • 28. Пример 2 int Function(int a) { // ... a = something; // ... return a; } // style: mutable parameter
  • 29. Пример 2 struct Test { virtual int VirtualMethod(int a) = 0; int Method(int a, int const b, int& c, int* d) { a = b; return a; } };
  • 30. Пример 2: AST Dumping Test: CXXRecordDecl 0x46b1b20 <test.cpp:1:1, line:9:1> line:1:8 struct Test definition |-CXXRecordDecl 0x46b1c30 <col:1, col:8> col:8 implicit struct Test |-CXXMethodDecl 0x46b1d90 <line:3:5, col:32> col:9 VirtualMethod 'int (int)' | `-ParmVarDecl 0x46b1cd0 <col:23, col:27> col:27 a 'int' `-CXXMethodDecl 0x46b2140 <line:4:5, line:8:5> line:4:9 Method 'int (int, const int, int &, int *)' |-ParmVarDecl 0x46b1e50 <col:16, col:20> col:20 used a 'int' |-ParmVarDecl 0x46b1ec0 <col:23, col:33> col:33 used b 'const int' |-ParmVarDecl 0x46b1f60 <col:36, col:41> col:41 c 'int &' |-ParmVarDecl 0x46b2000 <col:44, col:49> col:49 d 'int *' `-CompoundStmt 0x46b2320 <line:5:5, line:8:5> |-BinaryOperator 0x46b22a0 <line:6:9, col:13> 'int' lvalue '=' | |-DeclRefExpr 0x46b2238 <col:9> 'int' lvalue ParmVar 0x46b1e50 'a' 'int' | `-ImplicitCastExpr 0x46b2288 <col:13> 'int' <LValueToRValue> | `-DeclRefExpr 0x46b2260 <col:13> 'const int' lvalue ParmVar 0x46b1ec0 'b' 'const int' `-ReturnStmt 0x46b2308 <line:7:9, col:16> `-ImplicitCastExpr 0x46b22f0 <col:16> 'int' <LValueToRValue> `-DeclRefExpr 0x46b22c8 <col:16> 'int' lvalue ParmVar 0x46b1e50 'a' 'int'
  • 31. Пример 2: реализация void ImmutableParamsCheck::registerMatchers(MatchFinder* finder) { finder->addMatcher( parmVarDecl( hasAncestor(functionDecl(hasBody(stmt()))), unless(anyOf( hasType(isConstQualified()), hasType(referenceType()), hasType(pointerType())))).bind("parameter"), this); } void ImmutableParamsCheck::check(MatchFinder::MatchResult const& result) { ParmVarDecl const& parameter = *result.Nodes.getNodeAs<ParmVarDecl const>("parameter"); SourceLocation const location = parameter.getSourceRange().getEnd(); diag(location, "Consider making constant") << FixItHint::CreateInsertion(location, "const "); }
  • 32. Пример 2: результат $ clang-tidy -checks="misc-immutable-parameters-check" -fix ./test.cpp 2 warnings generated. ./test.cpp:4:20: warning: Consider making constant [iaso-immutable-params] int Method(int a, int const b, int& c, int* d) ^ const ./test.cpp:4:20: note: FIX-IT applied suggested code changes int Method(int a, int const b, int& c, int* d) ^ clang-tidy applied 1 of 1 suggested fixes.
  • 33. Пример 2: результат struct Test { virtual int VirtualMethod(int a) = 0; int Method(int a, int const b, int& c, int* d) { a = b; return a; } };
  • 34. Пример 2: результат struct Test { virtual int VirtualMethod(int a) = 0; int Method(int const a, int const b, int& c, int* d) { a = b; return a; } };
  • 35. Пример 3 // cxxRecordDecl(unless(matchesName("::[A-Z][a-zA-Z0-9]*$"))) class TestClass { public: // cxxMethodDecl(unless(matchesName("::[A-Z][a-zA-Z0-9]*$"))) // parmVarDecl(unless(matchesName("::[a-z][a-zA-Z0-9]*$"))) void Method(int arg); private: // fieldDecl(unless(matchesName("::m_[a-z][a-zA-Z0-9]*$")), // hasParent(cxxRecordDecl(isClass()))) int m_field; }; struct TestStruct { // fieldDecl(unless(matchesName("::[A-Z][a-zA-Z0-9]*$")), // hasParent(cxxRecordDecl(isStruct()))) int Field; };
  • 36. Пример 3 // varDecl(hasLocalStorage(), unless(matchesName("::[a-z][a-zA-Z0-9]*$"))) int localVariable; // varDecl(hasGlobalStorage(), // unless(anyOf(matchesName("::s_[a-z][a-zA-Z0-9]*$"), hasType(isConstQualified())))) static int s_staticVariable; // varDecl(hasGlobalStorage(), // unless(anyOf(matchesName("::[A-Z][a-zA-Z0-9]*$"), unless(hasType(isConstQualified()))))) static int const Constant = 42;
  • 37. Пример 4 Можно ли бросать исключение из деструктора?
  • 38. Пример 4 class Test { public: ~Test() { throw Exception(); } }; // c++11: terminate
  • 39. Пример 4 class Test { public: ~Test() noexcept(true) { throw Exception(); } }; // c++11: terminate
  • 40. Пример 4 class Test { public: ~Test() noexcept(false) { throw Exception(); } };
  • 41. Пример 4 class Test { public: ~Test() noexcept(false) { throw Exception(); } }; int main() { std::unique_ptr<Test> test(new Test()); test.reset(); } // c++11: terminate
  • 42. Пример 4 class Parent { public: ~Parent() noexcept(false); }; class Child : public Parent { public: ~Child() { } }; int main() { std::unique_ptr<Child> test(new Child()); test.reset(); } // c++11: terminate
  • 43. Пример 4 class Parent { public: ~Parent() noexcept(false); }; class Child : public Parent { public: ~Child() noexcept(true) { } }; int main() { std::unique_ptr<Child> test(new Child()); test.reset(); } // c++11: terminate
  • 44. Пример 4 ● Не бросать исключения из деструкторов ● Обозначить бросающие деструкторы ‘noexcept(false)’ ● Не использовать ‘noexcept(true)’ для деструкторов
  • 45. Пример 4: реализация void ExplicitThrowSpecificationCheck::registerMatchers(MatchFinder* finder) { finder->addMatcher(cxxDestructorDecl().bind("destructor"), this); } void ExplicitThrowSpecificationCheck::check(MatchFinder::MatchResult const& result) { FunctionDecl const& declaration = *result.Nodes.getNodeAs<FunctionDecl>("destructor"); FunctionProtoType const& prototype = *declaration.getType()->getAs<FunctionProtoType>(); bool const destructorCanThrow = prototype.getNoexceptSpec(*result.Context) == FunctionProtoType::NR_Throw; bool const isSpecificationExplicit = declaration.getExceptionSpecSourceRange().isValid(); if (destructorCanThrow && !isSpecificationExplicit) { diag(declaration.getSourceRange().getEnd(), "This destructor should be marked with 'noexcept(false)'"); } if (!destructorCanThrow && isSpecificationExplicit) { diag(declaration.getSourceRange().getEnd(), "Do not mark destructor with 'noexcept(true)'"); } }
  • 46. Пример 4: результат class Parent { public: ~Parent() noexcept(false); }; class Child : public Parent { public: ~Child() noexcept(true) { } }; // do not use ‘noexept(true)’
  • 47. Пример 4: результат class Parent { public: ~Parent() noexcept(false); }; class Child : public Parent { public: ~Child() { } }; // can throw, mark with ‘noexcept(false)’
  • 48. Пример 4: результат class Parent { public: ~Parent() noexcept(false); }; class Child : public Parent { public: ~Child() noexcept(false) { } };
  • 49. Пример 4 void Function(); struct Object { Object(); bool operator<(Object const& right) const; }; struct Test { Test() { Object a, b; if (a < b) { Function(); } int* p = new int(42); } };
  • 50. Пример 4: реализация void ThrowFromDestructorCheck::registerMatchers(MatchFinder* finder) { finder->addMatcher(expr(hasAncestor(cxxDestructorDecl().bind("destructor"))).bind("expression"), this); } void ThrowFromDestructorCheck::check(MatchFinder::MatchResult const& result) { FunctionDecl const& declaration = *result.Nodes.getNodeAs<FunctionDecl>("destructor"); FunctionProtoType const& prototype = *declaration.getType()->getAs<FunctionProtoType>(); bool const destructorCanThrow = prototype.getNoexceptSpec(*result.Context) == FunctionProtoType::NR_Throw; if (destructorCanThrow) { return; } Expr const& expression = *result.Nodes.getNodeAs<Expr>("expression"); bool const expressionCanThrow = m_compiler->getSema().canThrow(&expression) != CT_Cannot; if (expressionCanThrow) { diag(expression.getExprLoc(), "Expression can throw exception"); } }
  • 51. Пример 4: результат void Function(); struct Object { Object(); bool operator<(Object const& right) const; }; struct Test { Test() { Object a, b; if (a < b) { Function(); } int* p = new int(42); } }; // can throw: constructor // can throw: operator < // can throw: Function call // can throw: operator new
  • 52. Пример 4: результат void Function() noexcept(true); struct Object { Object() noexcept(true); bool operator<(Object const& right) const noexcept(true); }; struct Test { Test() { Object a, b; if (a < b) { Function(); } } };
  • 54. Clang-tidy: итоги ● Простая реализация сложных проверок ● Автоматизация рутинных проверок ● Отличный способ лучше узнать C++