- 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) :- /* ... */