In Roslyn API what the Speculative Semantic Model is? - roslyn

What does the extension function SemanticModel.TryGetSpeculativeSemanticModel return? What is it good for?
I could not find any meaningful documentation on the subject.

The documentation for TryGetSpeculativeSemanticModel says:
Get a SemanticModel object that is associated with X that did not appear in this source code. This can be used to get detailed semantic information about sub-parts of X that did not appear in source code.
The analyzer code for the StyleCop SX1101 diagnostic offers a great example of this API's usage. SX1101 tells you that it's safe to remove a this. qualifier from your code.
Lets step through a slightly simplified version of the analyzer code:
var memberAccessExpression = (MemberAccessExpressionSyntax)ctx.Node.Parent;
var originalSymbolInfo = context.SemanticModel.GetSymbolInfo(
memberAccessExpression,
context.CancellationToken);
var statement = context.Node.FirstAncestorOrSelf<StatementSyntax>();
var annotation = new SyntaxAnnotation();
var speculationRoot = statement.ReplaceNode(
memberAccessExpression,
memberAccessExpression.Name.WithAdditionalAnnotations(annotation));
context.Node is a ThisExpressionSyntax. speculationRoot is an expression where we replaced this.SomeMember with SomeMember. The annotation is used for quick lookup later.
Now that we have generated a modified version of the code, we want to check if it would a) still compile, and b) still refer to the same thing. Since the sources in Roslyn are immutable, we'd need to compile the entire project again (which would be doable, but more expensive) to get a new SemanticModel for the changed code.
Here is where TryGetSpeculativeSemanticModel steps in:
if(!context.SemanticModel.TryGetSpeculativeSemanticModel(
statement.SpanStart,
speculationRoot,
out var speculativeModel)) return;
var mappedNode = speculationRoot.GetAnnotatedNodes(annotation).Single();
var newSymbolInfo = speculativeModel.GetSymbolInfo(mappedNode, context.CancellationToken);
So now if we manage to get a speculative semantic model, it means SomeMember was valid at that same position in code as this.SomeMember. We've used the annotation to quickly look up the SomeMember syntax node and then get it's semantic info from the speculativeModel.
All that's left to do now is to check whether the modified statement means the same thing as the original one.
if (!Equals(originalSymbolInfo.Symbol, newSymbolInfo.Symbol)) return;
context.ReportDiagnostic(Diagnostic.Create(Descriptor, context.Node.GetLocation()));

Related

VSIX how to get current snapshot document name?

I have been trying to to create an extension that highlights specific line numbers for me in Visual Studio in the margins.
I manged to get my marking in the margins using predefined line number but for it to work properly I need to know what the current document FullName is (Path and filename)
After much googling I figured out how to do it with the sample code (which is not ideal)
DTE2 dte = (DTE2)System.Runtime.InteropServices.Marshal.GetActiveObject("VisualStudio.DTE.15.0");
var activeDocument = dte.ActiveDocument;
var docName = activeDocument.Name;
var docFullName = activeDocument.FullName;
Now I know the problems here
is that is for specific version bases on the text
there is no way to select which instance (when running more than one VS)
It seems to be very slow
I have a feeling I should be doing this with MEF Attributes but the MS docs examples are so simple that they do not work for me. I scanned a few SO questions too and I just cannot get them to work. They mostly talk about Services.. which I do not have and have no idea how to get.
The rest of my code uses SnapshotSpans as in the example Extension of Todo_Classification examples which is great if you do NOT need to know the file name.
I have never done any extensions development. Please can somebody help me do this correctly.
You can use following code to get a file from a snapshot without any dependencies.
public string GetDocumentPath(Microsoft.VisualStudio.Text.ITextSnapshot ts)
{
Microsoft.VisualStudio.Text.ITextDocument textDoc;
bool rc = ts.TextBuffer.Properties.TryGetProperty(
typeof(Microsoft.VisualStudio.Text.ITextDocument), out textDoc);
if (rc && textDoc != null)
return textDoc.FilePath;
return null;
}
If you don't mind adding Microsoft.CodeAnalysis.EditorFeatures.Text to your project it will provide you with an extension method Document GetOpenDocumentInCurrentContextWithChanges() on the Microsoft.VisualStudio.Text.Snapshot class. (Plus many other Rosyln based helpers)
using Microsoft.CodeAnalysis.Text;
Document doc = span.Snapshot.GetOpenDocumentInCurrentContextWithChanges();

