Patent Publication Number: US-9904541-B2

Title: Semantic baselining

Description:
BACKGROUND 
     Computer programs developed by program developers are oftentimes relatively large and complex. For instance, source code of an operating system can include hundreds of thousands to millions of lines of code, wherein different portions of the code are written by different developers over varying time frames. Because developers are often unable to surmise all possible executions through source code, unforeseen errors can occur at runtime of a program. For example, certain variable values, events, execution paths, some combination thereof, etc. may cause the program to operate in an unexpected/undesirable manner when executed (e.g., the program includes potential bugs). To detect bugs, static analysis and verification tools have been developed. 
     A static analysis and verification tool analyzes computer-executable code to identify potential bugs that may occur during program execution, such as null pointer exceptions, division by zero errors, array out of bounds errors, and the like. In certain instances, a developer provided with warnings may wish to ignore such warnings, as the bugs may be spurious or not worth fixing (e.g., fixing a bug may cause more severe problems in the program). Going forward, then, the developer may wish to have these “old” warnings suppressed, as the developer has already reviewed the warnings, and re-provision of the same warnings is superfluous. In other words, suppressing warnings may allow the developer to be notified only of issues caused by the addition of new features to the program. 
     Using conventional static analysis and verification tools, the options of the developer are either to 1) add assume statements to the code to suppress each undesired warning; or 2) suppress warnings via attributes or alarm filter files. These approaches have numerous drawbacks. For instance, the first option allows the developer to make assumptions explicit, but this is only an advantage if such assumptions are interesting to the developer. Moreover, inserting assumptions requires manual effort proportional to the number of warnings emitted by the tool that are desirably suppressed by the developer. Still further, the developer must manually refactor any assumptions as changes to the program are made. With respect to the second option noted above, warnings can be suppressed by way of syntactic matches between warning messages, warning types, or warning locations. Thus, for instance, the developer can indicate that any warnings at line i of a program is to be suppressed when new versions of the program are analyzed. 
     With more generality, the developer can add a suppression attribute (or an entry in an alarm filter file) after manual inspection of a warning; such syntax-based suppressions, however, can be quite brittle, in that small changes to a program can cause syntactic matching to fail. Failed suppression can then cause alarms to be re-reported that have already been classified as spurious by the developer, thereby causing confusion and irritation. Further, using syntactic matching may unintentionally suppress warnings that are, in fact, genuinely new (e.g., caused by a revision to a program). For instance, a line-based suppression strategy may desirably suppress all warnings about an assertion failure at line i of a program. Accordingly, a warning based on an assertion a 0  at line i may be suppressed in an original program. If, however, the developer inserts a new and potentially failing assertion a 1  at line i in the program, thereby shifting a 0  back to line i+1, the warning about the failure of a 1  will be (wrongly) suppressed, and the warning about the failure of a 0  will be (wrongly) reported. 
     SUMMARY 
     The following is a brief summary of subject matter that is described in greater detail herein. This summary is not intended to be limiting as to the scope of the claims. 
     Described herein are various technologies pertaining to semantic baselining, which refers to suppression of warnings in a computer-executable program as a function of semantic understanding of the causes of such warnings. With more particularity, a first version of a computer program (a baseline program) can be received and a static analysis can be executed thereover. The static analysis over the baseline program is a semantic analysis of such computer program, resulting in identification of possible bugs that may occur when the program is executed. When bugs are identified, warnings can be set forth to a developer, wherein exemplary warnings may include an indication to the developer that a null pointer exception may occur when executing the baseline program, that a divide by zero error may occur when executing the baseline program, that an array out of bounds error may occur when executing the baseline program, and the like. Each identified warning corresponds to a respective location in the baseline program. 
     A developer may then indicate that at least one warning output for the baseline program based upon the static analysis thereover is to be suppressed going forward, such that warnings deemed spurious or not worth fixing by the developer are not re-reported to the developer as the developer makes changes to the baseline program. Thus, the developer may indicate that she only wishes to be provided with potential errors caused by modifications to the baseline program. Thereafter, a second version of the computer program (a new program) can be received, which is non-identical to the baseline program. That is, the new program is in some way an update to the baseline program. 
     A developer may then indicate that at least one warning output for the baseline program based upon the static analysis thereover is to be suppressed going forward, such that warnings deemed spurious or not worth fixing by the developer are not re-reported the developer as the developer makes changes to the baseline program. Thus, the developer may indicate that she only wishes to be provided with potential errors caused by modifications to the baseline program. Thereafter, a second version of the computer program (a new program) can be received, which is non-identical to the baseline program. That is, the new program is in some way an update to the baseline program. 
     Assumptions of respective conditions inferred with respect to the baseline program may then be inserted into the new program at respective appropriate locations. When the new program is analyzed by a static analysis and verification tool, the assumptions of the semantic conditions are read and understood by the analysis tool; such conditions can act to suppress old warnings from the baseline program (the warnings desirably indicated as being suppressed by the developer). Warnings caused by modifications made by the developer to the baseline program, however, are still provided to the developer, thereby providing the developer with an opportunity to fix new bugs. 
     The above summary presents a simplified summary in order to provide a basic understanding of some aspects of the systems and/or methods discussed herein. This summary is not an extensive overview of the systems and/or methods discussed herein. It is not intended to identify key/critical elements or to delineate the scope of such systems and/or methods. Its sole purpose is to present some concepts in a simplified form as a prelude to the more detailed description that is presented later. 
    
    
     
       BRIEF DESCRIPTION OF THE DRAWINGS 
         FIG. 1  is a functional block diagram of an exemplary system that facilitates inserting assumptions of correctness conditions into a computer-executable program. 
         FIG. 2  illustrates a diagram of correctness condition extraction. 
         FIG. 3  illustrates a functional block diagram of a condition inferrer component that is configured to infer correctness conditions that cause warnings in a baseline program to be suppressed when analyzed by a static analysis and verification tool. 
         FIG. 4  is a functional block diagram of an exemplary inserter component that is configured to insert assumptions of inferred correctness conditions into a computer-executable program. 
         FIG. 5  is a flow diagram illustrating an exemplary methodology for inserting an assumption of a correctness condition into a computer-executable program. 
         FIG. 6  is a flow diagram illustrating an exemplary methodology for suppressing previously analyzed warnings output by a static analysis and verification tool. 
         FIG. 7  is an exemplary computing system. 
     
    
    
     DETAILED DESCRIPTION 
     Various technologies pertaining to warning suppression in computer-executable programs are now described with reference to the drawings, wherein like reference numerals are used to refer to like elements throughout. In the following description, for purposes of explanation, numerous specific details are set forth in order to provide a thorough understanding of one or more aspects. It may be evident, however, that such aspect(s) may be practiced without these specific details. In other instances, well-known structures and devices are shown in block diagram form in order to facilitate describing one or more aspects. Further, it is to be understood that functionality that is described as being carried out by certain system components may be performed by multiple components. Similarly, for instance, a component may be configured to perform functionality that is described as being carried out by multiple components. 
     Moreover, the term “or” is intended to mean an inclusive “or” rather than an exclusive “or.” That is, unless specified otherwise, or clear from the context, the phrase “X employs A or B” is intended to mean any of the natural inclusive permutations. That is, the phrase “X employs A or B” is satisfied by any of the following instances: X employs A; X employs B; or X employs both A and B. In addition, the articles “a” and “an” as used in this application and the appended claims should generally be construed to mean “one or more” unless specified otherwise or clear from the context to be directed to a singular form. 
     Further, as used herein, the terms “component” and “system” are intended to encompass computer-readable data storage that is configured with computer-executable instructions that cause certain functionality to be performed when executed by a processor. The computer-executable instructions may include a routine, a function, or the like. It is also to be understood that a component or system may be localized on a single device or distributed across several devices. Additionally, as used herein, the term “exemplary” is intended to mean serving as an illustration or example of something, and is not intended to indicate a preference. 
     Described herein is the notion of semantic baselining. “Baselining” refers to baselining alarms/warnings reported by static analysis and verification tools. Specifically, given two versions of a program, P and P′, the baseline problem involves reporting to the user only new alarms introduced in P′. At a high level, semantic baselining involves extraction of correctness conditions that suppress alarms in P and injection of such conditions into P′. Accordingly, semantic baselining masks alarms of P′ that have the same semantic cause as the alarms in P. As will be described herein, an abstract interpretation-based framework for cross program version condition extraction and injection formalizes semantic baselining. 
     With reference now to  FIG. 1 , an exemplary system  100  that facilitates inferring correctness conditions from a baseline program and injecting assumptions of such correctness conditions into a subsequent version of the baseline program is illustrated. The system  100  includes a data storage device  102 , which retains a first version of a computer executable program  104  (a baseline program). The data storage device  102  may be any suitable storage device, such as, but not limited to, a hard drive, a memory, a flash drive, a disk, etc. The system  100  further comprises an analyzer tool  106 , which is configured to perform static analysis and verification over computer executable programs. For instance, the analyzer tool  106  may be included in an integrated development environment. The analyzer tool  106 , when analyzing the baseline program, can perform a deep semantic analysis of the baseline program to identify possible bugs therein. To that end, the analyzer tool  106  includes a static analyzer component  108  that performs a first static analysis over the baseline program. The static analyzer component  108  can identify a plurality of warnings  110  based upon the first static analysis over the baseline program, and cause such warnings to be retained in the data storage device  102 . Each warning in the warnings  110  can correspond to a respective location (line of code) in the baseline program. Exemplary warnings may include, for example, null pointer exceptions, division by zero warnings, array out of bounds warnings, violation of assertion contracts set forth by a developer for the baseline program, etc. 
     The analyzer tool  106  may also include a condition inferrer component  112  that infers, for each warning in the warnings  110 , corresponding correctness conditions. Summarily, a correctness condition corresponding to a warning, when assumed in the baseline program, causes the warning to be suppressed when the static analyzer component  108  performs a static analysis over the baseline program. The data storage device  102  may further include a database  114 , and correctness conditions  116  inferred by the condition inferrer component  112  can be retained in the database  114 . Generally, the correctness conditions  116  are conditions that the surrounding environment of the baseline program (e.g. callers, other methods, native calls, physical environment, etc.) should satisfy, otherwise an error may occur when executing the baseline program. 
     A developer of the baseline program may indicate that at least one warning in the warnings  110  is desirably suppressed going forward, such that the developer, when revising the baseline program, is only provided with new warnings caused by revisions to the baseline program. 
     The analyzer tool  106  also comprises a receiver component  118  that receives a correctness condition (inferred by the condition inferrer component  112 ) from the database  114 . The analyzer tool  106  also includes an inserter component  120  that inserts an assumption of the condition into a second version of the program  122  (referred to as the new program). That is, the inserter component  120  inserts assumed conditions into the new program, such that the static analyzer component  108  takes the assumed conditions  124  into consideration when performing a static analysis over the new program. Accordingly, “old” errors propagated from the baseline program to the new program are suppressed, while a developer  126  is provided with “new” warnings caused by revisions included in the new program. In an exemplary embodiment, the inserter component  120  can insert an assumption of a condition at a method call in the new program. In another example, the inserter component  120  can insert an assumption of a condition at a function entry point in the new program. The analyzer tool  106  can cause such new warnings to be presented to the developer  126  on a display screen  128  of a computing device. Subsequently, the developer  126  can choose whether or not to fix these newly existent warnings. 
     Thus, the static analyzer component  108  can perform a second static analysis over the new program, wherein the new program includes assumed conditions  124  corresponding to the respective warnings  110  identified when the first static analysis was undertaken over the baseline program. Therefore, if the static analyzer component  108  outputs a warning to the developer  126  and displays such warning on the display screen  128 , such warning indicates to the developer  126  that a revision existent in the new program  122  has caused such warning. If, however, the static analyzer component  108  fails to output a particular warning when performing a static analysis over the new program, then it can be determined that such warning has been suppressed. 
     Additional detail pertaining to operation of the system  100  is now provided. As indicated, the data storage device  102  includes two versions of a program: the baseline program (P), and an updated new program (P′). The static analyzer component  108  outputs the warnings  110  (a p ) based upon a static analysis over P, and (if warnings are not suppressed) outputs warnings a p′  for P′. When suppression of a p  is desired for the new program, the system  100  is configured to emit the set of warnings a p /a p′  thus presenting only new alarms to the developer  126 . 
     If an oracle function δ were known that maps program points from P to program points from P′, solving the baselining problem would be relatively easy. For instance, for a warning at line i in P, the warning at line δ(i) in P′ can be identified and suppressed. Constructing the δ function, however, is difficult in practice, and relies on syntactic similarities between the two versions of the program that may or may not correspond to semantic similarities. The condition inferrer component  112  is configured to extract correctness conditions ε for the baseline program and install ε into the new program, thereby producing an instrumented program P′ ε . The analyzer tool  106  then emits the set of alarms reported for P′ ε . 
     Although a complete δ may be needed to match warnings in P to warnings in P′, only sources of uncertainty across the baseline program and the new program are needed to install the correctness conditions ε. Since the developer  126  is typically interested in suppressing false warnings that stem from analysis imprecision, the inferred correctness conditions typically concern sources of imprecision. By focusing on the sources of imprecision, the full syntactic matching problem can be reduced to an easier problem of matching a few identifiable locations, thereby improving effectiveness of baselining. 
     Various examples are set forth herein, which are relatively simple imperative programs with “holes,” which refer to calls to unknown external functions and input values. Unknown functions are a source of imprecision regardless of the granularity of the analysis (modular, whole program, etc.), and the analyzed language (Java/C sharp, C/C++, Assembly, etc.). For instance, Java programs frequently use the Java native interface, which allows calling C code from Java. A C analyzer will encounter operating system calls, whose code may only be available in binary form. Even a binary level analyzer may encounter statically unknown data via interaction with the disk, network or user. Unknown functions can also be used to model pieces of code that the analyzer tool  106  cannot understand or reason about precisely, such as complex loops or synchronization patterns, as well as global variables. 
     Referring now to  FIG. 2 , an exemplary diagram  200  depicting correctness condition extraction (inference) that can be performed by the condition inferrer component  112 , is illustrated. The diagram  200  summarizes a framework for extracting correctness conditions from the baseline program P. At the lowest level in the diagram  200  is the trace semantics τ P   + , which capture all finite executions of P. By abstracting away all traces in τ P   +  that lead to an error state via the abstraction function α G , the success semantics G[[P]] can be obtained. The success semantics captures only the good executions of the program P. 
     Given such success semantics, the goal of the condition inferrer component  112  is to extract correctness conditions—assumptions that must be made about the environment to ensure good traces. As the fork in the diagram  200  illustrates, two choices for the strength of the correctness conditions can be considered: 1) sufficient conditions; or 2) necessary conditions. A sufficient condition guarantees that the baseline program P always reaches a non-error state, while a necessary condition expresses that if the condition does not hold, the baseline program P definitely reaches an error state. Otherwise stated, if a sufficient condition does not hold, the program may or may not reach a bad state. If a necessary condition does not hold, then the program definitely will reach a bad state. The following examples illustrate the difference between sufficient and necessary correctness conditions. 
     
       
         
           
               
               
             
               
                   
                   
               
             
            
               
                   
                 Sufficient( ): 
               
               
                   
                 0:   ret = g( ) 
               
               
                   
                 1:   y = ret 
               
               
                   
                 2:   if * 
               
               
                   
                 3:    assert y &gt; 0 
               
               
                   
                   
               
            
           
         
       
     
     The expression * denotes nondeterministic choice. The condition ret&gt;0 (at program point  1 ) is sufficient for the correctness of the program. When ret is positive, the program will always reach a non-error state, no matter which nondeterministic choice is made. The condition, however, is not necessary because the program may still reach a non-error state even if ret is not positive. 
     An example illustrating a necessary but not sufficient condition is now set forth with respect to the following exemplary program: 
                                            Necessary( ):           0:   ret = g( )           1:   y = ret;           2:   if *           3:      assert y &gt; 0           4:   else           5:      assert false                        
