- Ziele der Arbeit
- Testbarkeit in a Nutshell
- Code Analyse-Tool
- Regelbasierte Analysen
- Architektur
- Komponenten von "YaCI"
- Rule Parser
- Rule Analyser
- Result Generator
- JTransformer Eclipse Integration
- Zusammenfassung
- Ausblick
There are two ways to write error-free programs: only the third one works.
... der Grad, bis zu dem eine Software (eine Softwarekomponente) durch Tests und Test-Suites unterstützt werden kann, durch die wiederum Fehler gefunden und vermieden werden können.
Vermeide komplexe Logik in Konstruktoren
public class LottoTicket {
private Database mDataBase = null;
/* bad */
public LottoTicket() {
mDatabase = new Database();
}
/* good */
public LottoTicket(Database db) {
mDatabase = db;
}
}
/* I am a long Description
maybe over one or more lines */
Short Description @ when <Premise> then <Conclusion>.
Vermeide statische oder finale Methoden
/* Prevent constructors with complex logic */ Complex Constructor @ when constructor contain keyword new more than 2 times and contain control_flow more than 2 times and has line_count greater 10 then drop error.
Bevorzuge Kompositionen anstatt Vererbung
/* Prefer composition pattern opposite inheritance */ Find Inheritance @ when class has inheritance of depth greater 3 then drop warning.
?- string_codes("/* Prevent constructors with complexe
logic */\nComplex Constructors @ when constructor [...]
then drop error.", Rule),
phrase(parse_rule(Result), Rule).
Result = [rule(
when(constructor(and(contain([keyword(new), greater, 2]),
and(contain([control_flow, greater|...]),
has([line_count, greater|...]))))),
then(drop(error)),
description(short('Complex Constructors'),
long('Prevent constructors with complexe logic'))
)]
compilationUnitT(28591, 28594, 28593,
[28595, 28596], [28597]).
packageT(28594, ’de.uniwue.thesis’).
classT(28597, 28591, ’AstExample’, [], [28598, 28599, 28600]).
methodT(28600, 28597, getNode, [28613], 10001, [], [], 28614).
constructorT(28599, 28597, [], [], [], 28605).
fieldT(28598, 28597, 28603, mNodes, null).
paramT(28613, 28600, 10080, index).
newT(28610, 28608, 28599, null, [], 21199, [], 28612, null) .
returnT(28616, 28614, 28600, 28617).
modifierT(28604, 28598, private).
modifierT(28615, 28600, public).
?- analyse_rule(when(constructor(and(
contain([keyword(new), greater, 2]),
and(contain([control_flow, greater|...]),
has([line_count, greater|...])
)
)))), Result).
Result = match(class(28597), scopes([
constructor(id(28604), name('ComplexConstructor'),
hints([new(1, [28645]), control_flows(2, [28624|...]),
line_count(8)])
)
])).
?- generate_match_term(match(class(28597), scopes([
constructor(id(28604), name('ComplexConstructor'),
hints([new(1, [28645]), control_flows(2, [28624,28645]),
line_count(8)])
)])), Result).
Result = result(result_group(28597), elements([
constructor(28604), new_keyword(28645),
control_flow(28624), control_flow(28645)
])).
analysis_api:analysis_definition(
ShortDescription, Trigger, SeverityLevel,
AnalysisGroup, LongDescription
).
analysis_api:analysis_result(Id, Group, Result) :-
/**
* run analysis here and create result terms
* for each matched ast element call make_result_term/3
*/
mark_result_term(AstElement, 'Description of element', Result).
analysis_definition initialisierenanalyse_result initialisieren
speakername :-
ask(questions).
This presentation is licensed under the
Creative Commons Attribution 4.0 International
license.
/* Prevent complex, private methods */ Rule 1 @ when method is private and has line_count [...]. /* Prevent final or static methods */ Rule 2 @ when method is final or method is static then drop warning. /* Do not use keyword new to often */ Rule 3 @ when method contain keyword new more than 3 times then drop warning. /* Prevent constructors with complex logic */ Rule 4 @ when constructor contain keyword new more than 2 times and contain control_flow more than 2 times and has line_count greater 5 then drop error. /* Prevent the Singleton pattern */ Rule 5 @ when constructor is private and method has return_type of class and is static and contain access of static field then drop warning.
/* Prefer composition pattern opposite inheritance */
Rule 6 @ when class has inheritance of depth
greater 2 then drop warning.
/* Wrap external libraries */
Rule 7 @ when method contain call of class_method more
than 0 times not belongs to package "org.joda.time"
then drop warning.
/* Prevent service lookups or static method calls */
Rule 8 @ when method contain call of class_method
more than 0 times then drop info.
/* Prefer interface-based design */
Rule 9 @ when method is public and has parameter
not of type interface or constructor has parameter
not of type interface then drop info.
/* Do not declare class as final */
Rule 10 @ when class is final then drop info.
Rule: when <PREMISE> [<CONJUNC.> <PREMISE> ...] then <CONCLUSION>.
Premise: <SCOPE> <CONDITION> <INSTRUCTION> [<CONJUNC.> <CONDITION> <INSTRUCTION>, ...]
Instruction: <CONDITION_PART> | <ACCESSOR> | <RELATION>
Conjunction: and | or
Scope: class | method | ...
Condition: is | has | ...
Conclusion: drop 'string' | pattern 'string' | score 'number'
parse_rules(Rules) -->
blanks, list_of_rules(Rules).
list_of_rules([SingleRule]) --> rule(SingleRule).
list_of_rules([SingleRule|Rules]) -->
rule(SingleRule), list_of_rules(Rules).
rule(Rule) -->
rule_description(Description), !,
when, blank, blanks, rule_premise(Premise),
then, blank, blanks, rule_conclusion(Conclusion),
".", ( "\n" ; eos ),
{ Rule = rule(when(Premise), Conclusion, Description) }.
rule_scope(ScopeResult) -->
single_rule_scope(Result),
{ ScopeResult = Result }.
rule_scope(Scope) -->
single_rule_scope(OpeningScope),
and, blank, blanks, rule_scope(ClosingScope),
{ Scope = and(OpeningScope, ClosingScope) }.
rule_scope(Scope) -->
single_rule_scope(OpeningScope),
or, blank, blanks, rule_scope(ClosingScope),
{ Scope = or(OpeningScope, ClosingScope) }.
analysis_definition
add_single_definition(
rule(_,then(Conclusion),
description(Short, Long))
) :-
Short = short(ShortLabel),
Long = long(LongLabel),
extractLogLevel(Conclusion, LogLevel),
assert(
analysis_api:analysis_definition(
ShortLabel,
onSave,
LogLevel,
'Testability',
LongLabel
)
).
analysis_result
create_single_analysis_result(Premise, Description) :-
Description = description(short(RuleID), _),
assert(
analysis_api:analysis_result(
RuleID,
ResultGroup,
Result
) :-
(
analyse_rule(Premise, AnalysisResult),
generate_match_term(AnalysisResult, MatchTerm),
MatchTerm = result(ResultGroup, RuleElements),
mark_match(RuleElements, Result)
)
).
analyse_rule(when(Premise), Result) :- find_compilation_unit_id(_, CompilationClassID), apply_premise(Premise, CompilationClassID, PremiseResult), Result = PremiseResult. apply_premise(Scope, Context, Result) :- apply_scope(Scope, Context, ScopeResult), filter_unbound(ScopeResult, BoundScopeResult), Result = match(class(Context), scopes(BoundScopeResult)). apply_scope(Scope, Context, [ScopeResult]) :- apply_single_scope(Scope, Context, ScopeResult).
apply_single_scope(constructor(Condition),
Context, Result) :-
constructorT(ConstructorID, Context, _ParamList,
_ExceptionList, _ParamTypeList, _BodyID),
classT(Context, _, ClassName, _, _),
is_direct_child(ConstructorID, Context),
apply_condition(Condition, Context,
ConstructorID, ConditionResult),
Result = constructor(
id(ConstructorID), name(ClassName),
hints(ConditionResult)
); fail.
apply_single_scope(method(Condition), Context, Result) :- /* ... */
apply_single_scope(class(Condition), Context, Result) :- /* ... */