✅ How to design an error interface
In my current project I have an interface called
ISymbol
. This interface has tons of derived interfaces (INamedSymbol
, ITypeSymbol
, IFunctionSymbol
, IVariableSymbol
, among others), which all can have multiple implementations. However, I also have an interface called IErrorSymbol
which just represents an error which has been produced somewhere. There are several cases in which I have to cast between these interfaces, and many times I also have to perform is
checks. The IErrorSymbol
is meant to act as a more or less general-purpose interface which can describe any error, but which also can be used in place of any of the other interfaces.
Currently I have a general ErrorSymbol : IErrorSymbol
implementation, as well as a couple specialized ones like NamedErrorSymbol : INamedSymbol, IErrorSymbol
and ErrorTypeSymbol : ITypeSymbol, IErrorSymbol
, although having one of these for every kind of error feels very annoying. What I would love to have is just a single (or at least very few) implementations which can serve the roll of any of the interfaces, however which don't necessarily implicitly implement those interfaces. Does that even make sense? Like, in my ideal world, new ErrorSymbol() is ITypeSymbol
would be false, but (ITypeSymbol)new ErrorSymbol()
would be acceptable. Is there any decent way to describe/implement this?19 Replies
Unfortunately I can't have
public static explicit operator ITypeSymbol(ErrorSymbol e)
, because you can't define implicit or explicit operators to/from an interface.just have a "referenced symbol" property
making a subclass for each one is nonsense
wdym?
hold on what do those implementations do
they are both an error an a named symbol?
what's that supposed to mean?
An
NamedErrorSymbol
is an error for when an INamedSymbol
was expectedWhat roslyn does is only have error types
You should have a
IErrorSymbol
that wraps the deduced symbol, or just use a normal named symbol while recording the errors, I guessI'm not certain what the scenario is for something like an error IFunctionSymbol
Hmm, right
Idk I suppose I can synthesize the rest of the symbols, i.e. just use the pre-existing ones but give them made-up data?
What is the scenario you're thinking of here?
foo(x: Int32, x: String): Something;
What do you do if you ask for the symbol of x: String
?it would a normal parameter with the name x
You get a parameter symbol
There's no error there
the same name error would be a diagnostic
Even if
String
is not defined, there's no error with x
When you ask for the type of x
, you get an IErrorTypeSymbol
You can't be more specific because you don't know what type of symbol it was intended to behmm, fair I suppose, maybe I don't need any more error symbols
That's really what I'm meaning here. Like, let's take a function call
M(1, 2)
, where M
is undefined, and someone asks for the method symbol. What could you return other than null
?
What we do in roslyn is you can query for the all the candidate methods, but the resolved symbol will be nullright
Also with the case above, I was mostly thinking about
x
being redefinedWell, the symbol itself can still be created, and isn't an error itself. It's just that in the context it's defined in, it's an error