sonar-delphi
sonar-delphi copied to clipboard
File-level scan failure on anonymous method declaration with a calling convention
Prerequisites
- [X] This bug is in SonarDelphi, not SonarQube or my Delphi code.
- [X] This bug has not already been reported.
SonarDelphi version
1.1.0
SonarQube version
No response
Issue description
Delphi secretly allows you to specify the calling convention for an anonymous method:
Foo := function(const P1: Integer): Boolean stdcall
begin
Result := True;
end;
SonarDelphi's grammar does not support this. There are occurrences of this pattern in the FMX source code, resulting in parsing errors when scanning code that references the FMX.WebBrowser.Win unit.
FWIW I'm not sure whether this has any effect or not - I haven't ever seen any reference to this, either officially or in the community.
Steps to reproduce
Scan the below Delphi code, or a unit including FMX.WebBrowser.Win.
Minimal Delphi code exhibiting the issue
unit MyUnit;
interface
implementation
uses System.SysUtils;
type
TStdFunc<T1, T2> = reference to function(const A: T1): T2 stdcall;
var
Foo: TStdFunc<Integer, Boolean>;
begin
// Cannot be assigned to TFunc<Integer, Boolean> because calling convention is different
Foo := function(const P1: Integer): Boolean stdcall
begin
Result := True;
end;
WriteLn(Foo(1));
end.
Some more observations:
- Explicitly specifying the
registercalling convention results in the same type as specifying no calling convention at all -
near,far, andexportare all syntactically valid directives but have no effect - Any number of directives can be specified, only the last one has any effect
-
winapiis not a calling convention in its own right (e.g. on Windows it resolves tostdcall) - The five true calling conventions (
register,pascal,cdecl,stdcall, andsafecall) are the five type differentiators - Routines cannot be converted to a type with a different calling convention
In summary, seems like the behaviour is generally as expected (better safe than sorry!).
From testing:
implicit register -> register
explicit register -> register
pascal -> pascal
cdecl -> cdecl
stdcall -> stdcall
safecall -> safecall
winapi -> stdcall
Test file
unit Unit7;
interface
implementation
uses System.SysUtils;
type
TRegister = reference to function: string register;
TPascal = reference to function: string pascal;
TCDecl = reference to function: string cdecl;
TStdCall = reference to function: string stdcall;
TSafeCall = reference to function: string safecall;
procedure Foo(F: TRegister); overload;
begin
Writeln(Format('%s -> register', [F]));
end;
procedure Foo(F: TPascal); overload;
begin
Writeln(Format('%s -> pascal', [F]));
end;
procedure Foo(F: TCDecl); overload;
begin
Writeln(Format('%s -> cdecl', [F]));
end;
procedure Foo(F: TStdCall); overload;
begin
Writeln(Format('%s -> stdcall', [F]));
end;
procedure Foo(F: TSafeCall); overload;
begin
Writeln(Format('%s -> safecall', [F]));
end;
begin
Foo(function: string
begin
Result := 'implicit register';
end);
Foo(function: string register
begin
Result := 'explicit register';
end);
Foo(function: string pascal
begin
Result := 'pascal';
end);
Foo(function: string cdecl
begin
Result := 'cdecl';
end);
Foo(function: string stdcall
begin
Result := 'stdcall';
end);
Foo(function: string safecall
begin
Result := 'safecall';
end);
Foo(function: string winapi
begin
Result := 'winapi';
end);
end.