On Wednesday, 30 March 2016 at 01:24:42 UTC, Jonathan M Davis wrote:
any attempt to warn about "unused identifiers" would have to be done very carefully.

Well, obviously that was a vague, ignorant first attempt. I do realize some care would be needed. Though I don't think it's all that difficult to define unused identifiers, finding them is a lot more complicated than just listing the symbols a module has, and subtracting the symbols it ends up exporting. I thought search() would be called on internal symbols, not just exports.

Aside from the normal issues with RAII and the like

So obviously any object with a constructor or a destructor would have to be excluded. Something with pure versions of those should probably be included though.

it's _very_ common in D code to declare stuff and then not actually use it when dealing with template constraints and the traits tested in them,

I was sort of hoping to get in there after template instantiation had been calculated and only ones with true constraints were left.

templates and string mixins could run into trouble depending on the exact arguments used with them and how complicated some of their static if-else logic is.

Again, it's important not to check for unused identifiers until you've given the programmer a chance to get rid of 'em themselves. While it would be possible to check "if this constraint had been true, THEN this code here with an unused identifier would be used" I don't see any real utility to that. I assume you're talking about like...

void main() {
  int i = 42;
  static if(false) {
    int j = 23;
  }
  import std.stdio: writeln;
  writeln(i);
}

I definitely would not include it as a goal to warn that "j" would be an unused identifier, if you ever set the condition to true. I'd be happy with a warning that only appeared when I did set the condition to true, and angry with one that wouldn't go away even if I used a CTFE to comment out the whole block of code "#if 0" style.

It might be safe not to report any unused identifier that appeared to be a type, not a variable or a module.

Anyway, if I could figure it out, this is what a "careful" algorithm would do. First, it would only check after the module has been fully instantiated, and all the compile time code has been executed. Of all the symbols a module has, it removes the ones that are touched by other modules (not just imported, but actually used, by this algorithm recursively). Then it removes any things that can have side effects even if not used, like structs or classes with non-pure constructors/destructors.

Then, generally the only thing that crops up in heavily modified code is unused imports, so there would be an option to report all unused identifiers and if not, it would eliminate all remaining symbols that weren't declared in a different module than this one.

Finally, it would have a set of unused identifiers. If the aforementioned option were false, it'd build a set of statements where those identifiers were imported, and highlight them giving a warning about unused import. And if true, it'd report each unused identifier, possibly grouped by the modules they came from.

Which is to say, that's what I would do, if I knew when to instrument it post-CTFE but pre-runtime, if I knew how to check if a symbol were an instantiation of an object with a constructor/destructor, if I knew how to look up those functions, if I knew how to check if a function is pure, if I knew how to get the module a symbol was declared in, if I knew how to get the statement a symbol was imported from, if I knew how to untangle complicated templated types into the identifiers they're constituted of, and if I knew how to add options to invoking dmd.

But for now, I'll have to use the strategy of repeatedly deleting import statements, and recompiling to see if it breaks my code.
_______________________________________________
dmd-internals mailing list
[email protected]
http://lists.puremagic.com/mailman/listinfo/dmd-internals

Reply via email to