@@ -98,23 +98,22 @@ void Init()
9898 if ( ! String . IsNullOrEmpty ( this . FileID ) ) {
9999 this . GetSequencePoints ( ) ;
100100 this . GetSequencePointsContent ( ) ;
101- this . getBodyStartSP ( ) ; // before OrderBy Line/Col
102- this . getBodyFinalSP ( ) ; // before orderBy Line/Col
103- //this.FilterSequencePoints(); // before orderBy Line/Col
101+ this . getBodyStartSP ( ) ;
102+ this . getBodyFinalSP ( ) ;
104103 this . GetBranchRatio ( ) ;
105104 this . GetBranchCoverage ( ) ;
106105
107106 // SP's are originaly ordered by CIL offset
108- // but ccrewrite can move offset of
109- // Contract.Requires before method signature SP { and
110- // Contract.Ensures after method closing SP }
107+ // because ccrewrite can move offset of
108+ // Contract.Requires before method start ({) SP offset
109+ // Contract.Ensures after method final (}) SP offset
111110 // So sort SP's back by line/column
112111 this . SequencePoints . OrderBy ( item => item . Line ) . OrderBy ( item => item . Column ) ;
113112 }
114113 }
115114
116- private static var cacheGetSource_LastFileName = ( string ) null ;
117- private static var cacheGetSource_LastSource = ( CodeCoverageStringTextSource ) null ;
115+ private static string cacheGetSource_LastFileName = null ;
116+ private static CodeCoverageStringTextSource cacheGetSource_LastSource = null ;
118117
119118 static CodeCoverageStringTextSource GetSource ( string filename ) {
120119
@@ -159,12 +158,12 @@ void GetSequencePoints() {
159158 sp . Document = xSPoint . Attribute ( "fileid" ) . Value ?? "" ;
160159 }
161160 else if ( sp . FileID == this . FileID ) {
162- // This method SequencePoint (from this.FileName)
161+ // This method SequencePoint (from this.FileName)
163162 sp . Document = this . FileName ;
164163 }
165164 else {
166165 // SequencePoint from another method/file
167- // ie: ccrewriten CodeContractClass/CodeContractClassFor
166+ // ie: ccrewriten CodeContractClass/CodeContractClassFor
168167 // [or dependency-injected or fody-weaved???]
169168 sp . Document = parent . GetFileName ( sp . FileID ) ;
170169 }
@@ -210,91 +209,49 @@ void GetSequencePointContent(CodeCoverageSequencePoint sp)
210209 }
211210 }
212211
213- // Find method-body start SequencePoint "{" (sp.Content required)
214- // Sequence points expected to be ordered by Offset
215- // Cannot just get first one because of ccrewrite&ContractClassFor
212+ // Find method-body first SequencePoint
213+ // -> this method SP with lowest Line/Column
216214 void getBodyStartSP ( ) {
217- bool startPointFound = false ;
218- CodeCoverageSequencePoint startSeqPoint = null ;
219- foreach ( CodeCoverageSequencePoint sp in this . SequencePoints ) {
220- if ( sp . Content == "{" ) {
221- if ( this . IsConstructor ) {
222- // take previous/last one if not null
223- startSeqPoint = startSeqPoint ?? sp ;
224- }
225- else {
226- startSeqPoint = sp ;
215+ if ( this . SequencePoints . Count != 0 ) {
216+ foreach ( CodeCoverageSequencePoint sp in this . SequencePoints ) {
217+ if ( sp . FileID != this . FileID ) continue ;
218+ if ( this . BodyStartSP == null || ( sp . Line < this . BodyStartSP . Line ) ||
219+ ( sp . Line == this . BodyStartSP . Line && sp . Column < this . BodyStartSP . Column )
220+ ) {
221+ this . BodyStartSP = sp ;
227222 }
228- startPointFound = true ;
229- break ;
230223 }
231- startSeqPoint = sp ;
232224 }
233- this . BodyStartSP = startPointFound ? startSeqPoint : null ;
234225 }
235226
236- // Find method-body final SequencePoint "}" (sp.Content required)
237- // Sequence points expected to be ordered by Offset
227+ // Find method-body last SequencePoint
228+ // -> this method SP.Content=="}" with highest Line/Column
229+ // and lowest Offset (when duplicated bw ccrewrite)
238230 void getBodyFinalSP ( ) {
239- CodeCoverageSequencePoint finalSeqPoint = null ;
240- foreach ( CodeCoverageSequencePoint sp in ( ( IEnumerable < CodeCoverageSequencePoint > ) this . SequencePoints ) . Reverse ( ) ) {
241- if ( sp . Content == "}" ) {
242- if ( finalSeqPoint == null ) {
243- finalSeqPoint = sp ;
244- }
245- // check for ccrewrite duplicate
246- else if ( sp . Line == finalSeqPoint . Line &&
247- sp . Column == finalSeqPoint . Column &&
248- sp . EndLine == finalSeqPoint . EndLine &&
249- sp . EndColumn == finalSeqPoint . EndColumn &&
250- sp . Offset < finalSeqPoint . Offset ) {
251- finalSeqPoint = sp ;
252- // duplicate found, so far no reason to expect "triplicate" :)
253- break ;
254- }
255- }
256- }
257- this . BodyFinalSP = finalSeqPoint ;
258- }
259-
260- void FilterSequencePoints ( ) {
261-
262- if ( this . SequencePoints . Count != 0 &&
263- this . BodyStartSP != null &&
264- this . BodyFinalSP != null ) {
265-
266- // After ccrewrite ContractClass/ContractClassFor
267- // sequence point(s) from another file/class/method
268- // is inserted into this method sequence points
269- //
270- // To remove alien sequence points, all sequence points on lines
271- // before method signature and after end-brackets xxx{} are removed
272- // If ContractClassFor is in another file but interleaves this method lines
273- // then, afaik, not much can be done to remove inserted alien SP's
274- var selected = new List < CodeCoverageSequencePoint > ( ) ;
275-
276- foreach ( var point in this . SequencePoints ) {
277-
278- // if Content.Length is 0, GetText() is failed by ccrewrite inserted invalid SequencePoint
279- if ( point . Content . Length != 0
280- && ( point . Line > BodyStartSP . Line || ( point . Line == BodyStartSP . Line && point . Column >= BodyStartSP . Column ) )
281- && ( point . Line < BodyFinalSP . Line || ( point . Line == BodyFinalSP . Line && point . Column < BodyFinalSP . Column ) )
282- ) {
283- selected . Add ( point ) ;
284- }
285- // After ccrewrite ContractClass/ContractClassFor
286- // duplicate method end-sequence-point "}" is added
287- //
288- // Add only first finalSP (can be a duplicate)
289- // Note: IL.Offset of second duplicate finalSP will
290- // extend branch coverage outside method-end "}",
291- // and that can lead to wrong branch coverage display!
292- if ( object . ReferenceEquals ( point , this . BodyFinalSP ) ) {
293- selected . Add ( point ) ;
231+ if ( this . SequencePoints . Count != 0 ) {
232+ for ( int i = this . SequencePoints . Count - 1 ; i > 0 ; i -- ) {
233+ var sp = this . SequencePoints [ i ] ;
234+ if ( sp . FileID != this . FileID ) continue ;
235+ if ( sp . Content != "}" ) continue ;
236+ if ( this . BodyFinalSP == null || ( sp . Line > this . BodyFinalSP . Line ) ||
237+ ( sp . Line == this . BodyFinalSP . Line && sp . Column >= this . BodyFinalSP . Column )
238+ ) {
239+ // ccrewrite ContractClass/ContractClassFor
240+ // adds duplicate method end-sequence-point "}"
241+ //
242+ // Take duplicate BodyFinalSP with lower Offset
243+ // Because IL.Offset of second duplicate
244+ // will extend branch coverage of this method
245+ // by coverage of ContractClassFor inserted SequencePoint!
246+ if ( this . BodyFinalSP != null &&
247+ sp . Line == this . BodyFinalSP . Line &&
248+ sp . Column == this . BodyFinalSP . Column &&
249+ sp . Offset < this . BodyFinalSP . Offset ) {
250+ this . SequencePoints . Remove ( this . BodyFinalSP ) ; // remove duplicate
251+ }
252+ this . BodyFinalSP = sp ;
294253 }
295254 }
296-
297- this . SequencePoints = selected ;
298255 }
299256 }
300257
@@ -309,21 +266,6 @@ int GetSequencePointsCount() {
309266 return 0 ;
310267 }
311268
312- void GetBranchPoints ( ) {
313- // get all BranchPoints
314- var xBPoints = this . element
315- . Elements ( "BranchPoints" )
316- . Elements ( "BranchPoint" ) ;
317- foreach ( XElement xBPoint in xBPoints ) {
318- CodeCoverageBranchPoint bp = new CodeCoverageBranchPoint ( ) ;
319- bp . VisitCount = ( int ) GetDecimalAttributeValue ( xBPoint . Attribute ( "vc" ) ) ;
320- bp . Offset = ( int ) GetDecimalAttributeValue ( xBPoint . Attribute ( "offset" ) ) ;
321- bp . Path = ( int ) GetDecimalAttributeValue ( xBPoint . Attribute ( "path" ) ) ;
322- bp . OffsetEnd = ( int ) GetDecimalAttributeValue ( xBPoint . Attribute ( "offsetend" ) ) ;
323- this . BranchPoints . Add ( bp ) ;
324- }
325- }
326-
327269 void GetBranchRatio ( ) {
328270
329271 this . BranchCoverageRatio = null ;
@@ -344,7 +286,7 @@ void GetBranchRatio () {
344286 foreach ( var sp in this . SequencePoints ) {
345287
346288 // SequencePoint is visited and belongs to this method?
347- if ( sp . VisitCount != 0 && sp . Document == this . FileName ) {
289+ if ( sp . VisitCount != 0 && sp . FileID == this . FileID ) {
348290
349291 // Don't want branch coverage of ccrewrite(n)
350292 // SequencePoint's with offset before and after method body
0 commit comments