Skip to content

Commit 1a090fb

Browse files
committed
Merge branch 'peron/funcall-cxx-call-chains' into 'master'
Instrument.C(funcall): Instrument C++ method calls with generic witness See merge request eng/das/cov/gnatcoverage!790 Fixes C++ case of #397
2 parents b0013c8 + 91963df commit 1a090fb

File tree

13 files changed

+399
-92
lines changed

13 files changed

+399
-92
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#pragma once
2+
#include <cstdlib>
3+
4+
class A
5+
{
6+
int _x;
7+
8+
public:
9+
A (int x) : _x (x){};
10+
11+
int
12+
get_x () const
13+
{
14+
return this->_x;
15+
}
16+
};
17+
18+
class B
19+
{
20+
A _a;
21+
22+
public:
23+
B (A a) : _a (a){};
24+
25+
A
26+
get_a () const
27+
{
28+
// Purposely exit early after dumping the buffers.
29+
/* GNATCOV_DUMP_BUFFERS */
30+
exit (0);
31+
return this->_a;
32+
}
33+
};
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#include "pkg.h"
2+
3+
int
4+
main (void)
5+
{
6+
A a (5); // # var-decl
7+
B b (a); // # var-decl
8+
9+
// .get_a() exits the program with 0 status code.
10+
b // # var-ref
11+
.get_a (); // # method-call
12+
13+
// Program should not reach this point.
14+
}
15+
16+
//# test_exiting_method.cpp
17+
// /var-decl/ l+ ## 0
18+
// /var-ref/ l+ ## 0
19+
// /method-call/ l+ ## 0
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#include "pkg.h"
2+
3+
int
4+
main (void)
5+
{
6+
A a (5); // # var-decl
7+
B b (a); // # var-decl
8+
9+
// .get_a() exits the program with 0 status code.
10+
b // # var-ref
11+
.get_a () // # method-call
12+
.get_x (); // # not-reached
13+
14+
/* GNATCOV_DUMP_BUFFERS */
15+
}
16+
17+
//# test_shortcut_method_chain.cpp
18+
// /var-decl/ l+ ## 0
19+
// /var-ref/ l+ ## 0
20+
// /method-call/ l+ ## 0
21+
// /not-reached/ l! ## c-
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#include "pkg.h"
2+
3+
int
4+
main (void)
5+
{
6+
A a (5); // # var-decl
7+
8+
a // # var-ref
9+
.get_x (); // # method-call
10+
11+
/* GNATCOV_DUMP_BUFFERS */
12+
}
13+
14+
//# test_simple_call.cpp
15+
// /var-decl/ l+ ## 0
16+
// /var-ref/ l+ ## 0
17+
// /method-call/ l+ ## 0
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
"""
2+
This test ensures that method call instrumentation behaves correctly and
3+
produces the expected coverage.
4+
"""
5+
6+
from SCOV.tc import TestCase
7+
from SCOV.tctl import CAT, CovControl
8+
from SUITE.context import thistest
9+
10+
TestCase(category=CAT.stmt, fun_call_lvl=True).run(
11+
covcontrol=CovControl(dump_trigger="manual")
12+
)
13+
thistest.result()
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
class A
2+
{
3+
int _x;
4+
5+
public:
6+
A (int x) : _x (x){};
7+
8+
int
9+
get_x () const
10+
{
11+
return this->_x;
12+
}
13+
14+
int
15+
get_x_plus_one () const
16+
{
17+
return get_x () + 1; // # non-prefix-call
18+
}
19+
};
20+
21+
int
22+
main (void)
23+
{
24+
A (5).get_x_plus_one (); // # method-call
25+
}
26+
27+
//# test_non_prefix.cpp
28+
// /non-prefix-call/ l+ ## 0
29+
// /method-call/ l+ ## 0
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
"""
2+
This test makes sure that the instrumentation of non-prefixed method calls
3+
works correctly.
4+
"""
5+
6+
from SCOV.tc import TestCase
7+
from SCOV.tctl import CAT
8+
from SUITE.context import thistest
9+
10+
TestCase(category=CAT.stmt, fun_call_lvl=True).run()
11+
thistest.result()

tools/gnatcov/clang-extensions.adb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,19 @@ package body Clang.Extensions is
8585
return Is_Instrumentable_Call_Expr_C (C) /= 0;
8686
end Is_Instrumentable_Call_Expr;
8787

88+
-------------------------------
89+
-- Is_CXX_Member_Call_Expr --
90+
-------------------------------
91+
92+
function Is_Prefixed_CXX_Member_Call_Expr (C : Cursor_T) return Boolean is
93+
function Is_CXX_Member_Call_Expr_C (C : Cursor_T) return unsigned
94+
with
95+
Import, Convention => C,
96+
External_Name => "clang_isPrefixedCXXMemberCallExpr";
97+
begin
98+
return Is_CXX_Member_Call_Expr_C (C) /= 0;
99+
end Is_Prefixed_CXX_Member_Call_Expr;
100+
88101
--------------------
89102
-- Get_Opcode_Str --
90103
--------------------

tools/gnatcov/clang-extensions.ads

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,33 @@ package Clang.Extensions is
130130
-- TODO??? Actually decide what to do for the rest, so Ctor/Dtor call
131131
-- coverage makes sense.
132132

