QL code and tests for C#/C++/JavaScript.

This commit is contained in:
Pavel Avgustinov
2018-08-02 17:53:23 +01:00
commit b55526aa58
10684 changed files with 581163 additions and 0 deletions

View File

@@ -0,0 +1,32 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
This rule finds functions that use more functions and variables from another file than functions and variables from its own file.
This function may be better placed in the other file, to avoid exposing internals of the file it depends on.
</p>
</overview>
<recommendation>
<p>See if the function can be moved to the file which contains most of its dependencies.</p>
</recommendation>
<references>
<li>W. C. Wake, <em>Refactoring Workbook</em>, pp. 95 &ndash; 96. Addison-Wesley Professional, 2004.</li>
<li>E. Gamma, R. Helm, R. Johnson, J. Vlissides,
<em>Design patterns: elements of reusable object-oriented software</em>.
Addison-Wesley Longman Publishing Co., Inc. Boston, MA, 1995.</li>
<li>
MSDN Magazine: <a href="http://msdn.microsoft.com/en-us/magazine/cc947917.aspx">Patterns in Practice: Cohesion And Coupling</a>
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,65 @@
/**
* @name Feature envy
* @description A function that uses more functions and variables from another file than functions and variables from its own file. This function might be better placed in the other file, to avoid exposing internals of the file it depends on.
* @kind problem
* @problem.severity recommendation
* @precision high
* @id cpp/feature-envy
* @tags maintainability
* modularity
* statistical
* non-attributable
*/
import cpp
predicate functionUsesVariable(Function source, Variable v, File target) {
v.getAnAccess().getEnclosingFunction() = source and
not (v.(LocalScopeVariable).getFunction() = source) and
v.getFile() = target
}
predicate functionUsesFunction(Function source, Function f, File target) {
exists(FunctionCall fc | fc.getEnclosingFunction() = source and fc.getTarget() = f) and
f.getFile() = target
}
predicate dependencyCount(Function source, File target, int res) {
res = strictcount(Declaration d |
functionUsesVariable(source, d, target) or
functionUsesFunction(source, d, target)
)
}
predicate selfDependencyCountOrZero(Function source, int res) {
exists(File target
| target = source.getFile() and onlyInFile(source, target)
| res = max(int i | dependencyCount(source, target, i) or i = 0))
}
predicate dependsHighlyOn(Function source, File target, int res) {
dependencyCount(source, target, res) and
target.fromSource() and
exists(int selfCount |
selfDependencyCountOrZero(source, selfCount) and
res > 2*selfCount and
res > 4
)
}
predicate onlyInFile(Function f, File file) {
file = f.getFile() and
not exists(File file2 | file2 = f.getFile() and file2 != file)
}
from Function f, File other, int selfCount, int depCount, string selfDeps
where dependsHighlyOn(f, other, depCount) and
selfDependencyCountOrZero(f, selfCount) and
not exists(File yetAnother | dependsHighlyOn(f, yetAnother, _) and yetAnother != other) and
not other instanceof HeaderFile and
not f instanceof MemberFunction
and if selfCount = 0 then selfDeps = "0 dependencies"
else if selfCount = 1 then selfDeps = "only 1 dependency"
else selfDeps = "only " + selfCount.toString() + " dependencies"
select f, "Function " + f.getName() + " could be moved to file $@" +
" since it has " + depCount.toString() + " dependencies to that file, but " +
selfDeps + " to its own file.", other, other.getBaseName()

View File

@@ -0,0 +1,24 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>This query shows graph of class inheritance hierarchy</p>
<p />
</overview>
<section title="How to Address the Query Results">
<p />
</section>
<references>
</references>
</qhelp>

View File

@@ -0,0 +1,15 @@
/**
* @name Class hierarchies
* @description Shows classes and their base classes.
* @kind graph
* @id cpp/architecture/class-hierarchies
* @graph.layout organic
* @workingset jhotdraw
* @result succeed 48
* @result_ondemand succeed 48
*/
import cpp
from Class s
where s.fromSource()
select s, s.getABaseClass()

