Skip to content
This repository has been archived by the owner on May 1, 2019. It is now read-only.

Fix parsing of non-primitive Expressions #725

Merged
merged 4 commits into from
Oct 27, 2017
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
* Scan for CSS custom variable uses and assignments.
* Fix value of reflectToAttribute for polymer properties
* Remove scriptElement from PolymerElement
* Fix handling of types of properties defined in a behavior

## [2.2.2] - 2017-07-20

Expand Down
48 changes: 30 additions & 18 deletions src/javascript/esutil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import * as estree from 'estree';
import {ScannedMethod} from '../index';
import {ImmutableSet} from '../model/immutable';
import {Privacy} from '../model/model';
import {ScannedEvent, Severity, SourceRange, Warning, WarningCarryingException} from '../model/model';
import {ScannedEvent, Severity, SourceRange, Warning} from '../model/model';
import {ParsedDocument} from '../parser/document';
import * as docs from '../polymer/docs';
import {annotateEvent} from '../polymer/docs';
Expand Down Expand Up @@ -88,6 +88,13 @@ export function objectKeyToString(key: estree.Node): string|undefined {
export const CLOSURE_CONSTRUCTOR_MAP = new Map(
[['Boolean', 'boolean'], ['Number', 'number'], ['String', 'string']]);

const VALID_EXPRESSION_TYPES = new Map([
['FunctionExpression', 'Function'],
['ObjectExpression', 'Object'],
['ArrayExpression', 'Array'],
['TemplateLiteral', 'string']
]);

/**
* AST expression -> Closure type.
*
Expand All @@ -98,22 +105,23 @@ export const CLOSURE_CONSTRUCTOR_MAP = new Map(
*/
export function closureType(
node: estree.Node, sourceRange: SourceRange, document: ParsedDocument):
string {
if (node.type.match(/Expression$/)) {
return node.type.substr(0, node.type.length - 10);
string|Warning {
const type = VALID_EXPRESSION_TYPES.get(node.type);
if (type) {
return type;
} else if (node.type === 'Literal') {
return typeof node.value;
} else if (node.type === 'Identifier') {
return CLOSURE_CONSTRUCTOR_MAP.get(node.name) || node.name;
} else {
throw new WarningCarryingException(new Warning({
code: 'no-closure-type',
message: `Unable to determine closure type for expression of type ` +
`${node.type}`,
severity: Severity.WARNING, sourceRange,
parsedDocument: document,
}));
}
return new Warning({
code: 'no-closure-type',
message: `Unable to determine closure type for expression of type ` +
`${node.type}`,
severity: Severity.WARNING,
sourceRange,
parsedDocument: document,
});
}

export function getAttachedComment(node: estree.Node): string|undefined {
Expand Down Expand Up @@ -150,10 +158,10 @@ function getLeadingComments(node: estree.Node): string[]|undefined {
}
const comments = [];
for (const comment of node.leadingComments || []) {
// Espree says any comment that immediately precedes a node is "leading",
// but we want to be stricter and require them to be touching. If we don't
// have locations for some reason, err on the side of including the
// comment.
// Espree says any comment that immediately precedes a node is
// "leading", but we want to be stricter and require them to be
// touching. If we don't have locations for some reason, err on the
// side of including the comment.
if (!node.loc || !comment.loc ||
node.loc.start.line - comment.loc.end.line < 2) {
comments.push(comment.value);
Expand Down Expand Up @@ -205,6 +213,10 @@ export function toScannedMethod(
if (typeTag) {
type = doctrine.type.stringify(typeTag.type!) || type;
}
if (type instanceof Warning) {
warnings.push(type);
type = 'Function';
}
const name = maybeName || '';
const scannedMethod: ScannedMethod = {
name,
Expand Down Expand Up @@ -284,8 +296,8 @@ export function getOrInferPrivacy(
}

/**
* Properties on element prototypes that are part of the custom elment lifecycle
* or Polymer configuration syntax.
* Properties on element prototypes that are part of the custom elment
* lifecycle or Polymer configuration syntax.
*
* TODO(rictic): only treat the Polymer ones as private when dealing with
* Polymer.
Expand Down
4 changes: 4 additions & 0 deletions src/polymer/js-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ export function toScannedPolymerProperty(
if (typeTag) {
type = doctrine.type.stringify(typeTag.type!) || type;
}
if (type instanceof Warning) {
warnings.push(type);
type = 'Object';
}
const name = maybeName || '';
const result: ScannedPolymerProperty = {
name,
Expand Down
27 changes: 23 additions & 4 deletions src/test/polymer/behavior-scanner_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ suite('BehaviorScanner', () => {
let behaviors: Map<string, ScannedBehavior>;
let behaviorsList: ScannedBehavior[];

suiteSetup(async() => {
suiteSetup(async () => {
const parser = new JavaScriptParser();
const file = fs.readFileSync(
path.resolve(__dirname, '../static/js-behaviors.js'), 'utf8');
Expand Down Expand Up @@ -74,8 +74,8 @@ suite('BehaviorScanner', () => {
test('Supports behaviors On.Property.Paths', () => {
assert(behaviors.has('Really.Really.Deep.Behavior'));
assert.equal(
behaviors.get('Really.Really.Deep.Behavior')!.properties
.get('deep')!.name,
behaviors.get('Really.Really.Deep.Behavior')!.properties.get('deep')!
.name,
'deep');
});

Expand Down Expand Up @@ -109,6 +109,25 @@ suite('BehaviorScanner', () => {
throw new Error('Could not find Polymer.SimpleNamespacedBehavior');
}
assert.deepEqual([...behavior.methods.keys()], ['method']);
assert.deepEqual([...behavior.properties.keys()], ['simple']);
assert.deepEqual(
[...behavior.properties.keys()],
['simple', 'object', 'array', 'attached', 'templateLiteral']);
});

test('Correctly transforms property types', function() {
const behavior = behaviors.get('Polymer.SimpleNamespacedBehavior');
if (!behavior) {
throw new Error('Could not find Polymer.SimpleNamespacedBehavior');
}
assert.deepEqual(
[...behavior.properties.values()].map(
(p) => ({name: p.name, type: p.type})),
[
{name: 'simple', type: 'boolean'},
{name: 'object', type: 'Object'},
{name: 'array', type: 'Array'},
{name: 'attached', type: 'Object'},
{name: 'templateLiteral', type: 'string'}
]);
});
});
6 changes: 5 additions & 1 deletion src/test/static/js-behaviors.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@ var SimpleBehavior = {
* */
var SimpleNamespacedBehavior = {
simple: true,
method: function (paramA, paramB) {
method: function(paramA, paramB) {

},
object: {},
array: [],
attached: true ? null : function() {},
templateLiteral: `foo`
};


Expand Down