Cannot get internal members of a Type from within INamedTypeSymbol - roslyn

I am getting a referenced type from a Roslyn compilation by using Compilation.GetTypeByMetadataName(...) method. Then I use INamedTypeSymbol.GetMembers() method to get all its member symbols. I am getting all public and protected symbols but neither private nor internal. I do not care much about private member symbols, but internals are a must for me. Is there any way to get them from a Roslyn compilation?

It seems like I resolved the problem but not without a hack.
one has to set a hidden property MetadataImportOptions of the compilation to MetadataImportOptions.All (which is 2). Both the CSharpCompilationOptions.MetadataImportOptions member and MetadataImportOptions enumeration are internal to Microsort.CodeAnalysis assembly. I had to use reflection to be able to set it. In particular I
did something like:
MethodInfo withMetadataImportOptionsMethodInfo =
typeof(CSharpCompilationOptions).GetMethod("WithMetadataImportOptions, BindingFlags.NonPublic | BindingFlags.Instance);
MyCompilationOptions =
(CSharpCompilationOptions) withMetadataImportOptionsMethodInfo.Invoke(MyCompilationOptions, (byte) 2);
(byte) 2 is the MetadataImportOptions.All option.

Related

With roslyn, how can I get the symbol for a specific method on a type defined in a metadata reference?

My solution builds ok in roslyn and so all types should be resolved
I'm able to get the type defined in a metadata assembly like so:
string typeName = "MyCompany.MyLibrary.MyType`1";
var theType = compilation.GetTypeByMetadataName(typeName);
and when I query the member names I see the method on the type, and I want to find all references to that method, but I can't figure out how I'm supposed to get the symbol for the method. When I try
var symbols = compilation.GetSymbolsWithName("MethodName");
it always returns 0.
And I can't see anyway to navigate from my type to the symbols underneath it in the tree.
I can't get the semantic model and find the symbol that way because I don't have a syntax tree for the metadata assembly.
I can find the symbol if I find an implementation in the current solution when overrides this method, but I don't want to have to go through that, I'd like to just go directly to the symbol.
ITypeSymbol has GetMembers which returns all members from type as ISymbol by a specified name (second overload). So you just need to check, that the set of returned members contains at least an one IMethodSymbol (or you may add a more specific check if you want):
string typeName = "MyCompany.MyLibrary.MyType`1";
var theType = compilation.GetTypeByMetadataName(typeName);
if (!(theType is null))
{
foreach (var member in theType.GetMembers("MethodName"))
{
if (member is IMethodSymbol method) //may check that method has a special parameters, for example
{
// you found the first "MethodName" method
}
}
}

Roslyn Analyzer Object initializer

I'd like to have an analyzer detect all calls to setter properties not inside an object initializer of an object implementing a specific interfaces. I'm a bit lost of how to detect that, the docs are a bit thin. I can get an invocation expression, but how to check if it's inside an object initializer?
Any ideas?
To know if any particular code is inside an object initializer, you can simply look up the ancestors for any node that is of type InitializerExpressionSyntax.
var initializer = node.Ancestors().OfType<InitializerExpressionSyntax>.FirstOrDefault();
To know if any particular code is an assignment to a property setter, you'll have to do a bit more work. You'll need to ask the SemanticModel for the symbol being assigned for an AssignmentExpressionSyntax. Your analyzer should have access to the correct SemanticModel and SyntaxTree from its arguments/context.
SemanticModel model = ...;
AssignmentExpressionSyntax assignment = ...; // find the assignment
var symbol = model.GetSymbolInfo(assignment).Symbol as IMethodSymbol;
if (symbol?.MethodKind == MethodKind.PropertySet) { ... }
To know if the object implements a particular interface you will need to find the symbol for the object. You might be able to find it by looking up the containing symbol chain of the property setter symbol. You can also find the symbol for the object by finding the ObjectCreationExpressionSyntax that should be the parent or ancestor of the InitializerExpressionSyntax you already have.
Once you have that creation node you can ask the SemanticModel again. Use the GetTypeInfo method to get the type of the expression (the type/symbol being constructed) and not the symbol for the constructor.
var creation = initializer.Ancestors().OfType<ObjectCreationSyntax>().FirstOrDefault();
var createdType = model.GetTypeInfo(creation).Type as INamedTypeSymbol;
Now, you just need to know if the type implements the interface.
First you will need a symbol for the interface type. One quick way to get that is to look it up using its CLR metadata name.
var interfaceType = model.Compilation.GetTypeByMetadataName("MyNamspace.MyInterfaceType");
This part is usually done once at the initialization of the analyzer, so you don't have to keep looking it up, over and over again.
Now you have everything you need to discover if the type being constructed implements the interface.
if (createdType.AllInterfaces.Contains(interfaceType)) { ... }

Unexpected behaviour with accessors=true on a component

I am trying to use synthesised accessors on a component on Lucee (although this issue seems to be the same on ColdFusion too).
Repro code:
// Person.cfc
component accessors=true {
property firstName;
property lastName;
function init(firstName, lastName){
variables.firstName = arguments.firstName;
variables.lastName = arguments.lastName;
}
}
And the calling code:
// person.cfm
person = new Person("Abigail", "Bowen");
writeDump(person);
Notice how I am not using the synthesised accessors here, I am purely setting the argument values into same-named variables-scoped variables.
However when I run this code, I see this:
Note how the properties have been populated. There's no problem with this, but I'm clearly not understanding how the accessors flag is supposed to work. I thought it was merely intended to synthesise some accessor methods for me (which it has), but that's all.
Also note that if I modify the CFC definition to not set accessors to true, then the dump shows this:
So no synthesised accessors (as expected), but also now the properties aren't even being displayed (with the variables-scoped values or not).
I don't really understand this conflation of "properties" and the accessors setting? Surely the accessors setting should only impact whether those accessor methods get created?
If I was only seeing this on one of the platforms, I'd probably put it down to a vagary of how writeDump() interprets the property definitions. But the behaviour is the same on ColdFusion 11, so it really does seem like there's some difference in behaviour I'm not quite getting.
Can anyone explain this? Are there any docs which explain it? If not... um... why not?
My underlying concern here is that the property values are not being stored "properly" and might cause me problems once I implement more of the code.
UPDATE:
At least on ColdFusion, it seems to be just a change in writeDump()'s behaviour, because if there are getters for the properties (whether or not the accessors flag is set) then the property values start showing up in the dump. This is not the case on Lucee though, so there's still a question mark there.
For the sake of full disclosure, this question is a summary of a question I also asked on my blog ("CFML: trying to understand accessors"). The duplication is intentional as my blog gets a different audience from that of this site.
Without accessors=true, the property declarations are just metadata.
With accessors=true, the property declarations trigger the generation of getters / setters and thus a property is both a variables scope item and a pair of methods.
In your constructor, you assign to the variables scope items -- which would be the same as using the generated setters -- and when CFML dumps the component, it sees the property metadata and the generated getters and so it displays the values those properties have (since it can easily and safely call the generated getters).
This came up with ACF9. Until then the definition in the property docs was right: cfproperty declarations are just metadata. (see dump(getMetaData()).
In ACF9 this was not fully correct anymore for 3 reasons:
With accessors=true a getter and setter is generated for each property and those accessors read from and write to the variables scope. Cfproperty is not just metadata anymore, but has a direct effect on the behaviour of the instance. I like to think about it as the CF version of real OO properties (introduced by accident).
The cfdump implementation changes its behaviour based on the property declarations. If property name; is defined and method getName() exists (generated or implemented) it is added to the property section of the dump.
The property attributes control the ORM.
Since I got to know those features, I design all my (public) CFCs to look right when dumped, eg. I only use the property declaration (+ getters) when I want to have it visible. In addition, you can implement methods that are only called by the dumps and cost nothing on instantiation:
struct function getDebug(){
var x = doSomethingExpensive();
return { "Foo":f, "Bar":b, "Baz":x };
}
//or for a user iterator
string function getName(){
return qUsers.name[index];
}
Some caveats I know:
ACF always calls the getters from the dumps, while in Railo/Lucee the value from the variables scope is shown. Thus, the above examples (getDebug()and getName()) don't work on Railo/Lucee.
If the getter is not public or results in an error, the dump shows an empty string for the property (not sure here, maybe the property is missing instead).
Property declarations in extended CFCs are ignored. This gave me some headache in ORM entities that use inheritance, because you are not allowed to declare a property twice. Thus, you have no possibility to show a property that is defined in a base CFCs.
Railo/Lucee seems to ignore the property types. All accessor accept and return only strings (see getMetaData()).
Minor: In ACF when you activate the accessors, but deactivate getter and setter for a property: property name="user" getter="false" setter="false"; it is still visible in the dump - it should be hidden.

Use of singletons in compiler

I am writing a compiler for a C++-like language. I have to deal with symbol table which is represented in my code with class that has only static data and methods.
It's like:
class GlobalTable
{
/* static members */
static map<int, Symbol*> symbol_by_id;
static map<Symbol*, int> id_by_symbol;
/* some static methods */
};
Also is have a class which represents config:
class GlobalConfig
{
static const int int_size;
/* and so on ... */
};
I need to access these from a lot of places. Passing it around will result in swelling of code.
Is it convenient to use my class like that, or there is a better way to organize everything?
In a compiler, usually you store a symbol type in a symbol table, for some definition of symbol. Generally, I have a symbol table per scope, so the global scope in each module or compilable unit has its own symbol table.
You'll also have AST nodes that have symbols and symbol subtpes as their members.
Most internal compiler apis deal with symbol and ast subtypes, and those are stored in a indexed, fast access data structure. All of this has to be available to most compiler functions, so you either use a global compiler or context variable, or pass one around as a paramter.
Strictly, there is no need to use singletons. I can only talk in examples, so the way I do it is with a Compiler class that has instance members like currentScope and globalScope as well as the root AST or currentCompilableUnit, so the parser main instantiates a Compiler, and everything is reentrant. It is a bit more convenient, though, if your compiler instance is a global variable, so your functions can omit the compiler parameter, but besides that, not really any need for any others.
In short, it is typical for most APIs to include a "scope" or "compiler" struct or class in many of its signatures. Though if you use OOP to model the compiler, any methods of the class have implicit "this" access.

How to use the C++ compiler/linker/trick to detect a value that is assigned but never subsequently referred to?

I want to make a singleton class, call it global_references containing a bunch of cached global values. The singleton class code will be automatically generated from a set of tables. The general scheme is something like this:
global_references.h
struct global_references
{
some_type value1;
some_type value2;
some_type value3;
...
static const global_references& instance();
};
Assume that global_references::instance() initializes the various valueN variables and returns the singleton instance. Then various clients will used the cached values in their own code:
some_translation_unit.cpp
...
x = global_references::instance().value1;
My concern is that I will add items to the table that is used to produce the auto-generated global_references class but over time, some of the symbols they generate will become obsolete and stop being used in the program. I would like to know when this happens, preferably by having the compiler or linker complain about an unused symbol, or a value that is assigned but never used. This will remind me to go delete the corresponding item from the table. I am also open to any other reasonably simple solution.
I am using MinGW [g++ 4.6.2] on Windows.
Options Already Considered
I thought about making member functions to return the values, but the The -Wunused-function compiler option only works on static functions whereas I need global_references to be a modular component which I can include in any translation unit.
Use -Wunused-but-set-variable, or for broader effect, -Wunused, or -Wall.
I am not sure that I have understood your purpose. So, after you add items to the table each time, you will recompile the project code to find what is not used by compile-warning. It Sounds like an inefficient way.