iOS开发 - OCLint的自定义规则编写
文章目录
通过Xcode显示Warning
方案的选择
前面已经说过如何添加自定义的规则rule以及Xcode调试:iOS开发 - OCLint自定义规则的编译与Xcode调试
一般来说,我们都有这样的需求:想在写代码的时候,就能分析出代码问题,并及时进行修改。虽然OCLint
能够分析单个文件,但没有Xcode插件
,像 Analyze 这样的实时分析可能并不好实现,这里我们就选择编译一次然后在项目中提示出所有的问题。
有如下方案:
-
在主工程中添加
Run Script
这个方案并不可取,原因有:
1.Warning
容易与原工程的Warning
混淆,没法区分是 Analyze 还是OCLint
的Warning。
2.每次编译都会运行代码检测,编译时间会被拉长,有时候你仅仅只需要编译,不需要代码分析。 -
通过
Aggregate
添加脚本
这样子能和编译分离,避免每次都进行代码分析。
OK,我们就采取 Aggregate
添加脚本的方式。
简易Rule编写
上篇文章中没有提及自定义规则的编写,这里先编写一个简单的检测rule
,把流程走通,然后在进行全面的rule
编写:
在定义的自定义rule
文件中选取 VisitObjCMethodDecl
进行编写一个检测方法首字母的rule:
//表示这个rule的名称,叫什么
virtual const string name() const override
{
return "ObjCMethodCapitalBegin";
}
//优先级,分为P1 P2 P3,如果不进行任何设置,P1是不允许的,P2允许10次,P3允许20次,超过的话OCLint就会报错。当然这些限值可以修改
virtual int priority() const override
{
return 3;
}
//区分这个Waring种类是哪种类型,一般自行创建的会设置为custom
virtual const string category() const override
{
return "growing";
}
#ifdef DOCGEN
virtual const std::string since() const override
{
return "0.15";
}
virtual const std::string description() const override
{
return ""; // TODO: fill in the description of the rule.
}
virtual const std::string example() const override
{
return R"rst(
.. code-block:: cpp
void example()
{
// TODO: modify the example for this rule.
}
)rst";
}
bool VisitObjCMethodDecl(ObjCMethodDecl *node)
{
//检查名称的每部分,都不允许以大写字母开头
Selector sel = node -> getSelector();
string selectorName = sel.getAsString();
int selectorPartCount = node -> getNumSelectorLocs();
for (int i = 0; i < selectorPartCount; i++) {
// 方法参数名称
StringRef selName = sel.getNameForSlot(i);
if (selName.size() == 0) {
return true;
}
char c = selName[0];
if (isUppercase(c)) {
// 提示
// 获取将要报错的位置
SourceLocation loc = node->getSelectorLoc(i);
string message = "方法名/方法参数: \'" + selectorName + "\' 不能以大写开头";
addViolation(loc, loc, this, message);
}
if (c == '_') {
// 提示
// 获取将要报错的位置
SourceLocation loc = node->getSelectorLoc(i);
string message = "方法名/方法参数: \'" + selectorName + "\' 不要以下划线开头";
addViolation(loc, loc, this, message);
}
}
return true;
}
将下图中的启动参数修改
修改为:
-R=/Users/sheng/Documents/Caicai/OCLint/oclint/oclint-xcoderules/rules.dl/Debug /Users/sheng/Documents/Caicai/OCLint/oclint/demo/Test.m -- -x objective-c -isystem /Users/sheng/Documents/Caicai/OCLint/oclint/build/oclint-release/lib/clang/10.0.0/include -iframework /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks -isystem /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/include
这会使得输入到Xcode
的控制台,然后Run
进行调试,输出为:
OCLint Report
Summary: TotalFiles=1 FilesWithViolations=1 P1=0 P2=0 P3=5
/Users/sheng/Documents/Caicai/OCLint/oclint/demo/Test.m:16:28: unused method parameter [unused|P3] The parameter '_name' is unused.
/Users/sheng/Documents/Caicai/OCLint/oclint/demo/Test.m:6:5: short variable name [naming|P3] Length of variable name `i` is 1, which is shorter than the threshold of 3
/Users/sheng/Documents/Caicai/OCLint/oclint/demo/Test.m:16:14: Growing Code Warning [growing|P3] 方法名/方法参数: 'JsonFromFile:' 不能以大写开头
/Users/sheng/Documents/Caicai/OCLint/oclint/demo/Test.m:3:1: long variable name [naming|P3] Length of variable name `kGrowingNormalEventPath` is 23, which is longer than the threshold of 20
/Users/sheng/Documents/Caicai/OCLint/oclint/demo/Test.m:6:5: unused local variable [unused|P3] The local variable 'i' is unused.
[OCLint (http://oclint.org) v0.15]
Program ended with exit code: 0
然后生成dylib
库,这里选择 ALL_BUILD
生成所有release格式的dylib
。
如果无法生成所有的,去ALL_BUILD的Build Phases->Dependencies添加所有的dylib即可。
然后获取到输出路径 /Volumes/CaiCai/OCLint/oclint/oclint-xcoderules/rules.dl/Release
brew安装
由于给公司小伙伴使用,当然是最简单最好,拉源码拷贝配置的方式太LOW
了,也太麻烦,小伙伴当然想着步骤越少越好。我们使用brew
安装OCLint
,然后进行添加dylib
,这样的工作量比较少了。
尝试了一下发现brew
上oclint
版本为0.13
,我在0.15
上编译的dylib
通过0.13
编译报错,可能是Clang
版本不一致的原因,考虑到需要修改其内容,不如自己做一套。
https://blog.csdn.net/shengpeng3344/article/details/107378382
Aggregate 添加脚本
添加 Aggregate
,并添加脚本
source ~/.bash_profile
cd ${SRCROOT}
xcodebuild clean
#build xcodebuild.log
xcodebuild | xcpretty -r json-compilation-database -o compile_commands.json
oclint-json-compilation-database -e Pods -v -- -report-type xcode -rc LONG_LINE=1000 -rc LONG_VARIABLE_NAME=100 -max-priority-1=9999 -max-priority-2=9999 -max-priority-3=9999 -R=/Volumes/CaiCai/OCLint/oclint/oclint-xcoderules/rules.dl/Release
-
-e Pods
过滤Pods -
-report-type xcode
输出到Xcode IDE -
-rc LONG_LINE=1000
行长度设置为1000,原OCLint设置为100,太容易报Warning了 -
-rc LONG_VARIABLE_NAME=100
变量名长度设置100 -
-max-priority
优先级允许错误个数,这里P1 P2 P3
都设置为9999 -
-R=
设置调试的dylib文件夹路径,我们上面已经生成了一个了
更多设置参考官网 http://docs.oclint.org/en/stable/manual/oclint.html
配置好之后Run
,项目中就可以看到提示。
说明自定义库检测成功了,我们需要去掉-R
,将我们编译的自定义的 libCCRuleTestRule.dylib
拷贝到默认引用的那个OCLint
的lib库中去。
注意要拷贝Release版本,因为Debug版本dylib库太大了
我这里路径是:/Users/sheng/Documents/Caicai/OCLint/oclint/build/oclint-release/lib/oclint/rules
去掉 -R
,再进行 Run
,成功。
好了,我们将-R
加上,进行编写各种规则。
规则编写
我们要使用
clang -fmodules -fsyntax-only -Xclang -ast-dump test.m
来分析文件抽象语法树组成,先将有问题的代码输入,然后查找关键字。
关于OC的编译过程,抽象语法树在何时生成,可以 查看此文
例如我们需要编写分类前缀
和static NSString前缀
的规则,就加入如下代码:
#import <Foundation/Foundation.h>
static NSString * const kGrowingNormalEventPath = @"/v2/%@/ios/events?stm=%llu";
int main(void) {
int i=9;
return 0;
}
@interface AppGroupManager : NSObject
@end
@implementation AppGroupManager
- (NSString*)JsonFromFile:(NSString*)_name {
return @"";
}
@end
@implementation AppGroupManager (Growing_node)
@end
然后转成AST
进行查看
TranslationUnitDecl 0x7ff69001ce08 <<invalid sloc>> <invalid sloc> <undeserialized declarations>
|-TypedefDecl 0x7ff69001d6a0 <<invalid sloc>> <invalid sloc> implicit __int128_t '__int128'
| `-BuiltinType 0x7ff69001d3a0 '__int128'
|-TypedefDecl 0x7ff69001d710 <<invalid sloc>> <invalid sloc> implicit __uint128_t 'unsigned __int128'
| `-BuiltinType 0x7ff69001d3c0 'unsigned __int128'
|-TypedefDecl 0x7ff69001d7b0 <<invalid sloc>> <invalid sloc> implicit SEL 'SEL *'
| `-PointerType 0x7ff69001d770 'SEL *' imported
| `-BuiltinType 0x7ff69001d600 'SEL'
|-TypedefDecl 0x7ff69001d898 <<invalid sloc>> <invalid sloc> implicit id 'id'
| `-ObjCObjectPointerType 0x7ff69001d840 'id' imported
| `-ObjCObjectType 0x7ff69001d810 'id' imported
|-TypedefDecl 0x7ff69001d978 <<invalid sloc>> <invalid sloc> implicit Class 'Class'
| `-ObjCObjectPointerType 0x7ff69001d920 'Class' imported
| `-ObjCObjectType 0x7ff69001d8f0 'Class' imported
|-ObjCInterfaceDecl 0x7ff69001d9d0 <<invalid sloc>> <invalid sloc> implicit Protocol
|-TypedefDecl 0x7ff69001dd48 <<invalid sloc>> <invalid sloc> implicit __NSConstantString 'struct __NSConstantString_tag'
| `-RecordType 0x7ff69001db40 'struct __NSConstantString_tag'
| `-Record 0x7ff69001daa0 '__NSConstantString_tag'
|-TypedefDecl 0x7ff69005a400 <<invalid sloc>> <invalid sloc> implicit __builtin_ms_va_list 'char *'
| `-PointerType 0x7ff69001dda0 'char *'
| `-BuiltinType 0x7ff69001cea0 'char'
|-TypedefDecl 0x7ff69005a6e8 <<invalid sloc>> <invalid sloc> implicit __builtin_va_list 'struct __va_list_tag [1]'
| `-ConstantArrayType 0x7ff69005a690 'struct __va_list_tag [1]' 1
| `-RecordType 0x7ff69005a4f0 'struct __va_list_tag'
| `-Record 0x7ff69005a458 '__va_list_tag'
|-ImportDecl 0x7ff68f868d78 </Users/sheng/Documents/Caicai/OCLint/oclint/demo/test.m:1:1> col:1 implicit Foundation
|-VarDecl 0x7ff68f86a320 <line:2:1, col:52> col:25 kGrowingNormalEventPath 'NSString *const' static cinit
| `-ObjCStringLiteral 0x7ff68f86a440 <col:51, col:52> 'NSString *'
| `-StringLiteral 0x7ff68f86a408 <col:52> 'char [27]' lvalue "/v2/%@/ios/events?stm=%llu"
|-FunctionDecl 0x7ff68f86a540 <line:3:1, line:6:1> line:3:5 main 'int (void)'
| `-CompoundStmt 0x7ff68f86a708 <col:16, line:6:1>
| |-DeclStmt 0x7ff68f86a6c0 <line:4:5, col:12>
| | `-VarDecl 0x7ff68f86a638 <col:5, col:11> col:9 i 'int' cinit
| | `-IntegerLiteral 0x7ff68f86a6a0 <col:11> 'int' 9
| `-ReturnStmt 0x7ff68f86a6f8 <line:5:5, col:12>
| `-IntegerLiteral 0x7ff68f86a6d8 <col:12> 'int' 0
|-ObjCInterfaceDecl 0x7ff68f86a740 <line:7:1, line:9:2> line:7:12 AppGroupManager
| |-super ObjCInterface 0x7ff68f869488 'NSObject'
| `-ObjCImplementation 0x7ff68f86a870 'AppGroupManager'
|-ObjCImplementationDecl 0x7ff68f86a870 <line:11:1, line:15:1> line:11:17 AppGroupManager
| |-ObjCInterface 0x7ff68f86a740 'AppGroupManager'
| `-ObjCMethodDecl 0x7ff68f86a9e0 <line:12:1, line:14:1> line:12:1 - JsonFromFile: 'NSString *'
| |-ImplicitParamDecl 0x7ff68f87d618 <<invalid sloc>> <invalid sloc> implicit self 'AppGroupManager *'
| |-ImplicitParamDecl 0x7ff68f87d680 <<invalid sloc>> <invalid sloc> implicit _cmd 'SEL':'SEL *'
| |-ParmVarDecl 0x7ff68f86aa68 <col:28, col:38> col:38 _name 'NSString *'
| `-CompoundStmt 0x7ff68f87d770 <col:44, line:14:1>
| `-ReturnStmt 0x7ff68f87d760 <line:13:5, col:13>
| `-ObjCStringLiteral 0x7ff68f87d740 <col:12, col:13> 'NSString *'
| `-StringLiteral 0x7ff68f87d728 <col:13> 'char [1]' lvalue ""
`-ObjCCategoryImplDecl 0x7ff68f8c9978 <line:17:1, line:18:1> line:17:17 Growing_node
|-ObjCInterface 0x7ff68f86a740 'AppGroupManager'
`-ObjCCategory 0x7ff68f8c98e0 'Growing_node'
ObjCCategoryImplDecl
明显就是分类的,然后搜索代码,找到
/* Visit ObjCCategoryImplDecl
bool VisitObjCCategoryImplDecl(ObjCCategoryImplDecl *node)
{
return true;
}
*/
去掉注释,然后观察类型里面的值,进行判断,最终通过addViolation
添加waring错误
,这里举例几个示例。
Static常量和Extern常量
对于static
修饰的常量,我们应该以k
开头,并附带Growing字段,并附加该常量说明,尽可能简洁详细,不应该带下划线
从AST
中分析到VarDecl
表示变量类型
|-VarDecl 0x7fd2f9aed320 <line:3:1, col:52> col:25 kGrowingNormalEventPath 'NSString *const' static cinit
| `-ObjCStringLiteral 0x7fd2f9aed440 <col:51, col:52> 'NSString *'
| `-StringLiteral 0x7fd2f9aed408 <col:52> 'char [27]' lvalue "/v2/%@/ios/events?stm=%llu"
- 通过
getType
获取类型,类型为NSString *const
- 通过
getInitStyle
获取初始化类型,类型为cinit
- 判断是否
Static
- 是否
NSString *
类型 - 获取配置的前缀名,默认为
kGrowing
,并判断 - 获取
location
添加addViolation
/// 检测到变量声明
/// 1. static以及Extern修饰的NSString类型,以kGrowing开头
bool VisitVarDecl(VarDecl *node)
{
string varname = node->getNameAsString();
//通过 `getType` 获取类型,类型为 `NSString *const`
QualType type = node->getType();
//通过 `getInitStyle` 获取初始化类型,类型为 `cinit`
clang::VarDecl::InitializationStyle initType = node->getInitStyle();
if (initType == clang::VarDecl::CInit) {
//判断是否 `Static`
StorageClass stclass = node->getStorageClass();
bool isStatic = (stclass == SC_Static || stclass == SC_Extern);
string typeName = type.getAsString();
//是否 `NSString *` 类型
if (isPrefixOf("NSString *", typeName) && isStatic) {
//获取配置的前缀名,默认为 `kGrowing`,并判断
string prefix = RuleConfiguration::stringForKey("GIO_STATIC_NSSTRING_PREFIX","kGrowing");
if (!isPrefixOf("kGrowing", varname)) {
//获取 `location` 添加 `addViolation`
SourceLocation loc = node->getLocation();
string message = "static NSString*的变量: \'" + varname + "\' 前缀应使用" + prefix;
addViolation(loc, loc, this, message);
}
}
}
return true;
}
最终输出就为:
/Users/sheng/Documents/Caicai/OCLint/oclint/demo/Test.m:3:25: growing rule [custom|P3] static NSString*的变量: 'XXXNormalEventPath' 前缀应使用kGrowing
BOOL类型的规则判断
Objective-C
使用YES
和NO
。因此true
和false
只能用于CoreFoundation
、C
或c++
代码。因为nil
解析为NO
,所以没有必要在条件下对它进行比较。永远不要直接将某个值与YES
进行比较,因为YES
被定义为1
,一个BOOL
最多可以是8位。
bool VisitIfStmt(IfStmt *node)
{
//获得操作符号
Expr* condExpr = node->getCond();
BinaryOperator *binaryOperator = dyn_cast_or_null<BinaryOperator>(condExpr);
///如果是 == 和 !=
if ((binaryOperator && binaryOperator->getOpcode() == BO_NE) || (binaryOperator && binaryOperator->getOpcode() == BO_EQ)) {
//获得左右两侧 expr
ImplicitCastExpr *lhs = dyn_cast<ImplicitCastExpr>(binaryOperator->getLHS());
ImplicitCastExpr *rhs = dyn_cast<ImplicitCastExpr>(binaryOperator->getRHS());
//当expr为BOOL类型时,强转ObjCBoolLiteralExpr并判断是否为true,然后加入violation
if (GrowingCheckImplicitCastExpr(lhs) || GrowingCheckImplicitCastExpr(rhs)) {
SourceLocation loc = node->getIfLoc();
string message = "不能与YES的BOOL值进行直接比较";
addViolation(loc, loc, this, message);
}
}
return true;
}
bool GrowingCheckImplicitCastExpr(ImplicitCastExpr *expr) {
Expr *subExpr = expr->getSubExpr();
string operatorStr = subExpr->getType().getAsString();
if (operatorStr == "BOOL") {
ObjCBoolLiteralExpr *trueObjCBOOL =
dyn_cast_or_null<ObjCBoolLiteralExpr>(subExpr);
if (trueObjCBOOL && trueObjCBOOL->getValue()) {
return true;
}
}
return false;
}
枚举 Enum
在使用枚举时,建议使用新的类型规范,因为它具有更强的类型检查和代码完成功能。SDK现在包含一个宏来促进和鼓励使用固定的底层类型:NS_ENUM()
代码中加入两种枚举类型
typedef enum : NSUInteger {
GrowingTypeA,
GrowingTypeB,
GrowingTypeC,
} GrowingType;
typedef NS_ENUM(NSUInteger, GrowingKind) {
GrowingKindA,
GrowingKindB,
GrowingKindC,
};
发现NS_ENUM()
的枚举有EnumExtensibilityAttr
和prev
,通过这两个值来过滤
|-EnumDecl 0x7fc2a4075c78 <line:4:9, line:8:1> line:4:9 'NSUInteger':'unsigned long'
| |-EnumConstantDecl 0x7fc2a4075d50 <line:5:5> col:5 GrowingTypeA 'NSUInteger':'unsigned long'
| |-EnumConstantDecl 0x7fc2a4075da0 <line:6:5> col:5 GrowingTypeB 'NSUInteger':'unsigned long'
| `-EnumConstantDecl 0x7fc2a4075df0 <line:7:5> col:5 GrowingTypeC 'NSUInteger':'unsigned long'
|-TypedefDecl 0x7fc2a4075e98 <line:4:1, line:8:3> col:3 GrowingType 'enum GrowingType':'GrowingType'
| `-ElaboratedType 0x7fc2a4075e40 'enum GrowingType' sugar
| `-EnumType 0x7fc2a4075d30 'GrowingType'
| `-Enum 0x7fc2a4075c78 ''
|-EnumDecl 0x7fc2a4076208 </Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/CoreFoundation.framework/Headers/CFAvailability.h:138:43, /Users/sheng/Documents/Caicai/OCLint/oclint/demo/test.m:10:29> col:29 GrowingKind 'NSUInteger':'unsigned long'
| `-EnumExtensibilityAttr 0x7fc2a40762e0 </Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/CoreFoundation.framework/Headers/CFAvailability.h:127:45, col:68> Open
|-TypedefDecl 0x7fc2a4076378 </Users/sheng/Documents/Caicai/OCLint/oclint/demo/test.m:10:1, col:29> col:29 GrowingKind 'enum GrowingKind':'enum GrowingKind'
| `-ElaboratedType 0x7fc2a4076320 'enum GrowingKind' sugar
| `-EnumType 0x7fc2a40762c0 'enum GrowingKind'
| `-Enum 0x7fc2a40763f8 'GrowingKind'
|-EnumDecl 0x7fc2a40763f8 prev 0x7fc2a4076208 </Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/CoreFoundation.framework/Headers/CFAvailability.h:138:90, /Users/sheng/Documents/Caicai/OCLint/oclint/demo/test.m:14:1> line:10:29 GrowingKind 'NSUInteger':'unsigned long'
| |-EnumExtensibilityAttr 0x7fc2a40764c8 </Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/CoreFoundation.framework/Headers/CFAvailability.h:127:45> Inherited Open
| |-EnumConstantDecl 0x7fc2a40764d8 </Users/sheng/Documents/Caicai/OCLint/oclint/demo/test.m:11:5> col:5 GrowingKindA 'NSUInteger':'unsigned long'
| |-EnumConstantDecl 0x7fc2a4076528 <line:12:5> col:5 GrowingKindB 'NSUInteger':'unsigned long'
| `-EnumConstantDecl 0x7fc2a4076578 <line:13:5> col:5 GrowingKindC 'NSUInteger':'unsigned long'
同时,我们可以遍历 EnumConstantDecl
判断值是否与枚举类型对应
bool VisitEnumDecl(EnumDecl *node)
{
string nodename = node->getNameAsString();
if (nodename.length() > 0) {
for (clang::CapturedDecl::specific_decl_iterator<EnumConstantDecl> decllt = node->enumerator_begin(); decllt != node->enumerator_end(); decllt++) {
EnumConstantDecl *tmp = *decllt;
string tmpname = tmp->getNameAsString();
if (!isPrefixOf(tmpname, nodename)) {
SourceLocation loc = tmp->getLocation();
string message = "枚举值\'" + tmpname + "\'与声明的类型不对应,应该以\'" + nodename + "\'为前缀";
addViolation(loc, loc, this, message);
}
}
}
Decl *decl = node->getPreviousDecl();
bool hasattr = node->hasAttrs();
if (hasattr || decl) {
return true;
}
SourceLocation loc = node->getLocation();
string message = "请使用\'typedef NS_ENUM\'类型枚举";
addViolation(loc, loc, this, message);
return true;
}
检测NSString的copy属性以及集合类型泛型判断
///检测到属性,不管是谁的属性
bool VisitObjCPropertyDecl(ObjCPropertyDecl *node)
{
string propertyname = node->getNameAsString();
QualType type = node->getType();
string ivarname = type.getAsString();
if (isPrefixOf("NSString", ivarname)) {
int kind = node->getPropertyAttributes();
if (kind & 0x20) {
//nothing
} else {
SourceLocation loc = node->getLocation();
string message = "\'NSString\'类型的属性: \'" + propertyname + "\' 应该以copy修饰";
addViolation(loc, loc, this, message);
}
}
string str[] = {"NSArray", "NSDictionary", "NSSet"};
vector<string> v(str,str+sizeof(str)/sizeof(str[0]));
string templateStr[] = {"NSArray<NSNumber *>*", "NSDictionary<NSString *, NSDate *>*", "NSSet<NSNumber *>*"};
vector<string> templateV(templateStr,templateStr+sizeof(templateStr)/sizeof(templateStr[0]));
for (vector<string>::size_type i = 0; i != v.size(); ++i) {
string classname = v[i];
if (isPrefixOf(classname, ivarname)) {
if (ivarname.find("<") == ivarname.npos && ivarname.find(">") == ivarname.npos) {
SourceLocation loc = node->getLocation();
string message = classname + " 集合的属性: \'" + propertyname + "\' 应该以指定泛型. eg:" + templateV[i];
addViolation(loc, loc, this, message);
}
}
}
StringRef nameref = node->getName();
if (isUppercase(nameref[0])) {
SourceLocation loc = node->getLocation();
string message = "属性: \'" + propertyname + "\' 不能以大写开头";
addViolation(loc, loc, this, message);
}
return true;
}
Report的修改
由于OCLint默认设置提示的是Warning
,我希望它提示Error
,了解其构成之后,发现可以从Reporter中下手脚。
- 按照
https://blog.csdn.net/shengpeng3344/article/details/106926671
所述,同理创建Reporter
的工程 - 找到
Xcode Reporter
,修改其代码为Error
void writeViolation(std::ostream &out, const Violation &violation)
{
out << violation.path << ":" << violation.startLine << ":" << violation.startColumn;
const RuleBase *rule = violation.rule;
// out << ": warning: " << rule->name();
out << ": error: " << rule->name();
out << " [" << rule->category() << "|P" << rule->priority() << "]";
out << " " << violation.message;
}
- 编译后,替换
/oclint/build/oclint-growing/lib/oclint/reporters
中的libXcodeReporter.dylib
- 重新运行检测,OK,现在提示错误了。
demo链接
我将修改后的rule源码以及使用demo放在了github上 https://github.com/growingio/oclint-growing
代码格式化
关于项目中的空格,对齐等问题,想使用工具解决,这里找到Clang-format
,能够方便的解决我的问题,而且使用clang-format google
标准。
安装
- 使用brew
brew install clang-format
- 直接安装
sudo apt install clang-format
使用
- 预览规范后的代码,输出到控制台
clang-format main.cc
- 直接在原文件上规范代码
clang-format -i main.cc
- 显示指明代码规范,默认为 LLVM
clang-format -style=google main.cc
- 将代码规范配置信息写入文件
.clang-format
clang-format -dump-config > .clang-format
使用自定义代码规范,规范位于当前目录或任一父目录的文件 .clang-format
或 _clang-format
中(如果未找到文件,使用默认代码规范)
clang-format -style=file main.cc
这里
-style=file
就是对的,不要将file替换为.clang-format
根据文件变动调用clang-format
我想根据git
的文件变动,仅对变动的文件进行clang-format
,无论我有没有进行add
和commit
操作,撰写脚本如下:
#获取现在shell的目录
cur_dir=$(dirname $0)
echo "[LOG] current path is :$cur_dir"
gio_dirname=$(echo $cur_dir | grep "Growing")
if [[ "$gio_dirname" == "" ]]; then
echo "该shell脚本目录不为Growing,exit"
exit 0;
fi
cd "$cur_dir"
# 获得变动文件
git status | grep -Eo '([A-Za-z0-9_-]+/){1,}[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+' > $cur_dir/git_changed.txt
# 路径去重
sort -k2n $cur_dir/git_changed.txt | uniq > $cur_dir/git_changed_output.txt
find_code_file() {
m_name=$1
if [[ "${m_name##*.}"x = "m"x || "${m_name##*.}"x = "mm"x|| "${m_name##*.}"x = "cpp"x||"${m_name##*.}"x = "c"x|| "${m_name##*.}"x = "h"x|| "${m_name##*.}"x = "hpp"x ]];then
if [[ -f $m_name ]]; then
#statements
echo "clang-format Code :$m_name"
clang-format -style=google -i $m_name
else
echo "clang-format Not Exist:$m_name"
fi
fi
}
format_code() {
# 获取文件列表
filelist=$1
while read line
do
if [[ "$line" != "" ]]; then
find_code_file "$cur_dir/$line"
fi
done < $1
}
echo "[LOG] 格式化文件 $cur_dir/git_changed_output.txt"
format_code $cur_dir/git_changed_output.txt
rm $cur_dir/git_changed.txt
rm $cur_dir/git_changed_output.txt
Demo链接: https://github.com/growingio/oclint-growing
参考链接:
https://www.cnblogs.com/liuyunbin/p/11538267.html
http://clang.llvm.org/docs/ClangFormat.html
https://blog.csdn.net/shengpeng3344/article/details/106926671
http://clang.llvm.org/docs/ClangFormatStyleOptions.html
本文地址:https://blog.csdn.net/shengpeng3344/article/details/107320020
上一篇: 吃零食的危害是什么?