clang: custom attributes not visible in AST

i implemented a custom attribute in clang as described in the official manual:
http://clang.llvm.org/docs/InternalsManual.html#how-to-add-an-attribute
So i added the following Code to Attr.td:
def MyAttr: InheritableAttr {
let Spellings = [GNU<"my_attr">, CXX11<"me", "my_attr">, GCC<"my_attr">, Declspec<"my_attr">];
let Subjects = SubjectList<[Var, Function, CXXRecord]>;
let Documentation = [MyAttrDocs];
}
and the documentation to AttrDocs.td. After rebuilding clang, it obviously knows the attribute because i don't get an unknown attribute warning when using it. I can even access the new attribute class with libtooling, but the attribute doesn't show up in the AST, even if i add the line let ASTNode = 1 to the attribute definition.
Is there something else i need to consider or what could be the problem?
Unfortunately this was my fault, the missing step is described in the manual in section "Boilerplate": i just had to implement the semantic processing of the attribute in SemaDeclAttr.cpp by adding a new case:
case AttributeList::AT_MyAttr:
handleSimpleAttribute<MyAttrAttr>(S, D, Attr);
break;
So it works fine now.

Roslyn: SyntaxWalker through 2 different documents

I have a solution with two different projects. I use SyntaxWalker to process some stuff in ProjectA.Class1. However, ProjectA.Class1 has reference ProjectB.Class2.
Is there way to allow the syntax walker to traverse also through external classes? I can't even do it when both classes are in the same project but in different files (documents). It always goes through the same document. If both classes are in the same file then it works. If I extract them to separate ones, it doesn't...
I am working on a test coverage tool. A user click on the method in VS and then:
I use rewriter to add static variables to each branch.
I run the code so the static variables are set if branch was covered.
I wonder how should I configure a syntax walker\rewriter to recognize other classes in the same solution.
You're going to need access to the Symbol API. A simple rule of thumb I try to go by:
The Syntax API is for individual files
The Symbol API allows you to work with information that spans across files.
You've mentioned in the comments that you'd like to traverse methods and figure out some information about each method declaration. Here's some (naive) code that should get you started with the symbol API.
I've assumed you've got access to a Project that you're analyzing.
Project myProject;
public void ProcessMethod(MethodDeclarationSyntax method)
{
//Get the semantic model
var filePath = method.SyntaxTree.FilePath;
var containingDocument = myProject.Documents.Where(n => n.FilePath == filePath).Single();
var model = containingDocument.GetSemanticModelAsync().Result;
//...
//Do your processing on the current method here...
//...
//Process the invoked methods.
var invocations = method.DescendantNodes().OfType<InvocationExpressionSyntax>();
foreach(var invocation in invocations)
{
var invokedSymbol = model.GetSymbolInfo(invocation).Symbol; //Might be null
var invokedSymbolSyntax = (MethodDeclarationSyntax)invokedSymbol.DeclaringSyntaxReferences.First().GetSyntax(); //Partial methods might be declared in multiple places
ProcessMethod(invokedSymbolSyntax);
}
}
Note:
This approach doesn't handle constructors, destructors, properties, expression-bodied members and any other members I've forgotten. But it should be enough to get you started and introduce you to the symbol API.
Recursion will bite you.
You won't process implementations of interfaces. You'll have to look into the SymbolFinder for that.

Sitecore Glass Mapper always null

