either as warnings when a particular metric value is exceeded or as a mode/compile switch that generates tables for each method (class, ...) showing the value of a metric.
A while ago I put some temp code in the compiler to generate a warning if a methods LOC exceeded 100 lines - this picked up a number of excessively large methods in the compiler that were corrected.
The code for this remains in the compiler but not enabled with a note to eventually turn it into a proper option.
I was recently reminded of the McCabe Cyclomatic complexity calculation and followed that to a utility that generates an mcc for python code.
If you are not familiar with Cyclomatic complexity metric see
- wikipedia
- Debunking Cyclomatic complecity
- measuring cyclomatic complexity of python code
- pymetrics
- Continuous Integration with BuildBot
I hacked a quick and dirty pseudo calc into the compiler, compiled it and ran the result on the compiler code
Warning Results with cutoff set to > 25
- Code: Select all
580 xx:...cobra/wkspace/Source> mkcobcsh
Compiler.cobra(900): warning: Method Compiler.__loadReference numLines=53 complexity=28 McCabe Cyclomatic Complexity > 25.
CobraParser.cobra(1475): warning: Method CobraParser.declareMethod numLines=128 complexity=34 McCabe Cyclomatic Complexity > 25.
CobraParser.cobra(2932): warning: Method CobraParser.expression2 numLines=5 complexity=46 McCabe Cyclomatic Complexity > 25.
TypeProxies.cobra(287): warning: Method ClrTypeProxy._realTypeWithoutCache numLines=119 complexity=31 McCabe Cyclomatic Complexity > 25.
Compilation succeeded - 4 warnings
...
Results with Table cutoff level >10
First number is lines of code in method - its incorrect for methods with deeply indented trailing statements (like .expression2)
Second is complexity calc ( the exact values may differ some cos I was also fiddling with the calculation accumulation)
- Code: Select all
586 xx:...cobra/wkspace/Source> mkcobcsh > mccabe
587 xx:...cobra/wkspace/Source> sort -k 3 mccabe
DictLit._bindImp 16 11
IndexExpr._bindImp 21 11
CompareExpr._bindImp 38 11
Initializer._bindImp 28 11
CobraParser.branchStmt 48 11
CobraType.isAssignableTo 7 11
AssignExpr._writeSharpDef 46 11
RequirePart.writeSharpDef 61 11
PostCallExpr.writeSharpDef 1 11
AbstractMethod.writeSharpImp 2 11
Compiler.isCompilationNeeded 15 11
Extension.extensionMemberFor 30 11
StringTokenDef._matchBetween 14 11
Compiler.printConsoleMessages 13 11
NameSpace._extensionMemberFor 14 11
ChainedCompareExpr._willCompare 35 11
Box.canHaveDetailedStackTrace.get 13 11
AbstractTypeIdentifier._symbolForName 25 11
(unnamed extension 63591).isEnumerable 10 11
CobraParser.stmt 114 12
RaiseStmt._bindImp 9 12
CobraParser.tryStmt 43 12
MemberExpr._bindImp 51 12
InheritsExpr._bindImp 42 12
Compiler._loadAssembly 30 12
DotExpr._writeSharpDef 1 12
IntType.isAssignableTo 12 12
CobraCore._findCobraExe 1 12
TypeExpr._requiresTypeOf 2 12
CobraParser.statementsFor 4 12
TestifyRunner._testifyRun 33 12
CompareExpr._writeSharpDef 52 12
CobraParser.declareMethodSig 53 12
Container<of>.isAssignableTo 13 12
MemberExpr._bindImpBoxMember 25 12
CobraParser.nonqualifiedTypeId 66 12
Utils.cobraNameForSharpMemberName 1 12
PrimitiveType._installNativeMethodsFrom 7 12
Box._scanMethods 1 13
DotExpr._bindImp 12 13
ArgParser._parseArgs 66 13
CommandLine.doCompile 64 13
Class._makeInitializers 5 13
IdentifierExpr._bindImp 23 13
TestifyRunner._testifyInlineMessages 56 13
Container<of>.greatestCommonDenominatorWith 12 13
CobraParser.block 39 14
TestifyRunner._testifyFile 67 14
NumericTypeInfo.promoteNumerics 32 14
AbstractMethod._computeMatchingBaseMembers 6 14
Box.symbolForName 32 15
Compiler.compileSharp 102 15
PostCallExpr._bindImp 87 15
CobraParser.expression 71 15
ArrayType.isAssignableTo 11 15
Tokenizer._nextToken.get 2 15
CobraType.greatestCommonDenominatorWith 14 15
CommandLine.doHelp 29 16
AssignExpr._bindImp 56 16
CobraParser._nameSpaceAddDecl 46 16
CobraCore.findCobraExe 41 17
NumericPromoExpr._bindImp 72 17
CobraParser.compilerDirective 10 17
(unnamed extension 63591).isDictionaryLike 21 17
Set<of>.equals 1 18
IdentifyMainPhase.innerRun 28 18
CobraParser.indexOrSliceExpr 52 18
BinaryOpExpr.make 1 19
UseDirective._bindUse 58 19
MemberOverload.computeBestOverload 90 19
CobraParser._declareGetOrSetOnlyProperty 68 19
Box.innerType.get 46 20
GenerateHtmlDocVisitor.rank 11 20
CallExpr._bindImp 64 21
HtmlExceptionReportWriter.willDumpHtmlFor 13 21
(unnamed extension 63591).isSequenceLike 27 21
TestifyRunner._processFirstlineDirectives 86 22
CobraParser.declareProperty 82 23
StringMaker._makeString 37 24
CobraParser.typeSpecDecls 78 24
IfStmt._bindImp 84 25
Compiler.__loadReference 53 28
ClrTypeProxy._realTypeWithoutCache 119 31
CobraParser.declareMethod 128 34
CobraParser.expression2 5 46
... std compiler status elided...
I looked at the methods with the largest complexity count and as well as being (excessively) long (LOC) they are also uninhibitedly complex.
(I'll provide corrections to those anyway eventually)
but What are folks thoughts as to the usefulness of this and
if useful how should it be presented
- warnings with a fixed (wired cutoff)
- warnings with an optionally set cutoff
- table mode ( cobra -metric:ccyclomatic *.cobra)
- table mode and cutoff (args)
- other ??
At the moment I'm only considering LOC/method and complexity/method but it could be engineered so as to provide settings, accumulation and presentation scaffolding that made it easier
to add additional code metrics calculations in the future.