mirror of
https://github.com/github/codeql.git
synced 2025-12-23 04:06:37 +01:00
QL code and tests for C#/C++/JavaScript.
This commit is contained in:
32
cpp/ql/src/Architecture/FeatureEnvy.qhelp
Normal file
32
cpp/ql/src/Architecture/FeatureEnvy.qhelp
Normal 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 – 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>
|
||||
65
cpp/ql/src/Architecture/FeatureEnvy.ql
Normal file
65
cpp/ql/src/Architecture/FeatureEnvy.ql
Normal 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()
|
||||
@@ -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>
|
||||
@@ -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()
|
||||
@@ -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>
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -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
|
||||
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>about General Class-Level Information</p>
|
||||
|
||||
<!--TOC-->
|
||||
|
||||
</overview>
|
||||
</qhelp>
|
||||
@@ -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>
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -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"
|
||||
@@ -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>
|
||||
@@ -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
|
||||
@@ -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>
|
||||
@@ -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
|
||||
0
cpp/ql/src/Architecture/InappropriateIntimacy.cpp
Normal file
0
cpp/ql/src/Architecture/InappropriateIntimacy.cpp
Normal file
38
cpp/ql/src/Architecture/InappropriateIntimacy.qhelp
Normal file
38
cpp/ql/src/Architecture/InappropriateIntimacy.qhelp
Normal 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 – 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>
|
||||
63
cpp/ql/src/Architecture/InappropriateIntimacy.ql
Normal file
63
cpp/ql/src/Architecture/InappropriateIntimacy.ql
Normal 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()
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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 & 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>
|
||||
@@ -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() + ")"
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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 & 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>
|
||||
@@ -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()
|
||||
@@ -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>
|
||||
@@ -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() + ")"
|
||||
@@ -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>
|
||||
@@ -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()
|
||||
@@ -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)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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() + ")"
|
||||
13
cpp/ql/src/Architecture/index.qhelp
Normal file
13
cpp/ql/src/Architecture/index.qhelp
Normal file
@@ -0,0 +1,13 @@
|
||||
<!DOCTYPE qhelp PUBLIC
|
||||
"-//Semmle//qhelp//EN"
|
||||
"qhelp.dtd">
|
||||
<qhelp>
|
||||
|
||||
|
||||
<overview>
|
||||
<p>about Architecture</p>
|
||||
|
||||
<!--TOC-->
|
||||
|
||||
</overview>
|
||||
</qhelp>
|
||||
Reference in New Issue
Block a user