I am using Sitecore Glass Mapper for a new project I'm setting up.
We are using Sitecore 7.2, latest version of Team Development for Sitecore (TDS) code generation and the latest version of glass.
The code I am trying to execute:
var b = new SitecoreContext();
var c = b.GetCurrentItem<T01_Homepage>();
b is not null. c is null.
var d = b.GetItem<T01_Homepage>("path")
d is null.
I added my assembly in GlassMapperScCustom:
public static IConfigurationLoader[] GlassLoaders(){
var attributes = new AttributeConfigurationLoader(new[] { "Company.Framework.Websites.Corporate", "Company.Framework.Core", "Company.Framework.Common" });
return new IConfigurationLoader[] { attributes };
}
When I look into b.GlassContext.TypeConfigurations all my models are there.
I figured it could be a language issue because the site is in dutch and maybe the wrong language would be resolved incorrectly. This was also not the case.
I disabled WebActivator and added the GlassMapperSc.Start() in my Global.asax Application_Start method.
We are also using Autofac as DI framework. But without it, it still isn't working as you can see above. Also when I create my own custom models without TDS code generation the result of GetCurrentItem<T> is null.
Does anyone have an idea how I can fix this?
Did you check your Sites.config and the default language for this website? There could be a difference between the language which is defined in your Sitecore languages folder and your configuration.
I had a similar problem with one of my projects where I changed the Sitecore.Context.Language to "nl" instead of "nl-NL". The glass mapper will return null, but Sitecore.Context.Database.GetItem will return an object in this case.
Most of the times it is a language issue. The mapper returns a null object when you do not have versions in the current or given language.
What can be confusing is that Sitecore.Context.Database.GetItem returns an object, even if it does not have a version in the current language. Be sure to check that item.Versions has any.
Some things you may try (this didn't fit in the comments field)
1) Confirm that the related fields in the Sitecore Item object contain values (so Sitecore.Context.Item for your "c" var and Sitecore.Context.Database.GetItem("path") for your "d" var)
2) Try to encapsulate the GetItem/GetCurrentItem call in a VersionCountDisabler, like this:
T01_Homepage model = null;
using (new VersionCountDisabler())
{
var context = new SitecoreContext();
model = context.GetItem<T01_Homepage>("path");
}
// Do you have data in model now?
3) Try to encapsulate the same call with a SecurityDisabler. Just to confirm it's not a security issue.
4) If you still don't know what it is, please update your question with some (simplified) code for your model.

Cannot find qryname14411 key in structure

I have an error that seems to be associated with <cfscript> db operation
// traffic tracking
myQry = new Query();
myQry.setSQL("INSERT INTO dbo.Traffic (Circuit, Fuseaction, IP_hash) VALUES (:circuit, :fuseaction, :ip_hash)");
myQry.addParam(name="circuit", value="#listfirst(rc.fuseaction, '.')#", cfsqltype="CF_SQL_VARCHAR");
myQry.addParam(name="fuseaction", value="#listlast(rc.fuseaction, '.')#", cfsqltype="CF_SQL_VARCHAR");
myQry.addParam(name="ip_hash", value="#cgi.remote_addr#", cfsqltype="CF_SQL_VARCHAR");
myQry.execute();
The really strange thing is, it looks like the operation completed. What kind of a error is this?
Short answer: It's probably a scoping issue. Try:
var myQry = new Query();
Long-winded waffley answer:
I'd call it an Adobe-developers-being-useless kind of error.
If you look at line 460 of that file, you'll see the error is due to a failure of StructFind to find the query name in the variables scope, and the reason it's appearing in debug input is because there's a try/catch with type any surrounding it. The same functionality could be achieved without causing/catching an error by replacing the try/catch with <cfif StructKeyExists(variables,tagAttributes['name']) > which is basic CFML knowledge, and certainly something a developer of the CF product should know!
The same code still exists in the CF10 version of base.cfc, so you may or not feel like submitting it as a bug in Adobe's CF bugbase - though it's unlikely they'll fix it for CF9 (and uncertain whether they'll feel CF10 is worth the effort either).
However, that would only be side-stepping the issue of the variable not existing, not addressing the real issue of why it doesn't actually exist. Given that the debug info shows the query is successfully executing, and the query code is basically right above that line (starts at line 442), it shouldn't be a repeated/common error, but it may be due to your myQry variable not being scoped, and thus it could be colliding with another variable also called myQry (or even the same var from a separate call to the function) which is happening between the execution of the new Query() and .execute() lines, and thus causing the original query to not be there when the StructFind looks for it.
The solution is to put the keyword var before the first use of myQry which will place it in the local scope for that function - something that should be done for all variables that are only for use within an instance of a function, (otherwise they are placed in the variables scope of the component/request that the function exists within).