View File

@@ -0,0 +1,25 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>This query shows coupling between classes.</p>
<p>Red, large boxes are hub types that depend on many other classes
and are depended on by many other classes.</p>
</overview>
<section title="How to Address the Query Results">
<p />
</section>
<references>
</references>
</qhelp>

View File

@@ -0,0 +1,16 @@
/**
* @name Hub classes
* @description Shows coupling between classes; red, large boxes are hub types that depend on many other classes
* and are depended on by many other classes.
* @kind treemap
* @id cpp/architecture/hub-classes
* @treemap.warnOn highValues
*/
import cpp
from Class c
where c.fromSource()
select c as Class,
c.getMetrics().getAfferentCoupling() as AfferentCoupling,
c.getMetrics().getEfferentSourceCoupling() as EfferentCoupling
order by AfferentCoupling desc

View File

@@ -0,0 +1,48 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>This query shows the distribution of inheritance depth across all types, i.e. classes. Library types are ignored.</p>
<p>The result of this query is a line graph showing, for each number <i>n</i>, how many types have an inheritance depth of <i>n</i>, where
the inheritance depth of a type is the length of a longest path in the inheritance hierarchy from top class to the type.</p>
<p>When hovering the mouse pointer over a specific depth value, the number of types having this inheritance depth is displayed.</p>
</overview>
<section title="How to Address the Query Results">
<p>The depth of a type is an indication of how deeply nested a type is in a given design.
Very deep types can be an indication of over-engineering, whereas a system with predominantly shallow types
may not be exploiting object-orientation to the full.</p>
</section>
<references>
<li>
Shyam R. Chidamber and Chris F. Kemerer.
<a href="http://www.pitt.edu/~ckemerer/CK%20research%20papers/MetricForOOD_ChidamberKemerer94.pdf">A Metrics Suite for Object Oriented Design
</a>.
IEEE Transactions on Software Engineering,
20(6), pages 476-493, June 1994.
<a href="http://www.dmst.aueb.gr/dds/index.en.html">Diomides D. Spinnelis</a>.
<a href="http://www.spinellis.gr/codequality/">Code Quality: The Open Source Perspective</a>.
Addison-Wesley 2007.
<a href="http://www.dmst.aueb.gr/dds/index.en.html">Diomides D. Spinnelis</a>.
<a href="http://www.spinellis.gr/sw/ckjm/">ckjm - Chidamber and Kemerer Java Metrics</a>.
(implementation of CK metrics), 2006.
</li></references></qhelp>

View File

@@ -0,0 +1,22 @@
/**
* @name Inheritance depth distribution
* @description Shows distribution of inheritance depth across all classes.
* @kind chart
* @id cpp/architecture/inheritance-depth-distribution
* @chart.type line
* @workingset jhotdraw
* @result succeed 48
* @result_ondemand succeed 48
*/
import cpp
/** does source class c have inheritance depth d? */
predicate hasInheritanceDepth(Class c, int d) {
c.fromSource() and d = c.getMetrics().getInheritanceDepth()
}
from int depth
where hasInheritanceDepth(_, depth)
select depth as InheritanceDepth,
count(Class c | hasInheritanceDepth(c, depth)) as NumberOfClasses
order by InheritanceDepth

View File

@@ -0,0 +1,13 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>about General Class-Level Information</p>
<!--TOC-->
</overview>
</qhelp>

View File

@@ -0,0 +1,25 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>This query shows namespaces that cyclically depend
on one another.</p>
<p />
</overview>
<section title="How to Address the Query Results">
<p>If there are cyclic dependencies between packages, they cannot be developed and tested independently. It is thus preferable to
eliminate such cycles from the program.</p>
</section>
<references>
<li>Robert Martin's <a href="https://drive.google.com/file/d/0BwhCYaYDn8EgOGM2ZGFhNmYtNmE4ZS00OGY5LWFkZTYtMjE0ZGNjODQ0MjEx/view">Acyclic Dependencies Principle</a>.
</li></references></qhelp>