The condition ret&gt;0 is necessary for the correctness of the program, but not sufficient. If ret is not positive, then the first assertion will always fail. Since there is no condition that can be up imposed on ret to ensure that the second assertion will not fail, the program has no sufficient correctness condition.
 
     The condition inferrer component  112  can obtain necessary and/or sufficient conditions for a program using suitable abstractions of success semantics. As the diagram  200  illustrates, correctness conditions can be extracted at increasing levels of abstraction. For instance, one option is to extract correctness conditions for sequences of function calls (history conditions  ). Another option is to extract correctness conditions for each program point where a function call appears, e.g. different occurrences of the same function invocation are kept separate (state conditions). A still more abstract option is to merge together calls to the same function (call conditions  ,  ). 
     It can be noted, in general, that the condition inferrer component  112  is not able to infer the weakest sufficient conditions or strongest necessary conditions because of well-known computability problems. Approximation is used to make such inference tractable in practice. The diagram  200  indicates that weakest sufficient conditions can be under-approximated and strongest necessary conditions can be over-approximated. 
     As indicated above, once the correctness conditions ε have been extracted from the base program P by the condition inferrer component  112 , the inserter component  120  can install such conditions into the new program P′. If history or state conditions were extracted, then the previously mentioned δ function may be used to map each correctness condition from its program point in P to its corresponding program point in P′. If, however, call conditions are extracted, the task is much easier than constructing the δ function. This is true because it is much more reliable to map function calls (and entry points) across versions of a program than it is to map arbitrary program points. 
     Once the correctness conditions ε have been installed in P′ to create instrumented program P′ ε , results about the relationship between P, P′ and P′ ε , can be proved. If the extracted correctness conditions ε are sufficient, and the static analyzer component  108  detects an error in P′ ε , then this is a regression (e.g., a new bad run) of P′ with respect to P. Conversely, if the extracted correctness conditions are necessary and the static analyzer component  108  reports no errors for P′ ε , then P′ is correct with respect to the environmental assumptions in P. Otherwise stated, sufficient conditions are useful to find new bugs while necessary conditions are useful to prove (relative) correctness. 
     In an example, reference is made to the programs Sufficient (set forth above) and Sufficient′ (set forth below): 
                                            Sufficient&#39;( ):           0:   ret = g( )               //assume ret &gt;0               // read from baseline           1:   y = ret           2:   z = y−2           3:   if *           4:    assert z &gt; −12                        
