Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 12 Next »


This plugin is donation of Sabre Airline Solutions

Delphi Sonar plugin enables analysis of projects written using Delphi or Pascal. It was tested with projects written in Delphi 6, 7, 2006 and XE.

It is using grammar in ANTLR v3 format. It is extended on grammar found at http://www.dragonkiller.nl/Delphi/delphi2009.txt

TRY THE PLUGIN

You can find a sample project in "src/Sample/SampleProject". It contains an Ant script, which you can use to do the analysis. You must have ANT installed.

Install the Delphi plugin on your Sonar instance, and type "ant sonar" from sample project directory in order to try the plugin.

IMPLEMENTED FEATURES

  1. Counting lines of code, statements, number of files
  2. Counting number of classes, number of packages, methods, accessors
  3. Counting number of public API (methods, classes and fields)
  4. Counting comments ratio, comment lines (including blank lines)
  5. CPD (code duplication, how many lines, block and in how many files)
  6. Code Complexity (per method, class, file; complexity distribution over methods, classes and files)
  7. LCOM4 and RFC
  8. Code colorization
  9. Unit tests reports
  10. Assembler syntax in grammar
  11. Include statement
  12. Parsing preprocessor statements
  13. Rules
  14. Code coverage reports
  15. Source code highlight for unit tests
  16. “Dead” code recognition
  17. Unused files recognition

NOT IMPLEMENTED FEATURES (TODO)

  1. Package tangle index

CODE ASSUMPTIONS

  1. Grammar has problems with recognizing certain identifiers. When we add whitespace (“ “) at several places, everything works fine. It was difficult to solve this problem from the grammar level, so it is solved in DelphiSourceSanitizer class. It adds whitespaces where there are needed in parsed file, and grammar works fine. It is used for:
    1. Colon, “:” -> “ :”
    2. Array range, “x..y” -> “x .. y”
  2. Grammar is NOT case insensitive, but Delphi code is. Plugin deals with it by DelphiSourceSanitizer class, which feeds ANTLR parser lowercase characters (the “LA” method)
  3. Number of classes includes: classes, records
  4. Directory is count as a package. Number of packages equals number of directories.
  5. Preprocessor definitions between {$if xxx} and {$ifend} are removed (DefineResolver class).
  6. Sources imported to Sonar are parsed through IncludeResolver class. It means, that the source will be lowercased and unknown preprocessor definitions will be cut out.

GRAMMAR ASSUMPTIONS

All assumptions in grammar file are commented with //ASSUMPTION comment.

  1. “ident” rule – was extended, now it uses some keywords that were used as variable names in source code, ex. “name”, “readonly”, “index”. If you use more keywords as variable names or function names, you should extend the rule further.
  2. The grammar has problem with proper recognizing identifier that ends with “:” (“e:” does not work, does not recognize “e” as identifier), see code assumption 1 for solution.
  3. The grammar has problem with proper array range recognizing (“array[1..2]” does not work), see code assumption 1 for solution.
  4. The grammar is not case-insensitive, and it should be. For parsing files using ANTLRWorks, convert them to lowercase. Plugin deals with it automatically, by AntlrNoCaseFileStream class.

MOST COMMON GRAMMAR PROBLEMS (FIXED):

Below is the list of the most common grammar problems. You can also look at file GrammarTest.pas to get more detailed information.

  1. On e: exception do
  2. Name = procedure (x: integer) of object;
  3. Using keywords as variable names (ex. continue,  message, name, db)
  4. Array[1..2]
  5. @address = someProcedure(x,y);
  6. (expression).namespace.ident = x;
  7. Default function argument values crashed AbstractAnalyser
  8. Inherited;
  9. if (tempstr[1] in ['0' .. '9', 'a' .. 'z'] = false) then
  10. str: string[3];
  11. function tfilewriter.writebytes(var ibytes; isize : dword) : boolean;
  12. DetailedDescription    : array of Byte;
  13. ferrorlist : tlist<string>;
  14. Delphi complier does not require “;” at the end of except statement:

On e: exception do

Begin

End

DEBUGGING GRAMMAR FROM ANTLRWORKS

If you want to debug grammar from AntlrWorks, do the following:

  1. Convert file to be parsed to lowercase
  2. Apply all transformations mentioned in code assumption 1.
  3. Use more Java heap space while running AntlrWorks, ex. 1024M (java –Xmx1024m)