View File

@@ -0,0 +1,13 @@
/**
* @name Cyclic namespaces
* @description Shows namespaces that cyclically depend on one another.
* @kind graph
* @id cpp/architecture/cyclic-namespaces
* @graph.layout hierarchical
* @tags maintainability
*/
import cpp
from MetricNamespace a, MetricNamespace b
where a.getANamespaceDependency() = b and b.getANamespaceDependency*() = a
select a, b

View File

@@ -0,0 +1,22 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>This query finds classes that belong to no namespace</p>
</overview>
<section title="How to Address the Query Results">
<p>If there are too many classes that belong to no namespace, consider creating namespaces to get a better project structure.</p>
</section>
<references>
</references>
</qhelp>

View File

@@ -0,0 +1,13 @@
/**
* @name Global namespace classes
* @description Finds classes that belong to no namespace
* @kind table
* @id cpp/architecture/global-namespace-classes
*/
import cpp
from Class c
where c.fromSource()
and c.isTopLevel()
and c.getParentScope() instanceof GlobalNamespace
select c, "This class is not declared in any namespace"

View File

@@ -0,0 +1,24 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>This query finds namespace dependencies and draws hierarchical graph.</p>
<p />
</overview>
<section title="How to Address the Query Results">
<p />
</section>
<references>
</references>
</qhelp>

View File

@@ -0,0 +1,12 @@
/**
* @name Namespace dependencies
* @description Shows dependencies between namespaces.
* @kind graph
* @id cpp/architecture/namespace-dependencies
* @graph.layout hierarchical
*/
import cpp
from MetricNamespace a, MetricNamespace b
where a.getANamespaceDependency() = b
select a, b

View File

@@ -0,0 +1,34 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>This query shows general statistics about the application, organized as a table.</p>
<p>The query shows the number of namespaces making up the application, the number of files, header files, C files, CPP files, classes, structures, unions, and
functions, and the total number of source code resp. comment lines.</p>
<p>The self-containedness measure indicates how much the application
depends on third-party libraries: low self-containedness means that many dependencies
are to library classes (as opposed to source classes within the same application).</p>
</overview>
<section title="How to Address the Query Results">
<p>The results of this query are purely informative and more useful for getting an overall impression of the application than for
identifying particular defects.</p>
</section>
<references>
</references>
</qhelp>

View File

@@ -0,0 +1,34 @@
/**
* @name General statistics
* @description Shows general statistics about the application.
* @kind table
* @id cpp/architecture/general-statistics
*/
import cpp
from string l, string n
where (l = "Number of Namespaces" and
n = count(Namespace p | p.fromSource()).toString())
or (l = "Number of Files" and
n = count(File f | f.fromSource()).toString())
or (l = "Number of Header Files" and
n = count(HeaderFile f | f.fromSource()).toString())
or (l = "Number of C Files" and
n = count(CFile f | f.fromSource()).toString())
or (l = "Number of C++ Files" and
n = count(CppFile f | f.fromSource()).toString())
or (l = "Number of Classes" and
n = count(Class c | c.fromSource() and not c instanceof Struct).toString())
or (l = "Number of Structs" and
n = count(Struct s | s.fromSource()and not s instanceof Union).toString())
or (l = "Number of Unions" and
n = count(Union u | u.fromSource()).toString())
or (l = "Number of Functions" and
n = count(Function f | f.fromSource()).toString())
or (l = "Number of Lines Of Code" and
n = sum(File f, int toSum | (f.fromSource()) and (toSum = f.getMetrics().getNumberOfLinesOfCode()) | toSum).toString())
or (l = "Self-Containedness" and
n = (100 * sum(Class c, int toSum | (c.fromSource()) and (toSum = c.getMetrics().getEfferentSourceCoupling()) | toSum)
/ sum(Class c, int toSum | (c.fromSource()) and (toSum = c.getMetrics().getEfferentCoupling()) | toSum)).toString()
+ "%")
select l as Title, n as Value