With respect to such exemplary programs, a syntactic baselining technique would likely fail to match the alarm from Sufficient( ) with the alarm in Sufficient′( ) because the line numbers have shifted and the expression being asserted has changed. Using the semantic baselining technique described herein, the (sufficient) conditioned ret&gt;0 is read from the analysis baseline and is installed as an assumption about the return value of g( ). The static analyzer component  108  is able to prove that the assertion holds under this assumption.
 
     In other words, the static analyzer component  108  can show that if Sufficient was correct under the condition ret&gt;0, then the changes made in Sufficient′( ) did not introduce any new errors. It can be noted that any under-approximation of return is greater than zero (e.g., false) allows for the same guarantee. Conversely, using an over-approximation (e.g., true) would report the alarm as a new alarm. It can be noted that the condition read from the baseline is stronger than (e.g., is an under-approximation of) the condition that can be extracted from Sufficient′; ret&gt;−10. 
     In some instances, the assertion in the new program P′ requires stronger environmental hypotheses than the assertion in the baseline program P. The following program is set forth as an example: 
     
       
         
           
               
               
             
               
                   
                   
               
             
            
               
                   
                 Sufficient&#39;WithRegression( ): 
               
               
                   
                 0:   ret = g( ) 
               
               
                   
                     // assume ret &gt; 0 
               
               
                   
                     // read from baseline 
               
               
                   
                 1:   y = ret −2 
               
               
                   
                 2:   if * 
               
               
                   
                 3:      assert y &gt; 0 
               
               
                   
                   
               
            
           
         
       
     
     In such program, a syntactic baselining technique that matches based on the expression being asserted, may wrongly suppress the warning since the expression is y&gt;0 in both Sufficient and Sufficient′WithRegression. In contrast, the semantic baselining technique described herein causes a new alarm to be emitted, because the condition ret&gt;0 from Sufficient is no longer strong enough to prove the correctness of the assertion in Sufficient′WithRegression. 
     Another example with respect to proving fixes is now described with reference to the programs Necessary (the baseline program shown above) and Necessary′ (the new program) set forth below: 
     
       
         
           
               
               
             
               
                   
                   
               
             
            
               
                   
                 Necessary&#39;( ) 
               
               
                   
                 0:   ret = g( ) 
               
               
                   
                     // assume ret &gt; 0 
               
               
                   
                     // read from baseline 
               
               
                   
                 1:   y = ret 
               
               
                   
                 2:   if (y % 2 == 1) 
               
               
                   
                 3:       assert y &gt; 0 
               
               
                   
                 4:   else 
               
               
                   
                 5:       assert y &gt; 1 
               
               
                   
                   
               
            
           
         
       
     
     Under the necessary correctness assumption ret&gt;0 read from the semantic baseline, the static analyzer component  108  can prove that Necessary′ is correct. Accordingly, Necessary′ fixed Necessary, since no regression was introduced and the condition that was all only necessary for the correctness of Necessary is sufficient for the correctness of Necessary′. 
     Formalization of semantic correctness condition extraction and insertion using Abstract Interpretation is now be described. First, concrete semantics of the baseline program are described as a sequence of states. With respect to syntax, a relatively simple block language can be considered. Basic statements are assignments, assertions, assumptions and function calls: 
     
       
         
           
               
               
               
             
               
                   
                   
               
             
            
               
                   
                 Stm ::= 
                 x = e | assert e | assume e| 
               
               
                   
                   
                 ret = f(arg 0 , ... arg n ) 
               
               
                   
                   
               
            
           
         
       
     
     An expression e ε Exp is a side effect-free expression. The expression *ε Exp models non-deterministic choice. To simplify the formal treatment, it can be assumed, without loss of generality, that nondeterministic choices can only appear in assignments (e.g. assert * and assume* are disallowed). Blocks are sequences of statements delimited by ;. In an example, nothing about the structure is assumed, except that the contracts of all function calls are explicitly expanded. That is, if a callee f has a contract that includes a preconditioned pre and post-condition post, then the precondition is asserted and postcondition is assumed as follows:
         assert pre; ret=f(arg 0 , . . . arg n ); assume post.       

     A program is a directed graph whose nodes are blocks. It can be assumed for the purposes of explanation, that a program has a single exit block. The special program points entry and exit denote function entry and exit points. The terms BeforeCall (resp. AfterCall) set can be used to denote the set of program points before (resp. after) some method call. 
     Concrete states are now described. A program state maps variables x, y . . . ε V to values in  . Some reserved variable names may be assumed, such as arg 0 , arg 1 , . . . which are the actual arguments for function calls, and ret is the value returned by a function call. Program states s, s 0 , . . . belong to a given set of states Σ. The function πεΣ→PC maps a state to the corresponding program point, and the function π s ερ→Stm maps states to the corresponding statements. Further, the function  ε(Σ×Exp)→{true, false} returns the Boolean value of an expression in a given state. Further, s e can be written for  (s, e)=true. 
     Small-step operational semantics is now described. The nondeterministic transition relation τ P ε (Σ×Σ) associates a state with its possible successors. When clear from the context, τ can be written instead of τ P , and τ(s, s′) can be written instead of  s, s′ ετ. 
     Function calls which can be used to model unknowns from the environment and imprecision in the static analysis undertaken by the static analyzer component  108 . It can be assumed that function calls are 1) black boxes (their bodies cannot be inspected, so they can return any value); and 2) side effect-free. 
     The model of functions used in semantic baselining can be formalized as follows: if sεΣ is the state corresponding to a function call (that is, π s (s)=ret=f(arg 0 , . . . arg n-1 ) than the transition relation is such that
 
∀ s ′εΣ·τ( s,s ′) ∃νε ·s′=s[ret ν].
 
     In general, it can be noted that side effect-free functions need not be deterministic. The model of functions described above accounts for the possibility that a function can be invoked with the same parameters and return a different value. 
     The set of “initial” states can be defined as  εΣ, and the set of “final” (or blocking) states have no successors:  ={sεΣ|∀s′.  τ(s, s′)}. The “bad” states    ⊂    correspond to assertion failures, e.g.,
 
 ={ sεΣ|π   s ( s )=assert  e     s     e}.  
 
The “good” states    ⊂    are non-failing final states:  = / .
 
     Maximal trace semantics will now be defined. The concrete semantics of the program P is a set of execution traces. Formally, different choices are possible for the trace semantics, e.g. to take into account one 1) partial or maximal executions; 2) only finite executions; or 3) both. For purposes of explanation, finite maximal traces are discussed. 
     Traces are sequences of states in Σ. The empty trace ε has a length 0. A trace {right arrow over (s)}=s 0 , s 1 , . . . , s n-1  has a length n. s i  can be written to refer to the ith state of the traces {right arrow over (s)}. The set of traces of length n≧0 is Σ n . The set of “finite” traces is Σ*={ε}∪∪ n&gt;0 Σ n . The good traces are  *={{right arrow over (s)}εΣ*|s n-1 ε }. Similarly, the bad traces are  *={{right arrow over (s)}εΣ*|s n-1 ε }. Trace “concatenation” can be defined as sequence juxtaposition, and trace “composition” can be defined as {right arrow over (s)} 0 s∘s{right arrow over (s)} 1 ={right arrow over (s)} 0 s{right arrow over (s)} 1 . Trace concatenation and composition can be extended to sets of traces. 
     The execution prefixes define the “partial execution” trace semantics. Partial runs of P of length n are partial executions such that
 