DUNIT  TESTS

You should put transformed DUnit xml files to directory specified in build.xml file, under sonar.surefire.reportsPath parameter. You can specify multiply directories separated by ‘,’. Then all specified directories will be parsed. The path can be either absolute or relative to the project main path.

The xml files should be renamed with “TEST-“ prefix, example: “dunit.xml” -> “TEST-dunit.xml”.

CODE COVERAGE WITH AQTIME

CodeCoverageSensor class is responsible for running CC analysis. It creates a parser that connects to specified data base holding AQTime generated CC report. Connection properties can be specified in a build.xml or pom.xml file (see below). If parser can’t connect to the data base, analysis is skipped.

You can specify exclude directories in build xml file, under sonar.delphi.codecoverage.excluded parameter. The files in specified directories will be not checked for code coverage.

DELPHICSOURCESANITIZER CLASS

This class prepares source code for parsing. It does the following:

-          Parse preprocessor definitions (using DefineResolver class)

-          Add include files (using IncludeResolver class)

-          “Fix” the code as explained in code assumption 1 (using SourceFixcerResolver class).

-          Feed ANTLR parser with lowercase characters (code assumption 2).

To properly do this things, the class firstly searches for areas that should not be parsed: comments and single quoted strings. Why? For example, you can have such piece of code:

X := 5;    //{$include file.inc}
S := ‘my {$include file.inc} string’;

Without recognizing which areas to exclude, the parser would simply copy-paste the file “file.inc” into a comment or a string, and this would produce an error.

ANT BUILD.XML / MAVEN POM.XML

There are few additional project parameters that you can set in Ant’s build.xml.

KEY

DESCRIPTION

EXAMPLE

sonar.delphi.sources.excluded

Excluded directories, they won’t be parsed and metrics won’t be calculated for them. Path’s can be relative to main project directory or absolute. Separate them with ‘,’.

Default value is “”.

<property key="sonar.delphi.sources.excluded " value="excluded\directory, another\excluded,c:\something" />

 

sonar.delphi.codecoverage.excluded

Code coverage excluded directories list. Files in those directories will not be checked for code coverage. Default value is “”.

<property key="sonar.delphi.codecoverage.excluded" value="cc\test\excluded,c:\sth" />

sonar.delphi.sources.include

Path to include directories for files included with {$include} or {$i} directive.

Default value is “”.

<property key="sonar.delphi.sources.include" value="includes_dir,includes_dir2" />

sonar.delphi.sources.include.extend

Should we extend (copy-paste) include files? Possible values are “true” or “false”.  

Default value is “true”.

<property key="sonar.delphi.sources.include.extend" value="true">

sonar.delphi.sources.projects

Project file. If provided, will be parsed for include lookup path, project source files and preprocessor definitions. Default value is “”.

<property key=" sonar.delphi.sources.project" value="projects\myProject.dproj">

sonar.delphi.sources.workgroup

Workgroup file. If provided, will be parsed, then all *.dproj files found in workgroup file will be parsed.

<property key=" sonar.delphi.sources.workgroup" value="projects\All.groupproj">

sonar.delphi.codecoverage.aqtime.jdbc.driver

Class name for JDBC driver. Default value is “net.sourceforge.jtds.jdbc.Driver”

<property key="sonar.delphi.codecoverage.aqtime.jdbc.driver" value="net.sourceforge.jtds.jdbc.Driver" />

sonar.delphi.codecoverage.aqtime.jdbc.url

Database connection url. Default value is “”.

<property key="sonar.delphi.codecoverage.aqtime.jdbc.url" value="jdbc:jtds:sqlserver://localhost" />

sonar.delphi.codecoverage.aqtime.jdbc.user

Database user name. Default value is “”.

<property key="sonar.delphi.codecoverage.aqtime.jdbc.user" value="admin" />

sonar.delphi.codecoverage.aqtime.jdbc.password

Database user password. Default value is “”.

<property key="sonar.delphi.codecoverage.aqtime.jdbc.password" value="pass" />

sonar.delphi.codecoverage.aqtime.jdbc.prefixDatabase prefix for connecting to AQTime database. Default value is "".<property key="sonar.delphi.codecoverage.aqtime.jdbc.prefix"   value="CC.dbo." />

 

CUSTOM PMD RULES

If you want to make a custom PMD rule, you can do it in two ways:

WRITE YOUR OWN CLASS