View File

@@ -0,0 +1,38 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
This rule looks for two files that share too much information about each other (accessing many operations or variables in both directions).
It would be better to invert some of the dependencies to reduce the coupling between the two files.
</p>
<p>
Having two files have too many dependencies on each other makes it difficult to modify one file without requiring modifications to the
other. This could lead to defects when the programmer forgets to put in the necessary changes to one file when he makes a change to the other.
</p>
</overview>
<recommendation>
<p>Move some of the methods and variables from one file to another, so that most of the dependencies go only in one direction. If possible,
try to make all the dependencies go in one direction.</p>
</recommendation>
<references>
<li>W. C. Wake, <em>Refactoring Workbook</em>, pp. 95 &ndash; 96. Addison-Wesley Professional, 2004.</li>
<li>E. Gamma, R. Helm, R. Johnson, J. Vlissides,
<em>Design patterns: elements of reusable object-oriented software</em>.
Addison-Wesley Longman Publishing Co., Inc. Boston, MA, 1995.</li>
<li>
MSDN Magazine: <a href="http://msdn.microsoft.com/en-us/magazine/cc947917.aspx">Patterns in Practice: Cohesion And Coupling</a>
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,63 @@
/**
* @name Inappropriate Intimacy
* @description Two files share too much information about each other (accessing many operations or variables in both directions). It would be better to invert some of the dependencies to reduce the coupling between the two files.
* @kind problem
* @problem.severity recommendation
* @precision high
* @id cpp/file-intimacy
* @tags maintainability
* modularity
* statistical
* non-attributable
*/
import cpp
predicate remoteVarAccess(File source, File target, VariableAccess va) {
va.getFile() = source and
va.getTarget().getFile() = target and
// Ignore variables with locations in multiple files
strictcount(File f | f = va.getTarget().getFile()) = 1 and
source != target
}
predicate remoteFunAccess(File source, File target, FunctionCall fc) {
fc.getFile() = source and
fc.getTarget().getFile() = target and
// Ignore functions with locations in multiple files
strictcount(File f | f = fc.getTarget().getFile()) = 1 and
source != target
}
predicate candidateFilePair(File source, File target) {
remoteVarAccess(source, target, _) or
remoteFunAccess(source, target, _)
}
predicate variableDependencyCount(File source, File target, int res) {
candidateFilePair(source, target) and
res = count(VariableAccess va | remoteVarAccess(source, target, va))
}
predicate functionDependencyCount(File source, File target, int res) {
candidateFilePair(source, target) and
res = count(FunctionCall fc | remoteFunAccess(source, target, fc))
}
predicate highDependencyCount(File source, File target, int res) {
exists(int varCount, int funCount |
variableDependencyCount(source, target, varCount) and
functionDependencyCount(source, target, funCount) and
res = varCount + funCount and
res > 20)
}
from File a, File b, int ca, int cb
where highDependencyCount(a, b, ca) and
highDependencyCount(b, a, cb) and
ca >= cb and
a != b and
not a instanceof HeaderFile and
not b instanceof HeaderFile and
b.getShortName().trim().length() > 0
select a, "File is too closely tied to $@ (" + ca.toString() + " dependencies one way and " + cb.toString() + " the other).",
b, b.getBaseName()

View File

@@ -0,0 +1,17 @@
// an include declaration just adds one source dependency, it does not automatically
// add a dependency from this file to all the declarations in stdio.h
#include <stdio.h>
#include <myfile.h> // contains non-static global myfile_err
extern int myfile_err; // this external declaration adds a dependency on myfile.h
class C {
public:
C() {
// one dependency for printf:
printf("Hello world!");
// one dependency for FILE type, and one for NULL macro:
FILE fp = NULL;
}
};

View File