{right arrow over (τ)} P   n   ={{right arrow over (s)}εΣ   n   |∀iε[ 0 ,n− 1)·τ P ( s   i   ,s   i+1 )}.
 
The complete or “maximal” runs of P are those partial runs ending with a blocking state:
 
     
       
         
           
             
               
                 τ 
                 → 
               
               P 
               + 
             
             = 
             
               
                 ⋃ 
                 
                   n 
                   &gt; 
                   0 
                 
               
               ⁢ 
               
                 
                   { 
                   
                     
                       
                         s 
                         → 
                       
                       ∈ 
                       
                         
                           τ 
                           → 
                         
                         P 
                         n 
                       
                     
                     | 
                     
                       
                         
                           s 
                           0 
                         
                         ∈ 
                       
                       ⩓ 
                       
                         
                           s 
                           
                             n 
                             - 
                             1 
                           
                         
                         ∈ 
                       
                     
                   
                   } 
                 
                 . 
               
             
           
         
       
     
     It can be noted that while each trace in {right arrow over (τ)} P   +  is finite, the set {right arrow over (τ)} P   +  is in general infinite. This means that while infinite executions are not captured, unbounded non-determinism from data sources, like input variables or values returned from a function, can be captured. For instance, lfp φ can denote, if it exists, the least fixed point of φ larger than ⊥ according to some order  . A constructive characterization of the maximal trace semantics can then be given as follows:
 
{right arrow over (τ)} P   + ( )=( lfp     λT·     1 ∪{right arrow over (τ)} P   2   ∘T )∩( lfp     λT·     1   ∪T∘{right arrow over (τ)}   P   2 ),
 
e.g., the maximal trace semantics can be intersected with the partial executions semantics. In the maximal trace semantics, initially the traces containing only a blocking state can be selected, and the transition relation τ can be applied backwards. At the fixpoint this provides all traces ending in a final state—there are no partial computations, but the computation may start from any point. In partial trace semantics, the transition relation can be systematically applied to obtain the set of all traces descending from an initial set of states  . This set includes partial traces, but all traces originate from the initial states. The intersection of the maximal trace semantics and the partial trace semantics includes all of (and only) the traces starting from   and ending in  .
 
     Abstract Interpretation is now briefly discussed. Abstract Interpretation is a theory of semantic approximations. Abstract interpretation can be used to approximate a concrete semantics comprising the maximal trace semantics defined above over a concrete domain defined by the complete Boolean lattice  (τ*),  ⊂ , Ø, Σ*, ∪, ∩,  . The concrete semantics can be either over-approximated or under-approximated. 
     For example,  C, ≦  and  A,  can be two partial orders. If there exists an abstraction function αεC→A and a concretization function γεA→C such that ∀cεC, aεA·α(c) a c≦γ(a), then  α, γ  can be referred to as a Galois connection, which can be noted by 
               〈     C   ,   ≤     〉     ⁢       ⇆   γ     α     ⁢       〈     A   ,   ⊑     〉     .           
In a Galois connection, 1) the abstraction function uniquely determines the concretization function and vice versa; and 2) the abstraction preserves the upper bounds and the concretization preserves the lower bounds. The composition of Galois connections is a Galois connection: If
 
                 〈     C   ,   ≤     〉     ⁢       ⇆     γ   1         α   1       ⁢       〈       A   1     ,   ⊑     〉     ⁢           ⁢   and   ⁢           ⁢     〈       A   1     ,   ≤     〉       ⁢       ⇆     γ   2         α   2       ⁢     〈       A   2     ,   ⊑     〉       ,         
then
 
               〈     C   ,   ≤     〉     ⁢       ⇆       γ   1     ·     γ   2             α   1     ·     α   2         ⁢       〈       A   2     ,   ⊑     〉     .           
The composition enables the step-wise construction of abstract semantics, and it is the theoretical justification to the construction described herein. In some instances, a relaxed form of the abstract interpretation framework in which the abstraction function α is monotonic, may be used. Under the hypothesis presented herein, the fixpoint transfer theorem implies that if FεC→C is a monotonic function and  F εA→A commutes with F (e.g., α∘F= F ∘α), then α(lfp ⊥   ≦ F)=lfp   F .
 
     “Good” execution semantics of the base program P are now described. The “good” execution semantics of P considers only the traces in the maximal execution traces of P that terminate in a non-error state. To formalize this, a Galois connection 
               〈       ⁢     (     Σ   *     )       ,   ⊆     〉     ⁢       ⇆     γ   ??         α   ??       ⁢     〈       ⁢     (     Σ   *     )       ,   ⊆     〉           