Create rule class, that extends from org.sonar.plugins.delphi.pmd.rules.DelphiRule class. You will need to overload visit(DelphiPMDNode node, Object data) method. You will have the current ast tree node and whole ast tree to your disposal. If you want to save a violation, invoke addViolation(Object data, DelphiPMDNode node) method.

If you want to initialize some class variables at start of each file, overload the init() method. Look at the org.sonar.plugins.delphi.pmd.rules package for sample classes.

After you write your class, modify the “rules.xml” and “default-delphi-profile.xml”, simply add your class to the list.

WRITE XPATH SENTENCE

You can write an XPath sentence which will parse the whole file. Open the “rules.xml” file, copy one of the XPath rules already available and change the “xpath” property to your XPath string. Don’t forget to add the rule to “default-delphi-profile.xml”. That’s it!

IMPLEMENTED RULES

For full summary (description, examples) of custom PMD rules, do the following: execute Sonar -> Configuration -> Default Delphi Profile and browse the rules under Delphi profile.

List of implemented rules, as follows:

RULE NAME

RULE PRIORITY

1.       Then Try Rule (you should place ‘begin’ before ‘try’)

3

2.       One Class Per File Rule (one class per file permitted)

3

3.       Inherited Method With No Code Rule (method that only inherits behavior)

2

4.       Empty Except Block Rule (empty ‘except’ block, catching no exceptions)

1

5.       Empty Interface Rule (empty interface)

2

6.       Empty Then Statement Rule (empty ‘then’ statement)

2

7.       Empty Begin Statement Rule (empty ‘begin’ .. ‘end’ statement)

2

8.       Empty Else Statement Rule (empty ‘else’ statement)

2

9.       Too Long Method Rule (method too long, above certain line limit)

3

10.   Too Many Arguments Rule (to many function/procedure arguments)

3

11.   Too Many Variables Rule (to many variables in a function/procedure)

3

12.   No Semi After Overload Rule (no semicolon ‘;’ after ‘overload’ keyword)

4

13.   No Function Return Type Rule (function with no return type specified)

3

14.   Interface Name Rule (interface name should begin with ‘I’ letter)

4

15.   Class Name Rule (class name should begin with ‘T’ letter)

4

16.   Record Name Rule (record name should begin with ‘T’ letter)

4

17.   Catching General Exception Rule (catching Exception class in ‘except’ statement)

3

18.   Raising General Exception Rule (raising Exception class in ‘raise’ statement)

3

19.   Avoid Out Parameter Rule (using ‘out’ arguments)

4

20.   If True Rule (convert ‘if(x=true) then’ to ‘if (x) then’)

4

21.   If Not False Rule (convert ‘if not (x = false) then’ to ‘if (x) then’

4

22.   Public Field Rule (class fields should NOT be public)

4

23.   Avoid Unused Method Parameters Rule (avoid making function arguments that are not used in body)

3

24.   No Interface Guid (no Guid at interface)

5

25.   If Assigned And Free (before calling ‘Free’ you don’t need to check for variable assignment ‘x <> nil’)

4

26.   Project File No Functions Rule (.dpr file should not contain procedures or functions)

3

27.   Project File No Variables Rule (.dpr file should not contain variables)

3

28.   Type Alias Rule (avoid using type aliases)

4

29.   Uppercase Reserved Keywords Rule (avoid using uppercase reserved keywords)

5

30.   Mixed Names Rule (avoid case mixing names of functions / variables)

4

31.   Unused Unit Rule (searches for units that are not referenced in other units)

3

32.   Unused Function Rule (searches for functions/procedures that are not used by other functions/procedures)

3

33.   Constructor Without Inherited Statement Rule (constructor does not contain ‘inherited’ statement)

2

34.   Destructor Without Inherited Statement Rule (destructor  does not contain ‘inherited’ statement)

2

35.   No ‘begin’ after ‘do Statement Rule

4

36.   ‘With’ after ‘do’ statement Rule

3

 

FUTURE WORK

  1. The org.sonar.squid.text.delphi package was copy-pasted from org.sonar.squid.text with modifications to work with Delphi source code. Hence some duplications are present. The whole package should be refactored, so duplications could be removed.
  2. We made a "hack" of PMD engine to make it run with Delphi code. When we made it, PMD didn't allow to work with other than Java code. Refactoring steps should be taken to migrate it to newest available version (with multi-language support).



  • No labels