@@ -0,0 +1,34 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>This rule finds classes that depend on many other types. Depending on too many other classes makes a class vulnerable to changes
and defects in its dependencies, and is also a sign that the class lacks cohesion (i.e. a single purpose). These classes
could be refactored into smaller, more cohesive classes.</p>
</overview>
<recommendation>
<p>These classes could probably be refactored into smaller classes with fewer dependencies.</p>
</recommendation>
<example>
<sample src="ClassesWithManyDependencies.cpp" />
</example>
<references>
<li>W. Stevens, G. Myers, L. Constantine, <em>Structured Design</em>, IBM Systems Journal, 13 (2), 115-139, 1974.</li>
<li>
Microsoft Patterns &amp; Practices Team. <a href="http://msdn.microsoft.com/en-us/library/ee658117.aspx">Architectural Patterns and Styles</a> <em>Microsoft Application Architecture Guide, 2nd Edition.</em> Microsoft Press, 2009.
</li>
<li>
<a href="en.wikipedia.org/wiki/Code_refactoring">Wikipedia: Code refactoring</a>
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,20 @@
/**
* @name Classes with too many source dependencies
* @description Finds classes that depend on many other types; they could probably be refactored into smaller classes with fewer dependencies.
* @kind problem
* @id cpp/architecture/classes-with-many-dependencies
* @problem.severity recommendation
* @workingset jhotdraw
* @result succeed 20
* @result_ondemand succeed 20
* @tags maintainability
* statistical
* non-attributable
*/
import cpp
from Class t, int n
where t.fromSource() and
n = t.getMetrics().getEfferentSourceCoupling() and
n > 10
select t as Class, "This class has too many dependencies (" + n.toString() + ")"

View File

@@ -0,0 +1,20 @@
//This struct contains 30 fields.
struct MyParticle {
bool isActive;
int priority;
float x, y, z;
float dx, dy, dz;
float ddx, ddy, ddz;
bool isCollider;
int age, maxAge;
float size1, size2;
bool hasColor;
unsigned char r1, g1, b1, a1;
unsigned char r2, g2, b2, a2;
class texture *tex;
float u1, v1, u2, v2;
};

View File