can be defined with the abstraction  =λT·T∩  and the concretization γ G =λT·T∪ *. Good execution trace semantics can be defined as G[[P]]= ({right arrow over (τ)} P   + ). It follows from the fact that α is a join morphism, the definition of blocking states, and the fixpoint transfer theorem that
 
 G[[P]] =( lfp     λT·     1 ∪({right arrow over (τ)} P   2   ∘T )∩( lfp     λT·     1   ∪T∘{right arrow over (τ)}   P   2 ).
 
     With reference to  FIG. 3 , the condition inferrer component  112  is presented. The condition inferrer component includes a trace inferrer component  302  that can infer trace-based environment conditions, including weakest sufficient environment conditions and strongest necessary environment conditions. With specificity, the trace inferrer component  302  can extract sufficient and necessary conditions from G[[P]]. As noted above, a sufficient condition (roughly) ensures that all executions exhibiting the condition are good conditions, while a necessary condition holds for all good conditions. The “optimal” sufficient and necessary conditions can be formalized by suitable abstractions. 
     First, the Galois connection 
               〈       ⁢     (     Σ   *     )       ,   ⊆     〉     ⁢       ⇆     γ   n         α   n       ⁢     〈       ⁢     (     Σ   *     )       ,   ⊆     〉           
can be defined, where the abstraction
 
α n   =λT·{s   0   {right arrow over (e)}|{right arrow over (s)}εT     {right arrow over (e)} =α′( {right arrow over (s)} )}
 
uses the commodity function α′ defined as:
 
                 a   ′     ⁡     (     s   →     )       =     {         ε           if   ⁢           ⁢   s     =   ε                 s   1     ⁢       α   ′     ⁡     (       s   2     ,   …   ⁢           ,     s     n   -   1         )                 if   ⁢           ⁢       π   s     ⁡     (     s   0     )         =     ret   =     f   ⁡     (   …   )                       α   ′     ⁡     (       s   1     ,     …   ⁢           ⁢     s     n   -   1           )           otherwise                 
Intuitively, α n  captures sequences of environment choices from a set of traces T. The abstraction function records the initial state and the state following each method call.
 
     The trace inferrer component  302  can infer approximations for weakest sufficient conditions, which capture the largest set of environment choices that guarantee that the program execution always reaches a good state. Weakest sufficient environmental conditions can be defined formally via the parameterized Galois connection 
               〈       ⁢     (     Σ   *     )       ,   ⊆     〉     ⁢       ⇆       γ   s     ⁡     [   S   ]             α   s     ⁡     [   S   ]         ⁢       〈       ⁢     (     Σ   *     )       ,   ⊆     〉     .           
The parameterized abstraction is as follows:
 
α s   [S]=λT·{α   n ({ {right arrow over (s)}} )| {right arrow over (s)}εα   G ( T ) ∀ {right arrow over (s)}′εS ∩ * α n ({ {right arrow over (s)}} )≠α n ({ {right arrow over (s)}′} )},
 
and the (parameterized) concretization is γ s [S]=λT·{{right arrow over (s)}|{right arrow over (s)}ε (T) ∃{right arrow over (s)}′εS·α n  ({{right arrow over (s)}})=α n ({{right arrow over (s)}})}. In other words, given a basis set of traces S, the abstraction first selects the traces in T which do terminate in a good state ({right arrow over (s)}εα n ( (T))), such that their environmental choices (α n ({{right arrow over (s)}})) are different from the choices of all the traces in S that may lead to a bad state ({right arrow over (s)}′εS∩ *). Then, out of the selected traces, the abstraction only retains the environmental choices. The largest set of environment choices guaranteeing that the program P is always correct is  α s [{right arrow over (τ)} P   + ](G[[P]]).
 
     In an example, with respect to the baseline program Sufficient( ), the weakest sufficient condition   is as follows:
 
 ={ y y 0   , ret     r   0   y,ret  r 1   |y 0   ,r   0 ε ,0 &lt;r   1 }
 
e.g., when g returns a positive number, the program is correct; that is, it will never reach any bad state. The program is correct, even if g insurers a stronger property, e.g., the return value is 45 or a positive even number.
 
     The trace inferrer component  302  can also infer approximations of strongest necessary environment conditions, which are now described. It is desirable to obtain the set of environment choices (e.g. input values and values returned from function calls) that characterize good executions. An intuition is that if a sequence of environment choices is obtained, but not in this set, then an execution compatible with those choices will definitely lead to a bad state. In particular, it is desirable to obtain the smallest such set  , which can be obtained by abstracting G[[P]]. 
     Therefore,  α n (G[[P]]) is the strongest environment property satisfied by good executions of P. If an environment does not make one of the choices admitted by  , than the program P will certainly fail. If an environment does make a choice allowed by  , then it can be known, by construction, that there is at least one concrete execution of P that terminates in a good state. There may, however, also be concrete executions that terminate in a bad state, as can be shown with reference to the exemplary program Necessary set forth above. Specifically, with reference to the exemplary base program Necessary:
 
 ={ y y 0   ,ret     r   0   |y 0   ,r   0 δ ,r 0 &gt;0}.
 
That is, in all the good executions, the value returned by f is positive. If f is negative, then the program will certainly fail. Otherwise, the program may or may not fail, depending on the non-deterministic choice.
 
     It follows from the results above that    ⊂   . In general, it is sound to over-approximate  , but not to under-approximated it. Dually, it is sound to under-approximate  , but not to over-approximate it. 
        characterizes the largest set of concrete executions that always terminate in a good state. It can be noted that any under-approximation of   yields a smaller set of concrete executions that always terminate in a good state.   characterizes the largest set of concrete executions that do not definitely terminate in a bad state. It can be noted that any over-approximation of   yields a larger set of concrete executions, some of which may definitely terminate in a bad state. Accordingly, the 
     The condition inferrer component  112  can also include a state inferrer component  304 , which can infer/approximate state-based environment conditions for the baseline program. State-based environment conditions are now described in greater detail. The above-noted trace based environment conditions capture the sequence of environment choices that are necessary or sufficient for program correctness. In some cases, however, the set of environment that are possible at a given program point may be of interest, regardless of the sequence of environment choices made previously. In such cases, the state inferrer component  304  can abstract the trace properties, described above, to state properties by collecting all the states observed at each program point and discarding the environment choices made along the way. 
     For instance, given a set of traces T, the reachable states abstraction α Σ collects the set of states that reach each program point. Formally, the Galois connection is defined as 
                 〈       ⁢     (     Σ   *     )       ,   ⊆     〉     ⁢       ⇆     γ   Σ         α   Σ       ⁢     〈       PC   →     ⁢     (     Σ   *     )         ,   ⊆     〉       ,         
where the abstraction function α Σ  is as follows:
 
α Σ   =λT·Tpc·{s   i   |∃{right arrow over (s)}εT·{right arrow over (s)}=s   0   , . . . ,s   i   , . . . ,s   n-1   π( s   i )= pc},  
 
and the concretization γ Σ  is the expected one.
 
     The weakest state-based sufficient conditions on the environment are the set of states    Σ   α Σ ( ). The strongest state-based necessary conditions on the environment are the set of states    Σ   α Σ ( ). If entry denotes the entry point of P, then 1)    Σ  (entry) is the weakest sufficient precondition and 2)    Σ  is the strongest necessary precondition. 
     For purposes of explanation, reference is now made to the following exemplary program TwoCallSites: 
                                            TwoCallSites(y,z):           0:   if *           1:      ret = f(y)           2:      x = ret + 1               else           3:      ret = f(z)           4:      x = ret −1           5:   assert x &gt; 100                        