133+
function Is_Prefixed_CXX_Member_Call_Expr (C : Cursor_T) return Boolean
134+
with Inline;
135+
-- Return True if the given cursor is a statement with type
136+
-- Stmt::CXXMemberCallExprClass, and if it is a prefixed method call
137+
-- (meaning not a method that is called from the body of another method
138+
-- in which it is possible to simply omit `this->`).
139+
140+
function Get_CXX_Member_Call_Expr_SCO_Sloc_Range
141+
(C : Cursor_T) return Source_Range_T
142+
with
143+
Import,
144+
Convention => C,
145+
External_Name => "clang_getCXXMemberCallExprSCOSlocRange";
146+
-- Assume the given cursor is a Stmt::CXXMemberCallExprClass.
147+
-- Given the expression is `Foo.Bar(Baz)`, it will return a source range
148+
-- containing `.Bar`.
149+
150+
function Get_CXX_Member_Call_Expr_Base_Sloc_Range
151+
(C : Cursor_T) return Source_Range_T
152+
with
153+
Import,
154+
Convention => C,
155+
External_Name => "clang_getCXXMemberCallExprBaseSlocRange";
156+
-- Assume the given cursor is a Stmt::CXXMemberCallExprClass.
157+
-- Given the expression is `Foo.Bar(Baz)`, it will return the source
158+
-- range of `Foo`.
159+
133160
function Is_Constexpr (C : Cursor_T) return Boolean with Inline;
134161

135162
function Unwrap (C : Cursor_T) return Cursor_T

tools/gnatcov/clang-wrapper.cc

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
/* Make sure we refer to the static version of symbols on Windows, not to DLL
2121
importers. */
2222

23+
#include "clang-c/CXSourceLocation.h"
24+
#include "clang/AST/Expr.h"
2325
#define CINDEX_NO_EXPORTS
2426

2527
#include "libclang/CXCursor.h"
@@ -33,6 +35,7 @@
3335
#include "clang/AST/ExprCXX.h"
3436
#include "clang/AST/ParentMapContext.h"
3537
#include "clang/AST/StmtCXX.h"
38+
#include "clang/Basic/CharInfo.h"
3639
#include "clang/Basic/SourceLocation.h"
3740
#include "clang/Basic/SourceManager.h"
3841
#include "clang/Frontend/ASTUnit.h"
@@ -351,7 +354,7 @@ clang_getLBracLocPlusOne (CXCursor C)
351354
return translateSLoc (TU, S->getLBracLoc ().getLocWithOffset (1));
352355
}
353356

354-
extern "C" bool
357+
extern "C" unsigned
355358
clang_isInstrumentableCallExpr (CXCursor C)
356359
{
357360
if (!clang_isExpression (C.kind))
@@ -382,6 +385,76 @@ clang_isInstrumentableCallExpr (CXCursor C)
382385
}
383386
}
384387

388+
// Return true if the cursor is C++ Method Call with an explicit base.
389+
extern "C" unsigned
390+
clang_isPrefixedCXXMemberCallExpr (CXCursor C)
391+
{
392+
if (!clang_isExpression (C.kind))
393+
return false;
394+
395+
const Expr *E = getCursorExpr (C);
396+
if (E->getStmtClass () != Stmt::CXXMemberCallExprClass)
397+
return false;
398+
399+
const CXXMemberCallExpr *MCE = cast<CXXMemberCallExpr> (E);
400+
const MemberExpr *ME = cast<MemberExpr> (MCE->getCallee ());
401+
402+
return !ME->isImplicitAccess ();
403+
}
404+
405+
extern "C" CXSourceRange
406+
clang_getCXXMemberCallExprSCOSlocRange (CXCursor C)
407+
{
408+
if (!clang_isExpression (C.kind))
409+
return clang_getNullRange ();
410+
411+
const Expr *E = getCursorExpr (C);
412+
if (E->getStmtClass () != Stmt::CXXMemberCallExprClass)
413+
return clang_getNullRange ();
414+
415+
const CXXMemberCallExpr *MCE = cast<CXXMemberCallExpr> (E);
416+
const MemberExpr *ME = cast<MemberExpr> (MCE->getCallee ());
417+
418+
ASTContext &ctx = getContext (C);
419+
420+
const SourceLocation start_loc = ME->getOperatorLoc ();
421+
const SourceLocation end_loc = ME->getMemberLoc ().getLocWithOffset (
422+
((long) ME->getMemberDecl ()->getName ().size ()) - 1);
423+
424+
// Check for validity on both sides.
425+
// Specifically, start loc can fail if there is no operator, if the method
426+
// call is made from another method and thus not prefixed by `object.` or
427+
// `object->`
428+
if (start_loc.isInvalid () || end_loc.isInvalid ())
429+
return clang_getNullRange ();
430+
431+
// Do not use the translateSourceRange wrapper because the token
432+
// delimitation is not right for us.
433+
return translateSourceRange (
434+
ctx.getSourceManager (), ctx.getLangOpts (),
435+
CharSourceRange::getCharRange (SourceRange (start_loc, end_loc)));
436+
}
437+
438+
extern "C" CXSourceRange
439+
clang_getCXXMemberCallExprBaseSlocRange (CXCursor C)
440+
{
441+
if (!clang_isExpression (C.kind))
442+
return clang_getNullRange ();
443+
444+
const Expr *E = getCursorExpr (C);
445+
if (E->getStmtClass () != Stmt::CXXMemberCallExprClass)
446+
return clang_getNullRange ();
447+
448+
const CXXMemberCallExpr *MCE = cast<CXXMemberCallExpr> (E);
449+
const MemberExpr *ME = cast<MemberExpr> (MCE->getCallee ());
450+
451+
const SourceLocation start_loc = ME->getBase ()->getBeginLoc ();
452+
const SourceLocation end_loc = ME->getBase ()->getEndLoc ();
453+
454+
return translateSourceRange (getContext (C),
455+
SourceRange (start_loc, end_loc));
456+
}
457+
385458
extern "C" CXSourceRange
386459
clang_getFunctionSignatureSloc (CXCursor C)
387460
{

0 commit comments

Comments
 (0)