@@ -0,0 +1,36 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>This rule finds classes with more than 15 instance (i.e., non-<code>static</code>) fields. Library classes
are not shown. Having too many fields in one class is a sign that the class lacks cohesion (i.e. lacks a single purpose).
These classes can be split into smaller, more cohesive classes. Alternatively, the related fields can be grouped
into <code>struct</code>s.</p>
</overview>
<recommendation>
<p>Classes with many fields may be hard to maintain. They could probably be refactored by breaking them down into smaller classes
and using composition.</p>
</recommendation>
<example>
<sample src="ClassesWithManyFields.cpp" />
</example>
<references>
<li>W. Stevens, G. Myers, L. Constantine, <em>Structured Design</em>. IBM Systems Journal, 13 (2), 115-139, 1974.</li>
<li>
Microsoft Patterns &amp; Practices Team, <em>Microsoft Application Architecture Guide (2nd Edition), Chapter 3: Architectural Patterns and Styles.</em> Microsoft Press, 2009 (<a href="http://msdn.microsoft.com/en-us/library/ee658117.aspx">available online</a>).
</li>
<li>
Wikipedia: <a href="en.wikipedia.org/wiki/Code_refactoring">Code refactoring</a>
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,113 @@
/**
* @name Classes with too many fields
* @description Finds classes with many fields; they could probably be refactored by breaking them down into smaller classes, and using composition.
* @kind problem
* @problem.severity recommendation
* @precision high
* @id cpp/class-many-fields
* @tags maintainability
* statistical
* non-attributable
*/
import cpp
string kindstr(Class c)
{
exists(int kind | usertypes(c, _, kind) |
(kind = 1 and result = "Struct") or
(kind = 2 and result = "Class") or
(kind = 6 and result = "Template class")
)
}
predicate vdeInfo(VariableDeclarationEntry vde, Class c, File f, int line)
{
c = vde.getVariable().getDeclaringType() and
f = vde.getLocation().getFile() and
line = vde.getLocation().getStartLine()
}
predicate previousVde(VariableDeclarationEntry previous, VariableDeclarationEntry vde)
{
exists(Class c, File f, int line | vdeInfo(vde, c, f, line) |
vdeInfo(previous, c, f, line - 3) or
vdeInfo(previous, c, f, line - 2) or
vdeInfo(previous, c, f, line - 1) or
(vdeInfo(previous, c, f, line) and exists(int prevCol, int vdeCol |
prevCol = previous.getLocation().getStartColumn() and vdeCol = vde.getLocation().getStartColumn() |
prevCol < vdeCol or (prevCol = vdeCol and previous.getName() < vde.getName())
))
)
}
predicate masterVde(VariableDeclarationEntry master, VariableDeclarationEntry vde)
{
(not previousVde(_, vde) and master = vde) or
exists(VariableDeclarationEntry previous | previousVde(previous, vde) and masterVde(master, previous))
}
class VariableDeclarationGroup extends @var_decl {
VariableDeclarationGroup() {
not previousVde(_, this)
}
Class getClass() {
vdeInfo(this, result, _, _)
}
// pragma[noopt] since otherwise the two locationInfo relations get join-ordered
// after each other
pragma[noopt]
predicate hasLocationInfo(string path, int startline, int startcol, int endline, int endcol) {
exists(VariableDeclarationEntry last, Location lstart, Location lend |
masterVde(this, last) and
this instanceof VariableDeclarationGroup and
not previousVde(last, _) and
exists(VariableDeclarationEntry vde | vde=this and vde instanceof VariableDeclarationEntry and vde.getLocation() = lstart) and
last.getLocation() = lend and
lstart.hasLocationInfo(path, startline, startcol, _, _) and
lend.hasLocationInfo(path, _, _, endline, endcol)
)
}
string toString() {
if previousVde(this, _) then
result = "group of "
+ strictcount(string name
| exists(VariableDeclarationEntry vde
| masterVde(this, vde) and
name = vde.getName()))
+ " fields here"
else
result = "declaration of " + this.(VariableDeclarationEntry).getVariable().getName()
}
}
class ExtClass extends Class {
predicate hasOneVariableGroup() {
strictcount(VariableDeclarationGroup vdg | vdg.getClass() = this) = 1
}
predicate hasLocationInfo(string path, int startline, int startcol, int endline, int endcol) {
if hasOneVariableGroup() then
exists(VariableDeclarationGroup vdg | vdg.getClass() = this | vdg.hasLocationInfo(path, startline, startcol, endline, endcol))
else
getLocation().hasLocationInfo(path, startline, startcol, endline, endcol)
}
}
from ExtClass c, int n, VariableDeclarationGroup vdg, string suffix
where n = strictcount(string fieldName
| exists(Field f
| f.getDeclaringType() = c and
fieldName = f.getName() and
// IBOutlet's are a way of building GUIs
// automatically out of ObjC properties.
// We don't want to count those for the
// purposes of this query.
not (f.getType().getAnAttribute().hasName("iboutlet")))) and
n > 15 and
not c.isConstructedFrom(_) and
c = vdg.getClass() and
if c.hasOneVariableGroup() then suffix = "" else suffix = " - see $@"
select c, kindstr(c) + " " + c.getName() + " has " + n + " fields, which is too many" + suffix + ".",
vdg, vdg.toString()

View File

@@ -0,0 +1,22 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>This rule finds functions that make too many calls. Having too many dependencies makes a function vulnerable to
changes and defects in those dependencies, and is also a sign that the function lacks cohesion (i.e. lacks a single purpose).
These functions can be split into smaller, more cohesive functions.</p>
</overview>
<recommendation>
<p>Splitting these functions would increase maintainability and readability.</p>
</recommendation>
<references>
<li>W. Stevens, G. Myers, L. Constantine, <em>Structured Design</em>, IBM Systems Journal, 13 (2), 115-139, 1974.
</li></references></qhelp>

View File