With respect to such exemplary program, the sufficient and necessary state-based conditions coincide:    Σ (1)=   Σ (1)={σ|σ(ret)&gt;99} and    Σ (3)=   Σ (3)={σ|σ(ret)&gt;101}.
 
     Since, as noted above,    ⊂   , it follows that    {dot over (⊂)}   , where  {dot over (⊂)}  is the pointwise functional extension of  ⊂ . As with the trace-based environment conditions, it is sound to cause the state inferrer component  304  over-approximate    Σ  and to under-approximate    Σ . 
     The condition inferrer component  112  can further comprise a call inferrer component  306  that can infer/approximate method call conditions for the baseline program. The above-described state-based environment conditions collect the possible environments at each program point. In some cases, one may only be interested in the environment conditions following a method call. Such abstraction is referred to as method call conditions. Callees can be the set of invoked functions. Then the weakest sufficient condition on function calls (e.g., the largest set of states that guarantees the program always terminates in a good state) is the intersection of all sufficient states at return points. The following monotonic abstraction function can be used, which is set forth as follows: 
     
       
         
           
             
               α 
               ?? 
             
             = 
             
               λ 
               ⁢ 
               
                   
               
               ⁢ 
               
                 r 
                 . 
                 λ 
               
               ⁢ 
               
                   
               
               ⁢ 
               
                 f 
                 . 
                 
                   
                     ⋂ 
                     
                       pc 
                       ∈ 
                       
                         Callees 
                         ⁡ 
                         
                           ( 
                           f 
                           ) 
                         
                       
                     
                   
                   ⁢ 
                   
                     
                       r 
                       ⁡ 
                       
                         ( 
                         pc 
                         ) 
                       
                     
                     . 
                   
                 
               
             
           
         
       
     
     Therefore,  ( ) are the weakest sufficient conditions on callees. 
     With reference to the above presented program TwoCallSites, the weakest sufficient condition on f is  (f)={σ|σ(ret)&gt;101}. No matter which branch is taken, if f returns a value larger than 101, the assertion holds. 
     The necessary call conditions are given by the Galois connection 
               〈       PC   →     ⁢     (   Σ   )         ,   ⊆     〉     ⁢       ⇆     γ   Σ         α   Σ       ⁢       〈       Callees   →     ⁢     (   Σ   )         ,   ⊆     〉     .           
The abstraction function α Σ  merges the states after all implications of a given callee f. The abstraction function can be formally set forth as follows:
 
               =     λ   ⁢           ⁢     r   .   λ     ⁢           ⁢     f   .       ⋂     pc   ∈     Callees   ⁡     (   f   )           ⁢     r   ⁡     (   pc   )               ,         
and the concretization function is
 
             =     λ   ⁢           ⁢     n   .   λ     ⁢           ⁢     pc   .     {             n   ⁡     (   f   )               pc   ∈   AfterCall     ⩓     pc   ∈     Callees   ⁡     (   f   )                   Σ       otherwise         .                 
Therefore,  (   Σ ) are the strongest conditions on the callees which are necessary for the program to be correct.
 
     Referring again to the exemplary program TwoCallSites set forth above, the strongest necessary condition on f is  (f)={σ|σ(ret)&gt;99}, e.g., if the return value from f is smaller or equal than 99, then both assertions will fail. 
     In summary, the condition inferrer component  112  can analyze the base program and infer any of the types of environmental conditions set forth above, and can utilize any suitable approximation techniques when performing such inference. 
     With reference to  FIG. 4 , a functional block diagram of the inserter component  120  is shown. The inserter component  120  is configured to receive the correctness conditions inferred for the baseline program, the baseline program, and the new program, and insert the correctness conditions at appropriate locations in the new program. To that end, the inserter component  120  includes a comparer component  402  that can perform a syntactic comparison between the baseline program P and the new program P′. Thus, the correctness conditions inferred for the baseline program provide a semantic baseline to allow for the reporting of only the new errors in the modified program P′. No constraint need be imposed on how much P and P′ differ. For purposes of explanation, it can be assumed that a function δεPC(P)→PC(P′)∪{⊥} is known that captures the syntactic changes between P and P′. The δ function maps a program point from the base program to its corresponding point in the new program or to ⊥ if the program point has been removed. For purposes of explanation, it can be assumed that variables from P are not renamed in P′. 
     The formalism described herein is agnostic to the method used to compute δ; it can be considered as a parameter of the analysis. There are several conventional techniques for computing δ. 
     The image of a trace with respect to δ can be defined as follows: 
     
       
         
           
             
               α 
               δ 
             
             = 
             
               λ 
               ⁢ 
               
                 
                   s 
                   → 
                 
                 . 
                 
                   { 
                   
                     
                       
                         
                           ε 
                         
                         
                           
                             
                               if 
                               ⁢ 
                               
                                   
                               
                               ⁢ 
                               
                                 s 
                                 → 
                               
                             
                             = 
                             ε 
                           
                         
                       
                       
                         
                           
                             s 
                             ⁢ 
                             
                                 
                             
                             ⁢ 
                             
                               α 
                               ⁡ 
                               
                                 ( 
                                 
                                   
                                     s 
                                     → 
                                   
                                   ′ 
                                 
                                 ) 
                               
                             
                           
                         
                         
                           
                             
                               if 
                               ⁢ 
                               
                                   
                               
                               ⁢ 
                               
                                 s 
                                 → 
                               
                             
                             = 
                             
                               
                                 s 
                                 ⁢ 
                                 
                                   
                                     s 
                                     → 
                                   
                                   ′ 
                                 
                               
                               ⩓ 
                               
                                 
                                   δ 
                                   ⁡ 
                                   
                                     ( 
                                     
                                       π 
                                       ⁡ 
                                       
                                         ( 
                                         s 
                                         ) 
                                       
                                     
                                     ) 
                                   
                                 
                                 ≠ 
                                 ⊥ 
                               
                             
                           
                         
                       
                       
                         
                           
                             α 
                             ⁡ 
                             
                               ( 
                               
                                 
                                   s 
                                   → 
                                 
                                 ′ 
                               
                               ) 
                             
                           
                         
                         
                           otherwise 
                         
                       
                     
                     . 
                   
                 
               
             
           
         
       
     
     That is, the comparer component  402  can employ α δ (s) to abstract away all the states in {right arrow over (s)} that do not refer to a program point in P′. 
     The inserter component  120  further includes a filter component  404  that filters traces in the concrete semantics of P′ which are compatible with  . The filtered maximal trace semantics of P′ can be formalized as follows:
 
{right arrow over (τ)} P   + ,└ ={{right arrow over (s)}ε{right arrow over (τ)} P   + ,|∃{right arrow over (t)}ε ·α δ ( {right arrow over (t)} )=α′( {right arrow over (s)} )},
 
e.g., the filter component  404  causes only those traces in {right arrow over (τ)} P   +  to be retained, such that the sequence of choices made by the environment is compatible with the trace condition  . This filtering then yields a subset of the concrete traces, e.g., {right arrow over (τ)} P   + ,└   ⊂ {right arrow over (τ)} P   + .
 
     The inserter component  120  can inject sufficient conditions   from P into the new program P′, as well as necessary conditions into the new program P′. With respect to semantic baselining with sufficient conditions, it can be known by construction that {right arrow over (τ)} P   + , └ ∩ *=Ø, since the sufficient conditions   were sufficient to eliminate all the bad runs for P. Therefore, if the sufficient conditions   are injected into P′, and at least one bad execution is found (formally {right arrow over (τ)} P   + , └ ∩ *≠Ø), then the new program has added a bad behavior with respect to P. When P′ introduces a bad behavior that was not present in P, it can be said that P′ has caused a regression. Another way of viewing the regression is that P′ needs stronger environmental conditions than P. In general, the following result holds.
     Theorem 1: If {right arrow over (τ)} P   + └ ∩ *≠Ø, then P′ has a definite regression with respect to P.   

     With respect to the inserter component  120  injecting necessary conditions into P′, in an example, it can be assumed that no bad execution is found (e.g., formally {right arrow over (τ)} P   + , └ ∩ *≠Ø). It can be concluded that under the same environment conditions that hold for all the good runs (and maybe some of the bad runs) of P, P′ has no bad runs. As a consequence, 1) P′ does not require stronger environmental hypotheses than P for correctness; and 2) if   were only necessary but not sufficient for the absence of bad runs in P, then P′ fixed the errors in P. Thus, the following theorem holds:
     Theorem 2: When {right arrow over (τ)} P   + , └ ∩ *≠Ø, P′ has no more bad behaviors than P, and P′ fixed a bug in P.   

     It can further be noted that, in general, the strongest necessary conditions for P may not be necessary conditions for P′ any longer. For instance, if P′ removes an assertion from P, the necessary conditions for P may preclude some good runs in P′. 
     The inserter component  120  also includes an approximator component  406 , which can generate approximations for inserting necessary and sufficient conditions into the new program P′. The semantic filtering and the two theorems above provide the best theoretical solution to the problem of cross-version correctness condition injection and, correspondingly, to the problem of semantic baselining. Such an approach, however, may not be practical because the exact computation of the success semantics and history conditions is often infeasible for real programs. Thus, to solve the problem of semantic baselining in practice, some approximation may be needed. 
     It can be noted that the use of the weakest sufficient conditions Equation 167 is sound and complete for detecting regressions. It guarantees that all regressions of P′, with respect to P, are captured. If an under approximation  ⊂  is considered, then all the regressions captured are definite regressions with respect to P. Some regressions with respect to P may be missed, but not with respect to P . 
     Reference is again made to the programs Sufficient, Sufficient′, and Sufficient′WithRegression set forth above. For example, the condition inferrer component  112  can extract the sufficient condition ret&gt;10. When such condition is injected into Sufficient′WithRegression by the inserter component  120 , the analyzer tool  106  can prove the assertion and no warning is raised. Thus, the relative completeness of Sufficient′WithRegression with respect to Sufficient proved. Sufficient ret&gt;10  is proved. 
     It can also be noted that the use of the strongest necessary conditions is sound and complete for detecting bug fixes. If an over-approximation  ⊃  is considered, than all of the bug fixes found are definite bug fixes with respect to P (soundness). Some bug fixes, however, may be missed (incompleteness). For instance, with respect to the programs Necessary and Necessary′, the condition ret&gt;−10 may be extracted from Necessary by the condition inferrer component  112 ; in such case, it cannot be proved that Necessary′ fixed Necessary. 
     Based at least in part upon the monotonicity and the soundness of the extractions noted above, the results can be lifted to the state condition semantics and the call condition semantics. Sound, yet computable approximations, e.g.,  S   Σ ,    Σ  and   can be used to obtain sound and relatively complete condition injection. 
     This only solves half of the baselining problem, however, since it has been assumed that the syntactic change function δ is given. In general, automatically constructing δ is relatively impossible because several different δ functions can characterize the transformation from P to P′. For example, reference is made to the program TwoCallSites noted above, as well as to the program TwoCallSites′ shown below to illustrate difficulties in constructing δ functions. 
                                            TwoCallSites&#39;(w)           0:   ret = f(w)           1:   x = ret           2:   assert x &gt; 100                        
