Visual C++ Code Model in Visual Studio 2010

Visual C++ Code Model in Visual Studio 2010

  • Comments 3

Hello, I’m Vytautas Leonavičius, a developer on the Visual C++ IDE team. Today, I’d like to discuss how the new principles we’re applying to code browsing in Visual Studio 2010 (see here and here) will affect VCCodeModel.  General information about VCCodeModel can be found here.

Improvements

Because of our new incremental update architecture, VCCodeModel implicitly gets certain benefits:

        Availability: Once initial population is done, VCCodeModel is pretty much always available. That is a positive result of design decisions to parse files independently. No change of a single file (even the omnipresent windows.h, if you like) will cause “rebuild of the world”. That means, if issued, calls to VCCodeModel.Synchronize will return almost instantly.

        Reliability: We no longer use the old NCB database. Instead, we now use the same redistributable SQL CE components that we provide to our end-users.  Suggestions to “delete your .ncb file” (due to DB corruption) in order to restore functionality of Browsing/Intellisense are a thing of the past. 

        Independence from a macro state: Prior to Visual Studio 2010, a file snapshot was taken in a certain undefined macro state. Depending on macro state active in a translation unit at the moment file contents were analyzed, certain constructs would or would not make it into the NCB database.  That caused a lot of confusion.  In Visual Studio 2010 macro state is ignored and all source file declarations and definitions make it into database, and therefore into VCCodeModel.

 

// Both A and B will make it into database and VCCodeModel, no matter if X is
// defined or not.

#ifdef X

class A {};

#else

class B {};

#endif


See Dealing with preprocessor conditional directives and Hint files sections of Thierry’s post.

        Improved eventing model: VCCodeModel edit Events are more precise and correct, thanks to improvements to our code difference algorithms.  The new difference algorithms have better understanding of user edits.  This is especially true in the presence of templates. This improves the reliability of the Visual C++ Wizards and clients who listen to VCCodeModel Events.

        Performance: VCCodeModel is faster in scenarios where an object reference is taken and passed through external third party code (your code!) which may perform looped inquiries. This is thanks to some targeted caching we added to VCCodeModel.  All clients should benefit from this since many of VCCodeModel services are implemented using its externally exposed APIs for correctness.

Limitations

The aforementioned benefits are primarily brought about by the design decision to treat every single file independently and through some targeted performance work.  However, a downside that this design decision exposes is a lack of absolute precision in full symbol resolution in Visual Studio 2010 Browsing. It applies the following limitations on VCCodeModel:

        No symbol resolution: Type Strings for functions (return type), typedefs, base classes, macro definitions (except those specified in hint files) and parameters are coming directly from source code. They are no longer resolved by compiler as it used to be.  This problem may surface if a caller wants to figure out precisely what type a function is returning:

 

Example 1:

using namespace X;

using namespace Z;

Type foo() { return Type(); }

 

The Type String for VCCodeFunction object referring to function foo is Type. We don’t really know if it is X::Type, global Type or Z::Type. Note that using namespace in this sample, should not necessarily be in same file, it can be in a header (far away). Without building the full translation unit, there’s no efficient way to resolve the symbol Type deterministically and to maintain the performance improvements that our customers desire and that we have achieved with this new design.

Example 2:

File.h

class Base {};

 

File2.h

namespace A {
       class Base {};

}

 

namespace B {

       class Base {};

}

 

File.cpp

#include “a_header_including_many_headers_and_introducing_using_directives.h”

 

class Derived : Base // Which Base is that? Base, A::Base or B::Base?

{};


Lack of precise fully-qualified symbol resolution affects TypeString properties of VCCodeParameter, VCCodeTypedef and FullName property of VCCodeBase.It also indirectly affects several APIs of the CodeTypeRef object that is used as a link between object in source code and type.

VCCodeModel provides heuristic APIs that attempt to resolve a name to a type, even if name is not fully qualified. For incompletely qualified names (like ”Type”, if Type belongs to namespace X)  VCCodeModel.CodeTypeFromFullName and VCCodeModel.CodeElementFromFullName prefer names defined at global scope and only resolve to a name defined in a namespace if a global one is not found.

We’ve added a helper API called VCCodeModel.CodeElementFromFullName2  in Visual Studio 2010 to help clients detect this ambiguity. See below.

        No code elements from imported assemblies in C++/CLI: Code elements from imported assemblies are not added to the Browsing database. As a consequence, the types coming from these assemblies would not be resolved.  For example, if you have a function with a parameter type specified as Exception in source code, VCCodeModel won’t be able to resolve it to System::Exception, since currently such class is not present in database.

 

        We’ve removed Managed C++ support from VCCodeModel in Dev10: Support for C++/CLI is still there.

 

New APIs in Visual Studio 2010

We have introduced the following APIs to VCCodeModel in Visual Studio 2010. All APIs are additions to the VCCodeModel interface:

        CodeElementFromFullName2: Is identical to CodeElementFromFullName, except that it will disregard namespaces during lookup. Because there is no symbol resolution in Visual Studio 2010 version of VCCodeModel it is sometimes beneficial to know whether a particular symbol is ambiguous. The primary source of ambiguity in VCCodeModel is using namespace directives. CodeElementFromFullName2 API looks up the name disregarding namespace. For the following source code:

class X {};

namespace NS1 {

       class X {};

       namespace NS2 {

              class X {};

       }

}

 

Calls to VCCodeModel.CodeElementFromFullName2(“X”) will yield {X; NS1::X; NS1::NS2::X}.

 

        CodeTypeFromFullName2: Is identical to CodeTypeFromFullName except that it will attempt typedef resolution. That is, for the following code:

class X {};

typedef X TD;

 

call to VCCodeModel.CodeTypeFromFullName2(“TD”) will yield class X.

        IsSynchronized: returns true or false depending on whether VCCodeModel is in sync with solution’s source code. Useful for avoiding blocking the calling thread for an undefined period of time while Browsing database is being populated.

        SynchronizeFiles: If VCCodeModel is not in sync with source code, it is not safe to query for a VCFileCodeModel for a project file (caller will get null reference if project files are not yet registered in Browsing database). Call to SynchronizeFiles makes sure that FileCodeModel property on a project file is guaranteed to be not null.

        SynchronizeCancellable: if caller invokes this API while Browsing database is being populated, and there’s significant delay till population completes (for example: initial population of solution Browsing database), the user will see a dialog box with a progress bar.  The dialog box also allows users to cancel wait and API will exit unblocking thread.

 

We’re looking forward to hearing your feedback.  Any suggestions, comments and feedback about what can we do to make VCCodeModel better are welcome.

Vytas/Visual C++

 

  • What happens for code like this?

    #ifdef X

    typedef float Number;

    class A {/*some definition*/};

    #else

    typedef double Number;

    class A {/*some other definition*/};

    #endif

  • Hi Adam,

    Since philosophy of Tag Parsing is to reflect all valid declarations in source code, this code fragment will generate two classes A and two typedefs Number. It does not make any difference whether macro X is defined or not.

    Of course, each class will contain its own constructs (member functions, variables), and each typedef will contain its type string (in this case: float and double correspondingly).

    Thanks,

    Vytas

  • Vytas,

    Thanks for the article. I have found I delete the NCB files ofton. I look forward in the release of VC 2010! keep up the good work.

    Monte--

Page 1 of 1 (3 items)