@@ -0,0 +1,18 @@
/**
* @name Complex functions
* @description Finds functions which call too many other functions. Splitting these functions would increase maintainability and readability.
* @kind problem
* @id cpp/architecture/complex-functions
* @problem.severity recommendation
* @tags maintainability
* statistical
* non-attributable
*/
import cpp
from Function f, int n
where f.fromSource() and
n = f.getMetrics().getNumberOfCalls() and
n > 99 and
not f.isMultiplyDefined()
select f as Function, "This function makes too many calls (" + n.toString() + ")"

View File

@@ -0,0 +1,30 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>This rule computes cyclomatic complexity per function. Cyclomatic complexity is a measure of how complex a function
is, and is derived from the number of branching statements in the function. Complex functions can be difficult to understand
and maintain, and usually can be split into smaller, less complex functions.</p>
<p>The rule finds functions with high (e.g. >50) cyclomatic complexity.</p>
</overview>
<recommendation>
<p>With increasing cyclomatic complexity there need to be more test cases that are necessary to achieve a complete branch coverage when testing the function.
Try to reduce the function's complexity by splitting it into several more cohesive functions.
A particularly effective way of reducing complexity is to put code that handles a particular case in an large <code>if</code> or
<code>switch</code> statement into a separate function with a descriptive name. This not only reduces the complexity of the function,
but makes it considerably more readable as the function's descriptive name gives an idea of its purpose without the developer
analyzing each line of code.
</p>
</recommendation>
<references>
<li>Wikipedia: <a href="https://en.wikipedia.org/wiki/Cyclomatic_complexity">Cyclomatic complexity</a>.</li>
<li>T. McCabe. <em>A Complexity Measure</em>. ICSE '76: Proceedings of the 2nd international conference on Software engineering, 1976.</li>
</references>
</qhelp>

View File

@@ -0,0 +1,16 @@
/**
* @name Cyclomatic Complexity
* @description Functions with high cyclomatic complexity. With increasing cyclomatic complexity there need to be more test cases that are necessary to achieve a complete branch coverage when testing this function.
* @kind problem
* @id cpp/architecture/cyclomatic-complexity
* @problem.severity warning
* @tags testability
* statistical
* non-attributable
*/
import cpp
from Function f, int complexity
where complexity = f.getMetrics().getCyclomaticComplexity()
and complexity > 250
select f, "Function has high cyclomatic complexity: " + complexity.toString()

View File

@@ -0,0 +1,8 @@
// this example has 15 parameters.
void fillRect(int x, int y, int w, int h,
int r1, int g1, int b1, int a1,
int r2, int g2, int b2, int a2,
gradient_type grad, unsigned int flags, bool border)
{
// ...
}

View File

@@ -0,0 +1,25 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>This rule finds functions with many parameters. Passing too many parameters to a function is a sign that the function
is not cohesive (i.e. lacks a single purpose). These functions could be split into smaller, more cohesive functions.</p>
</overview>
<recommendation>
<p>These functions could probably be refactored by wrapping related parameters into <code>struct</code>s.</p>
</recommendation>
<example><sample src="FunctionsWithManyParameters.cpp" />
</example>
<references>
<li>S. McConnell. <em>Code Complete, 2d ed</em>. Microsoft Press, 2004.</li>
</references>
</qhelp>

View File

@@ -0,0 +1,18 @@
/**
* @name Functions with too many parameters
* @description Finds functions with many parameters;
* they could probably be refactored by wrapping parameters into a struct.
* @kind problem
* @id cpp/architecture/functions-with-many-parameters
* @problem.severity recommendation
* @tags testability
* statistical
* non-attributable
*/
import cpp
from Function f
where f.fromSource() and
f.getMetrics().getNumberOfParameters() > 15
select f, "This function has too many parameters ("
+ f.getMetrics().getNumberOfParameters().toString() + ")"

View File

@@ -0,0 +1,13 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>about Architecture</p>
<!--TOC-->
</overview>
</qhelp>