The base program, TwoCallSites, includes two invocations off, one at program point 1, the other at program point 3. The new program, TwoCallSites′, contains only one invocation off. Through syntactic analysis, it cannot be ascertained with any certainty which, if either, of the two calls in the base program corresponds in the call to f in the new program. In other words, it is not possible to determine which δ function is correct out of the following two examples:  1 0,3 ⊥  or  1 ⊥, 3 0 .
 
     For these exemplary programs, it is ambiguous as to which δ function is the correct choice. This arbitrary choice, however, will have semantic ramifications because each state corresponding to a call to f in the base program has a different correctness condition associated with it. That is, each equally valid choice of δ will result in a different correctness condition being ported to P′, which is undesirable. 
     Many of the problems associated with constructing δ can be avoided by using the call condition semantics instead of the state condition semantics. This reduces the problem of computing δ for all program points to the much simpler problem of matching method names across program versions. As long as methods are not renamed, such approach is quite reliable. Thus, the comparer component  402  can compare method names across the base program and the new program. For instance, with reference to TwoCallSites and TwoCallSites′, the method f is called in both versions of the program. Since the call conditions are merged for all indications of a function, changing the number of calls to a particular function presents no difficulty. This can be contrasted with the ambiguity introduced by changing the number of calls to f during an attempt to construct a δ to port state conditions across versions. 
     For each function f that is called in both P and P′, it can be assumed that the correctness condition ε(f) after all calls to f in P′, where ε(f) can be either  (ƒ) or  (ƒ), depending on which kind of inference is desired. Thus, the correctness conditions can be treated as if they were post conditions for f that f&#39;s body does not need to prove. The injected program P ε  can be readily analyzed, resulting in an analysis that can be reported to the developer  126 . 
     Other sources of approximation can also be considered. For instance, the sources of approximations noted above are the under/over approximation of the extracted correctness conditions and the abstraction level at which such conditions are observed. Practical static techniques typically make many other approximations. All static verifiers and analyzers over-approximate the concrete semantics τ + . When over-approximation adds execution traces that end in an error state, the analyzers will report false alarms. Semantic baselining is just as suitable for suppressing false alarms due to over-approximation as it is for suppressing false alarms due to unknown code. When an analyzer loses precision in a way that causes a false alarm, a condition to suppress that false alarm is simply assumed in the next version of the program. 
     For example, semantic baselining can suppress false alarms due to imprecise handling of loops. If a loop is considered to be a black box (e.g., extracted into an unknown function), correctness conditions can be inferred for the black box corresponding to the loop in the base program and ported to the new program. False alarms due to imprecise handling of concurrency, bitwise operations, unbound data structures, can be handled in a similar manner. 
     Finally, a subtle problem can arise due to the often forgotten fact that practical analysis tools are not monotonic. That is, it is possible for an analyzer to compute a less precise result given more precise inputs (e.g., more assumptions). This can happen for several reasons: 1) the use of widenings, timeouts, non-monotonic state space finitization, etc. In the context of semantic baselining, this can result in an odd case were analyzing the new program P′ yields less warnings when compared to analyzing the refined program P′ ε . This can be solved by considering the intersection of the warnings generated by the analyses of P and P ε . It can be noted that computing this intersection does not pose any problems with respect to syntactic matching because only syntactic differences between P′ and P′ ε  are the addition of the correctness conditions ε. 
       FIGS. 5-6  illustrate exemplary methodologies relating to inferring correctness conditions for a baseline program and inserting such conditions into a new program is illustrated. While the methodologies are shown and described as being a series of acts that are performed in a sequence, it is to be understood and appreciated that the methodologies are not limited by the order of the sequence. For example, some acts can occur in a different order than what is described herein. In addition, an act can occur concurrently with another act. Further, in some instances, not all acts may be required to implement a methodology described herein. 
     Moreover, the acts described herein may be computer-executable instructions that can be implemented by one or more processors and/or stored on a computer-readable medium or media. The computer-executable instructions can include a routine, a sub-routine, programs, a thread of execution, and/or the like. Still further, results of acts of the methodologies can be stored in a computer-readable medium, displayed on a display device, and/or the like. 
     Now were referring to  FIG. 5 , an exemplary methodology  500  that facilitates inserting an assumption of a condition into computer-executable code (a program) is illustrated. The methodology  500  starts at  502 , and at  504  a first version of a computer program is received. For instance, this program can be written in any suitable programming language, including C, C#, C++, Java or the like. At  506 , a first static analysis over the first version of the computer program is executed. Executing the first static analysis includes identifying a warning in the first version of the computer program that corresponds to a first location in the first version of the computer program. Executing the first static analysis over the first version of the computer program also includes inferring a condition for the warning, wherein when the condition is assumed at the first location in the first version of the computer program, the warning is suppressed when the first static analysis is executed over the first version of the computer program. In other words, a correctness condition is inferred (e.g., a method call condition). At  508 , a second version of the computer program is received. The second version of the computer program is non-identical to the first version of the computer program and includes revisions to the first version of the computer program. 
     At  510 , an assumption of the condition is inserted at a second location in the second version of the computer program, such that the warning is suppressed when a second static analysis is executed over the second version of the computer program. Thus, the warning is an old warning and is not presented to the developer as being a new warning. In some examples, however, a second static analysis, when executed over the second version of the computing program results in identification of a second warning, wherein the second warning is caused by a revision to the first version of the computer program when developing the second version of the computer program. In this case, the second warning can be displayed to a developer as a new warning. The assumption of the condition can be inserted into, for instance, a function entry point or a method call. The methodology  500  completes at  512 . 
     Now referring to  FIG. 6 , an exemplary methodology  600  for outputting warnings pertaining to code when a static analysis is undertaken over such code is illustrated. The methodology  600  starts at  602 , and at  604 , a first version of a computer program is received. At  606 , a first static analysis is executed over the first version of the computer program, and at  608  a warning for a particular line in the first version of the computer program is output based upon the executing of the first static analysis over the first version of the computer program. Specifically, executing the first static analysis over the first version of the computer program can include identifying a warning at a first line of a plurality of lines of computer-executable code in the first version of the computing program, and inferring a condition for the warning that, when assumed, causes the warning to be suppressed. 
     At  610 , a condition is inferred that, when satisfied, causes the warning to be suppressed in the first version of the computer program. At  612 , an indication is received that a developer desires that the warning be suppressed in a subsequent version of the computer program. That is, the developer has indicated that the error is spurious or not worth fixing. 
     At  614 , a second version of the computer program is received, and at  616 , an assumption of the condition is inserted into the second version of the computer program. As noted above, this assumption can be inserted at a function entry point or a method call in the second version of the computer program. 
     At  618 , a second static analysis over the second version of the computer program is executed, wherein the second version of the computer program has the assumption of the condition inserted therein. At  620 , a second warning is output based upon the executing of the second static analysis over the second version of the computer program, the second warning indicating that a revision included in the second version of the computer program has caused the second warning. The methodology  600  completes at  622 . 
     Referring now to  FIG. 7 , a high-level illustration of an exemplary computing device  700  that can be used in accordance with the systems and methodologies disclosed herein is illustrated. For instance, the computing device  700  may be used in a system that infers correctness conditions for a baseline program. By way of another example, the computing device  700  can be used in a system that supports inserting the correctness conditions into a new program. The computing device  700  includes at least one processor  702  that executes instructions that are stored in a memory  704 . The instructions may be, for instance, instructions for implementing functionality described as being carried out by one or more components discussed above or instructions for implementing one or more of the methods described above. The processor  702  may access the memory  704  by way of a system bus  706 . In addition to storing executable instructions, the memory  704  may also store inferred correctness conditions, a baseline program, a new program, etc. 
     The computing device  700  additionally includes a data store  708  that is accessible by the processor  702  by way of the system bus  706 . The data store  708  may include executable instructions, inferred correctness conditions, etc. The computing device  700  also includes an input interface  710  that allows external devices to communicate with the computing device  700 . For instance, the input interface  710  may be used to receive instructions from an external computer device, from a user, etc. The computing device  700  also includes an output interface  712  that interfaces the computing device  700  with one or more external devices. For example, the computing device  700  may display text, images, etc. by way of the output interface  712 . 
     It is contemplated that the external devices that communicate with the computing device  700  via the input interface  710  and the output interface  712  can be included in an environment that provides substantially any type of user interface with which a user can interact. Examples of user interface types include graphical user interfaces, natural user interfaces, and so forth. For instance, a graphical user interface may accept input from a user employing input device(s) such as a keyboard, mouse, remote control, or the like and provide output on an output device such as a display. 
     Additionally, while illustrated as a single system, it is to be understood that the computing device  700  may be a distributed system. Thus, for instance, several devices may be in communication by way of a network connection and may collectively perform tasks described as being performed by the computing device  700 . 
     Various functions described herein can be implemented in hardware, software, or any combination thereof. If implemented in software, the functions can be stored on or transmitted over as one or more instructions or code on a computer-readable medium. Computer-readable media includes computer-readable storage media. A computer-readable storage media can be any available storage media that can be accessed by a computer. By way of example, and not limitation, such computer-readable storage media can comprise RAM, ROM, EEPROM, CD-ROM or other optical disk storage, magnetic disk storage or other magnetic storage devices, or any other medium that can be used to carry or store desired program code in the form of instructions or data structures and that can be accessed by a computer. Disk and disc, as used herein, include compact disc (CD), laser disc, optical disc, digital versatile disc (DVD), floppy disk, and blu-ray disc (BD), where disks usually reproduce data magnetically and discs usually reproduce data optically with lasers. Further, a propagated signal is not included within the scope of computer-readable storage media. Computer-readable media also includes communication media including any medium that facilitates transfer of a computer program from one place to another. A connection, for instance, can be a communication medium. For example, if the software is transmitted from a website, server, or other remote source using a coaxial cable, fiber optic cable, twisted pair, digital subscriber line (DSL), or wireless technologies such as infrared, radio, and microwave, then the coaxial cable, fiber optic cable, twisted pair, DSL, or wireless technologies such as infrared, radio and microwave are included in the definition of communication medium. Combinations of the above should also be included within the scope of computer-readable media. 
     Alternatively, or in addition, the functionally described herein can be performed, at least in part, by one or more hardware logic components. For example, and without limitation, illustrative types of hardware logic components that can be used include Field-programmable Gate Arrays (FPGAs), Program-specific Integrated Circuits (ASICs), Program-specific Standard Products (ASSPs), System-on-a-chip systems (SOCs), Complex Programmable Logic Devices (CPLDs), etc. 
     What has been described above includes examples of one or more embodiments. It is, of course, not possible to describe every conceivable modification and alteration of the above devices or methodologies for purposes of describing the aforementioned aspects, but one of ordinary skill in the art can recognize that many further modifications and permutations of various aspects are possible. Accordingly, the described aspects are intended to embrace all such alterations, modifications, and variations that fall within the spirit and scope of the appended claims. Furthermore, to the extent that the term “includes” is used in either the details description or the claims, such term is intended to be inclusive in a manner similar to the term “comprising” as “comprising” is interpreted when employed as a transitional word in a claim.